Files
better-genshin-impact/BetterGenshinImpact/View/Windows/FeedWindow.xaml
辉鸭蛋 9f67726781 I18n v2 (#2709)
* feat(i18n): 添加界面与日志的国际化支持

- 新增 ITranslationService 接口及 JsonTranslationService 实现,提供基于 JSON 的翻译服务
- 添加 TrConverter 转换器,支持通过绑定动态翻译界面文本
- 引入 AutoTranslateInterceptor 行为,自动扫描并翻译界面中的静态文本
- 集成 TranslatingSerilogLoggerProvider,实现日志输出的实时翻译
- 在 App.xaml 中注册全局样式,为 Window、UserControl 和 Page 启用自动翻译

* refactor(AutoTranslateInterceptor): 优化自动翻译拦截器的加载与应用机制

- 移除 HomePage 中冗余的 EnableAutoTranslate 属性设置,改为继承属性
- 通过类构造函数注册全局 Loaded 事件处理器,替代在每个元素上单独添加
- 引入请求队列机制,批量处理待应用翻译的元素,避免重复调度
- 扩展属性类型检查,支持 object 类型以处理更多动态内容场景

* fix: 移除全局自动翻译拦截器以避免冲突

移除在 App.xaml 中为 Window、UserControl 和 Page 全局设置的 AutoTranslateInterceptor,
改为仅在 PickerWindow 中显式启用。这解决了全局样式可能导致的意外行为或冲突。

* feat(ui): 为多个窗口启用自动翻译拦截器

为 MapLabelSearchWindow、ArtifactOcrDialog、PromptDialog 等 14 个窗口添加了 AutoTranslateInterceptor.EnableAutoTranslate 属性,以启用自动翻译拦截功能。

* feat(i18n): 添加国际化目录支持并优化异常处理

* feat(ui): 添加软件UI语言设置并改进翻译服务

- 在通用设置页面新增UI语言选择控件,支持动态切换界面语言
- 修改游戏语言标签为“原神游戏语言”以明确区分
- 改进JsonTranslationService,支持UI语言切换时的实时翻译更新
- 优化AutoTranslateInterceptor,缓存原始文本值并在语言切换时恢复
- 添加属性变更监听机制,确保UI元素在语言切换后正确刷新

* feat(自动翻译): 添加排除自动翻译的依赖属性

在 AutoTranslateInterceptor 中新增 ExcludeAutoTranslate 附加属性,允许对特定依赖对象禁用自动翻译功能。当遍历元素进行翻译时,会检查此属性并跳过已标记排除的元素。

* feat(translation): 为缺失文本翻译添加详细上下文信息

扩展翻译服务以收集缺失文本的详细上下文,包括视图路径、元素类型、属性名等。
重构 `ITranslationService` 接口,引入 `TranslationSourceInfo` 类封装上下文信息。
修改 `AutoTranslateInterceptor` 自动收集 UI 元素信息,`JsonTranslationService` 合并多来源上下文。

* Revert "feat(自动翻译): 添加排除自动翻译的依赖属性"

This reverts commit a1c2334951.

* fix: 跳过 GridViewRowPresenter 中的文本翻译

添加 IsInGridViewRowPresenter 检查,避免在 GridViewRowPresenter 控件内进行自动翻译,防止潜在的界面显示问题。

* fix: 修复自动翻译拦截器在组合框上下文中的误触发

在自动翻译拦截器中添加了 IsInComboBoxContext 方法,用于检测依赖对象是否处于 ComboBox 或其相关弹出菜单的上下文中。当检测到对象位于组合框上下文时,跳过自动翻译逻辑,避免对下拉选项等界面元素进行不必要的翻译操作,从而解决潜在的界面干扰问题。

* feat(translation): 添加缺失翻译上报至 Supabase 的功能

- 新增 IMissingTranslationReporter 接口及 SupabaseMissingTranslationReporter 实现
- 在 JsonTranslationService 中集成缺失翻译上报逻辑
- 添加缺失翻译收集的配置设置(MissingTranslationCollectionSettings)
- 优化缺失翻译文件的序列化格式,将 Source 字段改为紧凑的数字表示
- 移除 ScriptRepoUpdater 中未使用的 using 语句
- 在 App.xaml.cs 中注册 SupabaseMissingTranslationReporter 服务

* fix: 修复自动翻译功能中原始值恢复和重复报告问题

- 移除未使用的法语翻译支持以简化语言选项
- 修复 Supabase 报告序列化时移除冗余字段
- 添加已缺失翻译键的缓存以避免重复报告
- 重构自动翻译拦截器,将原始值存储移至依赖属性
- 修复原始值恢复逻辑,确保正确遍历所有子元素

* feat(ui): 添加更新UI语言文件功能

- 在 ITranslationService 接口中添加 Reload 方法
- 在 JsonTranslationService 中实现 Reload 方法,支持重新加载语言文件并发送变更通知
- 在通用设置页面添加“更新”按钮,点击后从远程仓库下载最新语言文件
- 实现 OnUpdateUiLanguageAsync 命令,支持从 GitHub 和镜像源下载语言文件
- 下载后自动替换本地文件并重新加载翻译服务
2026-02-15 19:03:33 +08:00

269 lines
17 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<ui:FluentWindow x:Class="BetterGenshinImpact.View.Windows.FeedWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:behavior="clr-namespace:BetterGenshinImpact.View.Behavior"
Width="400"
Height="700"
MinWidth="400"
MinHeight="250"
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
ExtendsContentIntoTitleBar="True"
behavior:AutoTranslateInterceptor.EnableAutoTranslate="True"
FontFamily="{DynamicResource TextThemeFontFamily}"
WindowBackdropType="Mica"
WindowStartupLocation="CenterScreen"
WindowStyle="SingleBorderWindow"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- 标题栏 -->
<ui:TitleBar Name="MyTitleBar" Grid.Row="0" Title="兑换码">
<ui:TitleBar.Icon>
<ui:ImageIcon Source="pack://application:,,,/Resources/Images/logo.png" />
</ui:TitleBar.Icon>
</ui:TitleBar>
<!-- 主要内容区域 -->
<Grid Grid.Row="1" Margin="20,10,20,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- 标题和描述 -->
<StackPanel Grid.Row="0" Margin="0,0,0,15">
<ui:TextBlock FontSize="24"
FontWeight="SemiBold"
Text="最新兑换码动态"
Margin="0,0,0,5" />
<ui:TextBlock FontSize="14"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}"
Text="查看最新的国服原神的兑换码,一键自动兑换"
TextWrapping="Wrap" />
</StackPanel>
<!-- 加载状态 -->
<StackPanel Grid.Row="1"
VerticalAlignment="Top"
Height="16"
Orientation="Horizontal"
Margin="0,0,0,10"
Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}">
<ui:ProgressRing Width="16" Height="16" IsIndeterminate="True" Margin="0,0,8,0" />
<ui:TextBlock Text="正在加载兑换码..." Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}" />
</StackPanel>
<!-- Feed 列表 -->
<ScrollViewer Grid.Row="2"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding FeedItems}" Padding="0,0,16,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ui:Card Margin="0,0,0,12"
Padding="16">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- 标题 -->
<ui:TextBlock Grid.Row="0"
FontSize="16"
FontWeight="SemiBold"
Text="{Binding Title}"
TextWrapping="Wrap"
Margin="0,0,0,8" />
<!-- 时间与有效期同一行更紧凑Valid 为空或 null 时隐藏) -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,6">
<ui:TextBlock FontSize="12"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="{Binding Time}" />
<ui:TextBlock FontSize="12"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text=" | "
Margin="8,0,8,0">
<ui:TextBlock.Style>
<Style TargetType="{x:Type ui:TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Valid}" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding Valid}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</ui:TextBlock.Style>
</ui:TextBlock>
<ui:TextBlock FontSize="12"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="有效期至">
<ui:TextBlock.Style>
<Style TargetType="{x:Type ui:TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Valid}" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding Valid}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</ui:TextBlock.Style>
</ui:TextBlock>
<ui:TextBlock FontSize="12"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="{Binding Valid}"
Margin="4,0,0,0">
<ui:TextBlock.Style>
<Style TargetType="{x:Type ui:TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Valid}" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding Valid}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</ui:TextBlock.Style>
</ui:TextBlock>
</StackPanel>
<!-- 内容:卡片式展示,增强层次与可读性 -->
<StackPanel Grid.Row="2"
Visibility="{Binding Content, Converter={StaticResource BooleanToVisibilityRevertConverter}}"
Margin="0,0,0,12">
<!--<ui:TextBlock Text="内容"
FontSize="12"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Margin="0,0,0,6"/>-->
<Border Background="{DynamicResource ControlFillColorDefaultBrush}"
BorderBrush="{DynamicResource ControlStrokeColorDefaultBrush}"
CornerRadius="{DynamicResource ControlCornerRadius}"
BorderThickness="1"
Padding="12">
<ui:TextBlock FontSize="14"
Text="{Binding Content}"
TextWrapping="Wrap"
LineHeight="20" />
</Border>
</StackPanel>
<!-- 兑换码列表胶囊标签WrapPanel自动换行 -->
<StackPanel Grid.Row="3" Margin="0,0,0,8">
<!--<ui:TextBlock Text="兑换码"
FontSize="12"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Margin="0,0,0,6"/>-->
<ItemsControl ItemsSource="{Binding Codes}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="{DynamicResource ControlFillColorDefaultBrush}"
BorderBrush="{DynamicResource ControlElevationBorderBrush}"
CornerRadius="4"
BorderThickness="1"
Padding="8,4"
Margin="0,0,8,8">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<ui:SymbolIcon Symbol="TicketDiagonal24" Margin="0,0,6,0" />
<ui:TextBlock FontSize="14"
LineHeight="16"
FontFamily="Consolas"
Text="{Binding}"
Foreground="{DynamicResource TextFillColorPrimaryBrush}" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<!-- 标签 -->
<StackPanel Grid.Row="4"
Orientation="Horizontal"
Visibility="{Binding HasTag, Converter={StaticResource BooleanToVisibilityConverter}}">
<Border CornerRadius="10"
Background="{DynamicResource CardBackground}"
BorderBrush="{DynamicResource CardBorderBrush}">
<ui:TextBlock FontSize="11"
VerticalAlignment="Center"
Foreground="White"
Text="{Binding Tag}" />
</Border>
</StackPanel>
<!-- 按钮组 -->
<StackPanel Grid.Row="5"
Orientation="Horizontal"
Margin="0,0,0,8">
<ui:Button Content="复制"
Appearance="Secondary"
FontSize="12"
Padding="12,6"
Margin="0,0,8,0"
Command="{Binding DataContext.CopyItemCodesCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}" />
<ui:Button Content="一键兑换"
Appearance="Primary"
FontSize="12"
Padding="12,6"
Command="{Binding DataContext.AutoRedeemItemCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}" />
</StackPanel>
</Grid>
</ui:Card>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
<!-- 底部按钮 -->
<Grid Grid.Row="2" Margin="40,10,20,20">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Right">
<ui:Button Name="BtnGetLiveCodes"
Margin="0,0,10,0"
Appearance="Secondary"
Content="实时获取前瞻兑换码"
Command="{Binding GetLiveRedeemCodesCommand}"
Visibility="{Binding IsDisplayBtnGetLiveCodes, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
<ui:Button Name="BtnClose"
Content="关闭"
Click="BtnCloseClick" />
</StackPanel>
</Grid>
</Grid>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
</ui:FluentWindow>