formatting-culture-aware-data

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Culture-Aware Data Formatting in WPF

WPF中的文化感知数据格式化

Format dates, numbers, and currency based on user's culture preferences.
根据用户的文化偏好格式化日期、数字和货币。

1. Culture Formatting Overview

1. 文化格式化概述

Data Typeen-USko-KRde-DE
Date (d)1/21/20262026-01-2121.01.2026
Currency (C)$1,234.56₩1,2351.234,56 €
Number (N2)1,234.561,234.561.234,56
Percent (P)75.00%75.00%75,00 %

数据类型en-USko-KRde-DE
日期 (d)1/21/20262026-01-2121.01.2026
货币 (C)$1,234.56₩1,2351.234,56 €
数字 (N2)1,234.561,234.561.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. 格式说明符参考

SpecifierDescriptionExample (en-US)
dShort date1/21/2026
DLong dateTuesday, January 21, 2026
tShort time2:30 PM
TLong time2:30:00 PM
gGeneral (short)1/21/2026 2:30 PM
GGeneral (long)1/21/2026 2:30:00 PM
CCurrency$1,234.56
NNumber1,234.56
PPercent75.00%
FFixed-point1234.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

8. References

8. 参考资料