Files

05 - 国际化(i18n)示例

演示如何使用 Sharp.Wpf 的国际化功能实现多语言支持,包括运行时切换语言、资源文件管理等。

🎯 学习目标

  • 使用 ILocalizationService 实现多语言
  • 在 XAML 中使用 {loc:Localize} 绑定本地化字符串
  • 运行时动态切换语言
  • 管理资源文件(.resx)
  • 格式化带参数的本地化字符串

📦 使用的模块

  • Sharp.Wpf - ILocalizationService, LocalizeExtension

🚀 快速运行

cd examples/05-Localization
dotnet run

📝 核心代码讲解

1. 创建资源文件

Resources/
├── Strings.resx           # 默认语言(中文)
├── Strings.en-US.resx     # 英文
└── Strings.ja-JP.resx     # 日文(可选)

Strings.resx (中文):

<data name="Welcome" xml:space="preserve">
    <value>欢迎使用 Sharp 框架</value>
</data>
<data name="Username" xml:space="preserve">
    <value>用户名</value>
</data>
<data name="Password" xml:space="preserve">
    <value>密码</value>
</data>
<data name="Login" xml:space="preserve">
    <value>登录</value>
</data>
<data name="WelcomeUser" xml:space="preserve">
    <value>欢迎, {0}!</value>
</data>

Strings.en-US.resx (英文):

<data name="Welcome" xml:space="preserve">
    <value>Welcome to Sharp Framework</value>
</data>
<data name="Username" xml:space="preserve">
    <value>Username</value>
</data>
<data name="Password" xml:space="preserve">
    <value>Password</value>
</data>
<data name="Login" xml:space="preserve">
    <value>Login</value>
</data>
<data name="WelcomeUser" xml:space="preserve">
    <value>Welcome, {0}!</value>
</data>

2. App.xaml.cs - 注册国际化服务

using System.Globalization;
using System.Resources;
using Wpf.Localization;

protected override void OnStartup(StartupEventArgs e)
{
    var services = new ServiceCollection();

    services.AddSharpWpf();

    // 添加国际化支持
    var resourceManager = new ResourceManager(
        "LocalizationExample.Resources.Strings",  // 资源文件的完整命名空间
        Assembly.GetExecutingAssembly());

    services.AddLocalization(
        resourceManager,
        new CultureInfo("zh-CN"),  // 中文
        new CultureInfo("en-US"),  // 英文
        new CultureInfo("ja-JP")   // 日文
    );

    services.AddViewModel<MainViewModel>();

    var provider = services.BuildServiceProvider();
    var mainWindow = provider.GetRequiredService<MainWindow>();
    mainWindow.Show();
}

3. MainViewModel.cs - 语言切换

using Wpf.Localization;

public class MainViewModel : ViewModelBase
{
    private readonly ILocalizationService _localization;
    private readonly IDialogService _dialogService;

    public ILocalizationService Localization => _localization;

    public List<CultureInfo> AvailableLanguages { get; }

    public CultureInfo CurrentLanguage
    {
        get => _localization.CurrentCulture;
        set
        {
            _localization.ChangeCulture(value);
            OnPropertyChanged();
        }
    }

    public ICommand ChangeLanguageCommand { get; }
    public ICommand ShowGreetingCommand { get; }

    public MainViewModel(
        ILocalizationService localization,
        IDialogService dialogService)
    {
        _localization = localization;
        _dialogService = dialogService;

        AvailableLanguages = localization.GetSupportedCultures().ToList();

        // 监听语言变化
        _localization.CultureChanged += (s, culture) =>
        {
            _dialogService.ShowMessage(
                $"语言已切换到: {culture.NativeName}",
                "提示");
        };

        ChangeLanguageCommand = new RelayCommand<CultureInfo>(culture =>
        {
            if (culture != null)
            {
                CurrentLanguage = culture;
            }
        });

        ShowGreetingCommand = new RelayCommand(() =>
        {
            // 使用格式化字符串
            var message = _localization.GetString("WelcomeUser", "张三");
            _dialogService.ShowMessage(message, "问候");
        });
    }
}

4. MainWindow.xaml - 使用本地化绑定

<Window x:Class="LocalizationExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:loc="clr-namespace:Wpf.Localization;assembly=Sharp.Wpf"
        Title="国际化示例" Height="400" Width="500">
    <StackPanel Margin="20">
        <!-- 方式1: 使用 LocalizeExtension(推荐) -->
        <TextBlock Text="{loc:Localize Welcome}" FontSize="24" FontWeight="Bold"/>

        <!-- 语言选择 -->
        <StackPanel Orientation="Horizontal" Margin="0,20,0,0">
            <TextBlock Text="选择语言:" VerticalAlignment="Center" Margin="0,0,10,0"/>
            <ComboBox ItemsSource="{Binding AvailableLanguages}"
                      SelectedItem="{Binding CurrentLanguage}"
                      DisplayMemberPath="NativeName"
                      Width="150"/>
        </StackPanel>

        <!-- 登录表单 -->
        <StackPanel Margin="0,30,0,0">
            <!-- 用户名 -->
            <TextBlock Text="{loc:Localize Username}"/>
            <TextBox Margin="0,5,0,15"/>

            <!-- 密码 -->
            <TextBlock Text="{loc:Localize Password}"/>
            <PasswordBox Margin="0,5,0,15"/>

            <!-- 登录按钮 -->
            <Button Content="{loc:Localize Login}"
                    Height="35" FontSize="14"/>
        </StackPanel>

        <!-- 方式2: 直接绑定 Localization 服务 -->
        <StackPanel Margin="0,30,0,0">
            <TextBlock Text="{Binding Localization[Welcome]}" FontStyle="Italic"/>
            <TextBlock Text="{Binding Localization[Username]}" Margin="0,5,0,0"/>
        </StackPanel>

        <!-- 测试格式化字符串 -->
        <Button Content="显示问候" Command="{Binding ShowGreetingCommand}" Margin="0,20,0,0"/>
    </StackPanel>
</Window>

5. 在代码中使用本地化

// 方式1: 通过服务获取
var welcomeText = _localization.GetString("Welcome");

// 方式2: 带格式化参数
var greeting = _localization.GetString("WelcomeUser", username);

// 方式3: 索引器(用于绑定)
var password = _localization["Password"];

🎓 练习建议

练习1:添加更多语言

添加法语(fr-FR)或德语(de-DE)资源文件。

练习2:保存语言偏好

将用户选择的语言保存到配置文件,下次启动时自动加载。

练习3:动态加载资源

实现从外部 JSON 文件加载翻译资源(而非编译进程序)。

练习4:翻译进度显示

显示每种语言的翻译完成度(如:英文 95%,日文 60%)。

💡 常见问题

Q: 资源文件修改后不生效? A: 确保:

  1. 资源文件的"生成操作"设为"嵌入的资源"
  2. 重新生成项目(Clean + Rebuild)

Q: 如何支持从右到左(RTL)语言? A: 监听 CultureChanged 事件,设置 FlowDirection:

_localization.CultureChanged += (s, culture) => {
    if (culture.TextInfo.IsRightToLeft) {
        Application.Current.MainWindow.FlowDirection = FlowDirection.RightToLeft;
    }
};

Q: 能否热重载资源文件? A: Sharp.Wpf 当前使用编译时嵌入资源,不支持运行时重载。可以使用外部 JSON 文件实现热重载。

Q: 如何处理复数形式? A: 使用不同的资源键或使用占位符:

"{Count} items" → "{0} 个项目"

📚 延伸阅读


构建面向全球用户的应用! 🌍