formatting-culture-aware-data
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCulture-Aware Data Formatting in WPF
WPF中的文化感知数据格式化
Format dates, numbers, and currency based on user's culture preferences.
根据用户的文化偏好格式化日期、数字和货币。
1. Culture Formatting Overview
1. 文化格式化概述
| Data Type | en-US | ko-KR | de-DE |
|---|---|---|---|
| Date (d) | 1/21/2026 | 2026-01-21 | 21.01.2026 |
| Currency (C) | $1,234.56 | ₩1,235 | 1.234,56 € |
| Number (N2) | 1,234.56 | 1,234.56 | 1.234,56 |
| Percent (P) | 75.00% | 75.00% | 75,00 % |
| 数据类型 | en-US | ko-KR | de-DE |
|---|---|---|---|
| 日期 (d) | 1/21/2026 | 2026-01-21 | 21.01.2026 |
| 货币 (C) | $1,234.56 | ₩1,235 | 1.234,56 € |
| 数字 (N2) | 1,234.56 | 1,234.56 | 1.234,56 |
| 百分比 (P) | 75.00% | 75.00% | 75,00 % |
2. XAML Formatting
2. XAML格式化
2.1 Date Formatting
2.1 日期格式化
xml
<!-- Short date (culture-aware) -->
<TextBlock Text="{Binding Date, StringFormat={}{0:d}}"/>
<!-- Long date -->
<TextBlock Text="{Binding Date, StringFormat={}{0:D}}"/>
<!-- Custom format (not culture-aware) -->
<TextBlock Text="{Binding Date, StringFormat={}{0:yyyy-MM-dd}}"/>
<!-- Date and time -->
<TextBlock Text="{Binding Date, StringFormat={}{0:g}}"/>xml
<!-- 短日期(文化感知) -->
<TextBlock Text="{Binding Date, StringFormat={}{0:d}}"/>
<!-- 长日期 -->
<TextBlock Text="{Binding Date, StringFormat={}{0:D}}"/>
<!-- 自定义格式(非文化感知) -->
<TextBlock Text="{Binding Date, StringFormat={}{0:yyyy-MM-dd}}"/>
<!-- 日期和时间 -->
<TextBlock Text="{Binding Date, StringFormat={}{0:g}}"/>2.2 Number Formatting
2.2 数字格式化
xml
<!-- Number with 2 decimal places -->
<TextBlock Text="{Binding Amount, StringFormat={}{0:N2}}"/>
<!-- Number with no decimals -->
<TextBlock Text="{Binding Count, StringFormat={}{0:N0}}"/>
<!-- Fixed decimal places -->
<TextBlock Text="{Binding Value, StringFormat={}{0:F2}}"/>xml
<!-- 保留两位小数的数字 -->
<TextBlock Text="{Binding Amount, StringFormat={}{0:N2}}"/>
<!-- 无小数位的数字 -->
<TextBlock Text="{Binding Count, StringFormat={}{0:N0}}"/>
<!-- 固定小数位 -->
<TextBlock Text="{Binding Value, StringFormat={}{0:F2}}"/>2.3 Currency Formatting
2.3 货币格式化
xml
<!-- Currency (culture-aware symbol and format) -->
<TextBlock Text="{Binding Price, StringFormat={}{0:C}}"/>
<!-- Currency with no decimals -->
<TextBlock Text="{Binding Price, StringFormat={}{0:C0}}"/>xml
<!-- 货币(文化感知的符号和格式) -->
<TextBlock Text="{Binding Price, StringFormat={}{0:C}}"/>
<!-- 无小数位的货币 -->
<TextBlock Text="{Binding Price, StringFormat={}{0:C0}}"/>2.4 Percent Formatting
2.4 百分比格式化
xml
<!-- Percent (multiplies by 100) -->
<TextBlock Text="{Binding Rate, StringFormat={}{0:P}}"/>
<!-- Percent with 1 decimal -->
<TextBlock Text="{Binding Rate, StringFormat={}{0:P1}}"/>xml
<!-- 百分比(乘以100) -->
<TextBlock Text="{Binding Rate, StringFormat={}{0:P}}"/>
<!-- 保留一位小数的百分比 -->
<TextBlock Text="{Binding Rate, StringFormat={}{0:P1}}"/>3. Code Formatting
3. 代码端格式化
3.1 Using Current Culture
3.1 使用当前文化
csharp
// Uses Thread.CurrentThread.CurrentCulture
var dateStr = DateTime.Now.ToString("d");
var currencyStr = price.ToString("C");
var numberStr = amount.ToString("N2");csharp
// 使用Thread.CurrentThread.CurrentCulture
var dateStr = DateTime.Now.ToString("d");
var currencyStr = price.ToString("C");
var numberStr = amount.ToString("N2");3.2 Specific Culture
3.2 指定文化
csharp
var koKr = new CultureInfo("ko-KR");
var deDE = new CultureInfo("de-DE");
// Korean formatting
var dateKr = DateTime.Now.ToString("d", koKr); // 2026-01-21
var currencyKr = price.ToString("C", koKr); // ₩1,234
// German formatting
var dateDE = DateTime.Now.ToString("d", deDE); // 21.01.2026
var currencyDE = price.ToString("C", deDE); // 1.234,56 €csharp
var koKr = new CultureInfo("ko-KR");
var deDE = new CultureInfo("de-DE");
// 韩语格式
var dateKr = DateTime.Now.ToString("d", koKr); // 2026-01-21
var currencyKr = price.ToString("C", koKr); // ₩1,234
// 德语格式
var dateDE = DateTime.Now.ToString("d", deDE); // 21.01.2026
var currencyDE = price.ToString("C", deDE); // 1.234,56 €3.3 Invariant Culture (for data storage)
3.3 固定文化(用于数据存储)
csharp
// Always use InvariantCulture for serialization
var dataStr = value.ToString(CultureInfo.InvariantCulture);
var parsed = double.Parse(dataStr, CultureInfo.InvariantCulture);csharp
// 序列化时始终使用InvariantCulture
var dataStr = value.ToString(CultureInfo.InvariantCulture);
var parsed = double.Parse(dataStr, CultureInfo.InvariantCulture);4. Localized Enum Converter
4. 本地化枚举转换器
csharp
namespace MyApp.Converters;
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
using MyApp.Resources;
public sealed class LocalizedEnumConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Enum enumValue)
{
var key = $"{enumValue.GetType().Name}_{enumValue}";
return Strings.ResourceManager.GetString(key, culture)
?? enumValue.ToString();
}
return value?.ToString() ?? string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}Resource file entries:
xml
<!-- Strings.resx -->
<data name="Status_Active"><value>Active</value></data>
<data name="Status_Inactive"><value>Inactive</value></data>
<!-- Strings.ko-KR.resx -->
<data name="Status_Active"><value>활성</value></data>
<data name="Status_Inactive"><value>비활성</value></data>Usage:
xml
<TextBlock Text="{Binding Status, Converter={local:LocalizedEnumConverter}}"/>csharp
namespace MyApp.Converters;
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
using MyApp.Resources;
public sealed class LocalizedEnumConverter : MarkupExtension, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Enum enumValue)
{
var key = $"{enumValue.GetType().Name}_{enumValue}";
return Strings.ResourceManager.GetString(key, culture)
?? enumValue.ToString();
}
return value?.ToString() ?? string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
public override object ProvideValue(IServiceProvider serviceProvider) => this;
}资源文件条目:
xml
<!-- Strings.resx -->
<data name="Status_Active"><value>Active</value></data>
<data name="Status_Inactive"><value>Inactive</value></data>
<!-- Strings.ko-KR.resx -->
<data name="Status_Active"><value>활성</value></data>
<data name="Status_Inactive"><value>비활성</value></data>使用方式:
xml
<TextBlock Text="{Binding Status, Converter={local:LocalizedEnumConverter}}"/>5. Localized String Resources
5. 本地化字符串资源
5.1 LocalizedStrings Class
5.1 LocalizedStrings类
csharp
namespace MyApp.Resources;
public sealed class LocalizedStrings
{
public Strings Strings { get; } = new();
}csharp
namespace MyApp.Resources;
public sealed class LocalizedStrings
{
public Strings Strings { get; } = new();
}5.2 App.xaml Registration
5.2 App.xaml注册
xml
<Application.Resources>
<local:LocalizedStrings x:Key="Loc"/>
</Application.Resources>xml
<Application.Resources>
<local:LocalizedStrings x:Key="Loc"/>
</Application.Resources>5.3 XAML Usage
5.3 XAML使用
xml
<TextBlock Text="{Binding Source={StaticResource Loc}, Path=Strings.WelcomeMessage}"/>
<Button Content="{Binding Source={StaticResource Loc}, Path=Strings.SaveButton}"/>xml
<TextBlock Text="{Binding Source={StaticResource Loc}, Path=Strings.WelcomeMessage}"/>
<Button Content="{Binding Source={StaticResource Loc}, Path=Strings.SaveButton}"/>6. Localized Images
6. 本地化图片
6.1 File Structure
6.1 文件结构
Resources/
├── Images/
│ ├── flag.png (default/en-US)
│ ├── flag.ko-KR.png (Korean)
│ └── flag.ja-JP.png (Japanese)Resources/
├── Images/
│ ├── flag.png (默认/en-US)
│ ├── flag.ko-KR.png (韩语)
│ └── flag.ja-JP.png (日语)6.2 Helper Class
6.2 辅助类
csharp
public static class LocalizedImageHelper
{
public static string GetLocalizedPath(string basePath)
{
var culture = Thread.CurrentThread.CurrentUICulture.Name;
var dir = Path.GetDirectoryName(basePath) ?? "";
var name = Path.GetFileNameWithoutExtension(basePath);
var ext = Path.GetExtension(basePath);
var localizedPath = Path.Combine(dir, $"{name}.{culture}{ext}");
return File.Exists(localizedPath) ? localizedPath : basePath;
}
}csharp
public static class LocalizedImageHelper
{
public static string GetLocalizedPath(string basePath)
{
var culture = Thread.CurrentThread.CurrentUICulture.Name;
var dir = Path.GetDirectoryName(basePath) ?? "";
var name = Path.GetFileNameWithoutExtension(basePath);
var ext = Path.GetExtension(basePath);
var localizedPath = Path.Combine(dir, $"{name}.{culture}{ext}");
return File.Exists(localizedPath) ? localizedPath : basePath;
}
}7. Format Specifiers Reference
7. 格式说明符参考
| Specifier | Description | Example (en-US) |
|---|---|---|
| d | Short date | 1/21/2026 |
| D | Long date | Tuesday, January 21, 2026 |
| t | Short time | 2:30 PM |
| T | Long time | 2:30:00 PM |
| g | General (short) | 1/21/2026 2:30 PM |
| G | General (long) | 1/21/2026 2:30:00 PM |
| C | Currency | $1,234.56 |
| N | Number | 1,234.56 |
| P | Percent | 75.00% |
| F | Fixed-point | 1234.56 |
| 说明符 | 描述 | 示例(en-US) |
|---|---|---|
| d | 短日期 | 1/21/2026 |
| D | 长日期 | Tuesday, January 21, 2026 |
| t | 短时间 | 2:30 PM |
| T | 长时间 | 2:30:00 PM |
| g | 常规格式(短) | 1/21/2026 2:30 PM |
| G | 常规格式(长) | 1/21/2026 2:30:00 PM |
| C | 货币 | $1,234.56 |
| N | 数字 | 1,234.56 |
| P | 百分比 | 75.00% |
| F | 定点数 | 1234.56 |