279 lines
7.9 KiB
Markdown
279 lines
7.9 KiB
Markdown
# 05 - 国际化(i18n)示例
|
||
|
||
演示如何使用 Sharp.Wpf 的国际化功能实现多语言支持,包括运行时切换语言、资源文件管理等。
|
||
|
||
## 🎯 学习目标
|
||
|
||
- 使用 `ILocalizationService` 实现多语言
|
||
- 在 XAML 中使用 `{loc:Localize}` 绑定本地化字符串
|
||
- 运行时动态切换语言
|
||
- 管理资源文件(.resx)
|
||
- 格式化带参数的本地化字符串
|
||
|
||
## 📦 使用的模块
|
||
|
||
- **Sharp.Wpf** - ILocalizationService, LocalizeExtension
|
||
|
||
## 🚀 快速运行
|
||
|
||
```bash
|
||
cd examples/05-Localization
|
||
dotnet run
|
||
```
|
||
|
||
## 📝 核心代码讲解
|
||
|
||
### 1. 创建资源文件
|
||
|
||
```
|
||
Resources/
|
||
├── Strings.resx # 默认语言(中文)
|
||
├── Strings.en-US.resx # 英文
|
||
└── Strings.ja-JP.resx # 日文(可选)
|
||
```
|
||
|
||
**Strings.resx** (中文):
|
||
```xml
|
||
<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** (英文):
|
||
```xml
|
||
<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 - 注册国际化服务
|
||
|
||
```csharp
|
||
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 - 语言切换
|
||
|
||
```csharp
|
||
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 - 使用本地化绑定
|
||
|
||
```xml
|
||
<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. 在代码中使用本地化
|
||
|
||
```csharp
|
||
// 方式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`:
|
||
```csharp
|
||
_localization.CultureChanged += (s, culture) => {
|
||
if (culture.TextInfo.IsRightToLeft) {
|
||
Application.Current.MainWindow.FlowDirection = FlowDirection.RightToLeft;
|
||
}
|
||
};
|
||
```
|
||
|
||
**Q: 能否热重载资源文件?**
|
||
A: Sharp.Wpf 当前使用编译时嵌入资源,不支持运行时重载。可以使用外部 JSON 文件实现热重载。
|
||
|
||
**Q: 如何处理复数形式?**
|
||
A: 使用不同的资源键或使用占位符:
|
||
```
|
||
"{Count} items" → "{0} 个项目"
|
||
```
|
||
|
||
## 📚 延伸阅读
|
||
|
||
- [.NET 资源文件](https://learn.microsoft.com/zh-cn/dotnet/core/extensions/resources)
|
||
- [CultureInfo 类](https://learn.microsoft.com/zh-cn/dotnet/api/system.globalization.cultureinfo)
|
||
- [WPF 本地化](https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/advanced/globalization-for-wpf)
|
||
- 下一个示例:[06-Theming](../06-Theming/README.md)
|
||
|
||
---
|
||
|
||
**构建面向全球用户的应用!** 🌍
|