Compare commits
2 Commits
5ab48c4db6
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b3bf46097 | |||
| 6e664a7762 |
@@ -17,12 +17,12 @@
|
||||
### Windows
|
||||
|
||||
数据库: %APPDATA%\Local\OCES\Resonance\Databases
|
||||
偏好设置: %APPDATA%\Roaming\OCES\Resonance\
|
||||
偏好设置: %APPDATA%\Roaming\OCES\Resonance\preferences.json
|
||||
|
||||
### Linux
|
||||
|
||||
数据库: XDG_DATA_HOME
|
||||
偏好设置: XDG_CONFIG_HOME
|
||||
偏好设置: XDG_CONFIG_HOME/preferences.json
|
||||
缓存数据: XDG_CACHE_HOME
|
||||
|
||||
### TODO
|
||||
@@ -30,7 +30,8 @@
|
||||
- [ ] 扫描时如果报错,报错信息可能会填满整个窗口,导致Overlay无法关闭。
|
||||
- [ ] 读取时如果报错,没有任何警告,会静默报错。需要有一个界面右下方的toast,或是发送系统通知告知用户遇到了错误。
|
||||
- [x] 指针化Artwork字段。如果artwork的md5
|
||||
- [ ] 本地化框架
|
||||
- [x] 本地化框架
|
||||
- [ ] 偏好设置/控制面板
|
||||
|
||||
## 技术栈
|
||||
|
||||
|
||||
+5
-5
@@ -3,22 +3,22 @@
|
||||
x:Class="GUI.App"
|
||||
Name="Resonance"
|
||||
xmlns:local="using:GUI"
|
||||
xmlns:l10n="using:GUI.Converters"
|
||||
RequestedThemeVariant="Default">
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
<Application.DataTemplates>
|
||||
<local:ViewLocator/>
|
||||
</Application.DataTemplates>
|
||||
|
||||
|
||||
|
||||
<NativeMenu.Menu>
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Header="关于…" />
|
||||
<NativeMenuItem Header="偏好设置…"/>
|
||||
<NativeMenuItem Header="{l10n:Localize About}" />
|
||||
<NativeMenuItem Header="{l10n:Localize Preferences}"/>
|
||||
</NativeMenu>
|
||||
</NativeMenu.Menu>
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
</Application>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Data.Core;
|
||||
using Avalonia.Data.Core.Plugins;
|
||||
using System.Linq;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using GUI.Services;
|
||||
using GUI.ViewModels;
|
||||
using GUI.Views;
|
||||
|
||||
|
||||
Generated
-48
@@ -1,48 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace GUI {
|
||||
using System;
|
||||
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Locale {
|
||||
|
||||
private static System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Locale() {
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.Equals(null, resourceMan)) {
|
||||
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("GUI.Locale", typeof(Locale).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,25 @@
|
||||
using Avalonia.Data;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using GUI.Services;
|
||||
|
||||
namespace GUI.Converters;
|
||||
|
||||
public class LocalizeExtension : MarkupExtension
|
||||
{
|
||||
public string Key { get; set; }
|
||||
|
||||
public LocalizeExtension(string key)
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
|
||||
public override object ProvideValue(IServiceProvider serviceProvider)
|
||||
{
|
||||
return new Binding
|
||||
{
|
||||
Source = LocalizationService.Instance,
|
||||
Path = $"[{Key}]",
|
||||
Mode = BindingMode.OneWay,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using GUI.Services;
|
||||
|
||||
namespace GUI.Converters;
|
||||
|
||||
@@ -36,8 +37,8 @@ public class ChannelsFormatConverter : IValueConverter
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
int ch when ch == 1 => "Mono",
|
||||
int ch when ch == 2 => "Stereo",
|
||||
int ch when ch == 1 => LocalizationService.Instance["ChannelsMono"],
|
||||
int ch when ch == 2 => LocalizationService.Instance["ChannelsStereo"],
|
||||
null => "--",
|
||||
int ch => $"{ch}ch",
|
||||
_ => "--",
|
||||
|
||||
+6
-11
@@ -10,6 +10,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Models\"/>
|
||||
<Folder Include="Resources\"/>
|
||||
<AvaloniaResource Include="Assets\**"/>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -32,17 +33,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Assets\Locale.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Locale.Designer.cs</LastGenOutput>
|
||||
<EmbeddedResource Include="Resources\LocaleZh.xml">
|
||||
<LogicalName>GUI.LocaleZh.xml</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Resources\LocaleEn.xml">
|
||||
<LogicalName>GUI.LocaleEn.xml</LogicalName>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Assets\Locale.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Locale.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="About" xml:space="preserve">
|
||||
<value>About…</value>
|
||||
</data>
|
||||
<data name="Preferences" xml:space="preserve">
|
||||
<value>Preferences…</value>
|
||||
</data>
|
||||
<data name="Database" xml:space="preserve">
|
||||
<value>Database</value>
|
||||
</data>
|
||||
<data name="MenuDeleteOrphanArtwork" xml:space="preserve">
|
||||
<value>Delete Unused Artworks</value>
|
||||
</data>
|
||||
<data name="MenuOpenDatabaseFolder" xml:space="preserve">
|
||||
<value>Open database file in Finder...</value>
|
||||
</data>
|
||||
<data name="ScanDirectory" xml:space="preserve">
|
||||
<value>Scan Directory</value>
|
||||
</data>
|
||||
<data name="SelectAudioDirectory" xml:space="preserve">
|
||||
<value>Select Audio Directory</value>
|
||||
</data>
|
||||
<data name="SearchFilesHint" xml:space="preserve">
|
||||
<value>Search files...</value>
|
||||
</data>
|
||||
<data name="Refresh" xml:space="preserve">
|
||||
<value>Refresh</value>
|
||||
</data>
|
||||
<data name="ResetFilters" xml:space="preserve">
|
||||
<value>Reset Filters</value>
|
||||
</data>
|
||||
<data name="NoFilesHint" xml:space="preserve">
|
||||
<value>No files yet. Scan a directory to add audio files.</value>
|
||||
</data>
|
||||
<data name="Metadata" xml:space="preserve">
|
||||
<value>Metadata</value>
|
||||
</data>
|
||||
<data name="Save" xml:space="preserve">
|
||||
<value>Save</value>
|
||||
</data>
|
||||
<data name="BasicInfo" xml:space="preserve">
|
||||
<value>Basic Info</value>
|
||||
</data>
|
||||
<data name="Rating" xml:space="preserve">
|
||||
<value>Rating</value>
|
||||
</data>
|
||||
<data name="TagsAndDescription" xml:space="preserve">
|
||||
<value>Tags & Description</value>
|
||||
</data>
|
||||
<data name="Category" xml:space="preserve">
|
||||
<value>Category</value>
|
||||
</data>
|
||||
<data name="KeywordsCommaSeparated" xml:space="preserve">
|
||||
<value>Keywords (comma-separated)</value>
|
||||
</data>
|
||||
<data name="Description" xml:space="preserve">
|
||||
<value>Description</value>
|
||||
</data>
|
||||
<data name="Notes" xml:space="preserve">
|
||||
<value>Notes</value>
|
||||
</data>
|
||||
<data name="DurationLabel" xml:space="preserve">
|
||||
<value>Duration: </value>
|
||||
</data>
|
||||
<data name="SampleRateLabel" xml:space="preserve">
|
||||
<value>Sample Rate: </value>
|
||||
</data>
|
||||
<data name="BitDepthLabel" xml:space="preserve">
|
||||
<value>Bit Depth: </value>
|
||||
</data>
|
||||
<data name="ChannelsLabel" xml:space="preserve">
|
||||
<value>Channels: </value>
|
||||
</data>
|
||||
<data name="TypeLabel" xml:space="preserve">
|
||||
<value>Format: </value>
|
||||
</data>
|
||||
<data name="SelectFileToViewMetadata" xml:space="preserve">
|
||||
<value>Select a file to view metadata</value>
|
||||
</data>
|
||||
<data name="CategoryPlaceholder" xml:space="preserve">
|
||||
<value>Enter category...</value>
|
||||
</data>
|
||||
<data name="KeywordsPlaceholder" xml:space="preserve">
|
||||
<value>Enter keywords...</value>
|
||||
</data>
|
||||
<data name="DescriptionPlaceholder" xml:space="preserve">
|
||||
<value>Enter description...</value>
|
||||
</data>
|
||||
<data name="NotesPlaceholder" xml:space="preserve">
|
||||
<value>Enter notes...</value>
|
||||
</data>
|
||||
<data name="Processed" xml:space="preserve">
|
||||
<value>Processed: </value>
|
||||
</data>
|
||||
<data name="AddedFormat" xml:space="preserve">
|
||||
<value>Added: {0}</value>
|
||||
</data>
|
||||
<data name="SkippedFormat" xml:space="preserve">
|
||||
<value>Skipped: {0}</value>
|
||||
</data>
|
||||
<data name="Close" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
</data>
|
||||
<data name="Browse" xml:space="preserve">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
<data name="AllFiles" xml:space="preserve">
|
||||
<value>All Files</value>
|
||||
</data>
|
||||
<data name="Tags" xml:space="preserve">
|
||||
<value>Tags</value>
|
||||
</data>
|
||||
<data name="ScanPreparing" xml:space="preserve">
|
||||
<value>Preparing to scan...</value>
|
||||
</data>
|
||||
<data name="ScanDiscovering" xml:space="preserve">
|
||||
<value>Discovering files...</value>
|
||||
</data>
|
||||
<data name="ScanParsing" xml:space="preserve">
|
||||
<value>Found {0} files, parsing metadata...</value>
|
||||
</data>
|
||||
<data name="ScanComplete" xml:space="preserve">
|
||||
<value>Scan complete: {0} added, {1} skipped</value>
|
||||
</data>
|
||||
<data name="ScanError" xml:space="preserve">
|
||||
<value>Scan error: {0}</value>
|
||||
</data>
|
||||
<data name="NoFileSelected" xml:space="preserve">
|
||||
<value>No file selected</value>
|
||||
</data>
|
||||
<data name="ChannelsMono" xml:space="preserve">
|
||||
<value>Mono</value>
|
||||
</data>
|
||||
<data name="ChannelsStereo" xml:space="preserve">
|
||||
<value>Stereo</value>
|
||||
</data>
|
||||
<data name="SortByField" xml:space="preserve">
|
||||
<value>Sort by</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,157 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="About" xml:space="preserve">
|
||||
<value>关于…</value>
|
||||
</data>
|
||||
<data name="Preferences" xml:space="preserve">
|
||||
<value>偏好设置…</value>
|
||||
</data>
|
||||
<data name="Database" xml:space="preserve">
|
||||
<value>数据库</value>
|
||||
</data>
|
||||
<data name="MenuDeleteOrphanArtwork" xml:space="preserve">
|
||||
<value>删除没有使用的封面</value>
|
||||
</data>
|
||||
<data name="MenuOpenDatabaseFolder" xml:space="preserve">
|
||||
<value>在访达中打开数据库文件…</value>
|
||||
</data>
|
||||
<data name="ScanDirectory" xml:space="preserve">
|
||||
<value>扫描目录</value>
|
||||
</data>
|
||||
<data name="SelectAudioDirectory" xml:space="preserve">
|
||||
<value>选择音频目录</value>
|
||||
</data>
|
||||
<data name="SearchFilesHint" xml:space="preserve">
|
||||
<value>搜索文件...</value>
|
||||
</data>
|
||||
<data name="Refresh" xml:space="preserve">
|
||||
<value>刷新</value>
|
||||
</data>
|
||||
<data name="ResetFilters" xml:space="preserve">
|
||||
<value>重置筛选</value>
|
||||
</data>
|
||||
<data name="NoFilesHint" xml:space="preserve">
|
||||
<value>暂无文件,请先扫描目录添加音频文件。</value>
|
||||
</data>
|
||||
<data name="Metadata" xml:space="preserve">
|
||||
<value>元数据</value>
|
||||
</data>
|
||||
<data name="Save" xml:space="preserve">
|
||||
<value>保存</value>
|
||||
</data>
|
||||
<data name="BasicInfo" xml:space="preserve">
|
||||
<value>基础信息</value>
|
||||
</data>
|
||||
<data name="Rating" xml:space="preserve">
|
||||
<value>评分</value>
|
||||
</data>
|
||||
<data name="TagsAndDescription" xml:space="preserve">
|
||||
<value>标签与描述</value>
|
||||
</data>
|
||||
<data name="Category" xml:space="preserve">
|
||||
<value>分类</value>
|
||||
</data>
|
||||
<data name="KeywordsCommaSeparated" xml:space="preserve">
|
||||
<value>关键词(逗号分隔)</value>
|
||||
</data>
|
||||
<data name="Description" xml:space="preserve">
|
||||
<value>描述</value>
|
||||
</data>
|
||||
<data name="Notes" xml:space="preserve">
|
||||
<value>备注</value>
|
||||
</data>
|
||||
<data name="DurationLabel" xml:space="preserve">
|
||||
<value>时长: </value>
|
||||
</data>
|
||||
<data name="SampleRateLabel" xml:space="preserve">
|
||||
<value>采样率: </value>
|
||||
</data>
|
||||
<data name="BitDepthLabel" xml:space="preserve">
|
||||
<value>位深: </value>
|
||||
</data>
|
||||
<data name="ChannelsLabel" xml:space="preserve">
|
||||
<value>声道: </value>
|
||||
</data>
|
||||
<data name="TypeLabel" xml:space="preserve">
|
||||
<value>格式: </value>
|
||||
</data>
|
||||
<data name="SelectFileToViewMetadata" xml:space="preserve">
|
||||
<value>选择文件查看元数据</value>
|
||||
</data>
|
||||
<data name="CategoryPlaceholder" xml:space="preserve">
|
||||
<value>输入分类...</value>
|
||||
</data>
|
||||
<data name="KeywordsPlaceholder" xml:space="preserve">
|
||||
<value>输入关键词...</value>
|
||||
</data>
|
||||
<data name="DescriptionPlaceholder" xml:space="preserve">
|
||||
<value>输入描述...</value>
|
||||
</data>
|
||||
<data name="NotesPlaceholder" xml:space="preserve">
|
||||
<value>输入备注...</value>
|
||||
</data>
|
||||
<data name="Processed" xml:space="preserve">
|
||||
<value>已处理: </value>
|
||||
</data>
|
||||
<data name="AddedFormat" xml:space="preserve">
|
||||
<value>新增: {0}</value>
|
||||
</data>
|
||||
<data name="SkippedFormat" xml:space="preserve">
|
||||
<value>跳过: {0}</value>
|
||||
</data>
|
||||
<data name="Close" xml:space="preserve">
|
||||
<value>关闭</value>
|
||||
</data>
|
||||
<data name="Browse" xml:space="preserve">
|
||||
<value>浏览</value>
|
||||
</data>
|
||||
<data name="AllFiles" xml:space="preserve">
|
||||
<value>全部文件</value>
|
||||
</data>
|
||||
<data name="Tags" xml:space="preserve">
|
||||
<value>标签</value>
|
||||
</data>
|
||||
<data name="ScanPreparing" xml:space="preserve">
|
||||
<value>准备扫描...</value>
|
||||
</data>
|
||||
<data name="ScanDiscovering" xml:space="preserve">
|
||||
<value>正在发现文件...</value>
|
||||
</data>
|
||||
<data name="ScanParsing" xml:space="preserve">
|
||||
<value>发现 {0} 个文件,正在解析元数据...</value>
|
||||
</data>
|
||||
<data name="ScanComplete" xml:space="preserve">
|
||||
<value>扫描完成:新增 {0},跳过 {1}</value>
|
||||
</data>
|
||||
<data name="ScanError" xml:space="preserve">
|
||||
<value>扫描出错:{0}</value>
|
||||
</data>
|
||||
<data name="NoFileSelected" xml:space="preserve">
|
||||
<value>未选择文件</value>
|
||||
</data>
|
||||
<data name="ChannelsMono" xml:space="preserve">
|
||||
<value>单声道</value>
|
||||
</data>
|
||||
<data name="ChannelsStereo" xml:space="preserve">
|
||||
<value>立体声</value>
|
||||
</data>
|
||||
<data name="SortByField" xml:space="preserve">
|
||||
<value>排序字段</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,168 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Xml;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace GUI.Services;
|
||||
|
||||
public sealed class LocalizationService : ObservableObject
|
||||
{
|
||||
public static LocalizationService Instance { get; } = new();
|
||||
|
||||
private readonly Dictionary<string, Dictionary<string, string>> _strings = new();
|
||||
|
||||
private CultureInfo _currentCulture = CultureInfo.GetCultureInfo("zh-CN");
|
||||
private string _currentCultureKey = "zh-CN";
|
||||
|
||||
public CultureInfo CurrentCulture
|
||||
{
|
||||
get => _currentCulture;
|
||||
set
|
||||
{
|
||||
var key = value.Name;
|
||||
if (key != "zh-CN" && key != "en-US")
|
||||
key = "zh-CN";
|
||||
|
||||
if (!SetProperty(ref _currentCulture, CultureInfo.GetCultureInfo(key), nameof(CurrentCulture)))
|
||||
return;
|
||||
|
||||
_currentCultureKey = key;
|
||||
CultureInfo.CurrentCulture = _currentCulture;
|
||||
CultureInfo.CurrentUICulture = _currentCulture;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<CultureInfo> SupportedCultures { get; }
|
||||
|
||||
private LocalizationService()
|
||||
{
|
||||
LoadResources("zh-CN", "GUI.LocaleZh.xml");
|
||||
LoadResources("en-US", "GUI.LocaleEn.xml");
|
||||
|
||||
SupportedCultures = new ReadOnlyCollection<CultureInfo>(
|
||||
[
|
||||
CultureInfo.GetCultureInfo("zh-CN"),
|
||||
CultureInfo.GetCultureInfo("en-US"),
|
||||
]);
|
||||
|
||||
LoadCulturePreference();
|
||||
}
|
||||
|
||||
private void LoadResources(string cultureKey, string resourceName)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
using var stream = FindResource(assembly, resourceName);
|
||||
if (stream == null) return;
|
||||
|
||||
using var reader = XmlReader.Create(stream);
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.NodeType != XmlNodeType.Element || reader.Name != "data") continue;
|
||||
var name = reader.GetAttribute("name");
|
||||
reader.ReadToDescendant("value");
|
||||
var value = reader.ReadElementContentAsString();
|
||||
if (name != null)
|
||||
dict[name] = value;
|
||||
}
|
||||
|
||||
_strings[cultureKey] = dict;
|
||||
}
|
||||
|
||||
private static Stream? FindResource(Assembly assembly, string resourceName)
|
||||
{
|
||||
var stream = assembly.GetManifestResourceStream(resourceName);
|
||||
if (stream != null) return stream;
|
||||
|
||||
foreach (var name in assembly.GetManifestResourceNames())
|
||||
{
|
||||
if (name.EndsWith(resourceName, StringComparison.OrdinalIgnoreCase))
|
||||
return assembly.GetManifestResourceStream(name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_strings.TryGetValue(_currentCultureKey, out var dict) && dict.TryGetValue(key, out var val))
|
||||
return val;
|
||||
if (_currentCultureKey != "zh-CN" && _strings.TryGetValue("zh-CN", out var fallback) && fallback.TryGetValue(key, out var fb))
|
||||
return fb;
|
||||
return $"<{key}>";
|
||||
}
|
||||
}
|
||||
|
||||
public string GetFormatted(string key, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return string.Format(_currentCulture, this[key], args);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return this[key];
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadCulturePreference()
|
||||
{
|
||||
var path = GetPreferencesPath();
|
||||
if (!File.Exists(path)) return;
|
||||
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(path);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
if (doc.RootElement.TryGetProperty("Language", out var lang))
|
||||
{
|
||||
var langStr = lang.GetString();
|
||||
if (langStr is "zh-CN" or "en-US")
|
||||
{
|
||||
CurrentCulture = CultureInfo.GetCultureInfo(langStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore malformed preference file
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveCulturePreference()
|
||||
{
|
||||
var path = GetPreferencesPath();
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
if (dir != null)
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
var opts = new JsonSerializerOptions { WriteIndented = true };
|
||||
var json = JsonSerializer.Serialize(new Dictionary<string, string>
|
||||
{
|
||||
["Language"] = _currentCultureKey,
|
||||
}, opts);
|
||||
File.WriteAllText(path, json);
|
||||
}
|
||||
|
||||
private static string GetPreferencesPath()
|
||||
{
|
||||
if (OperatingSystem.IsMacOS())
|
||||
return Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
"Library", "Preferences", "com.oces.Resonance.json");
|
||||
if (OperatingSystem.IsWindows())
|
||||
return Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"OCES", "Resonance", "preferences.json");
|
||||
return Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"OCES", "Resonance", "preferences.json");
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public partial class PlayerBarViewModel : ViewModelBase
|
||||
|
||||
[ObservableProperty] private AudioFileMeta? _currentFile;
|
||||
[ObservableProperty] private bool _hasFile;
|
||||
[ObservableProperty] private string _currentFileName = "未选择文件";
|
||||
[ObservableProperty] private string _currentFileName = string.Empty;
|
||||
|
||||
[ObservableProperty] private bool _isPlaying;
|
||||
[ObservableProperty] private double _currentPosition;
|
||||
@@ -21,6 +21,7 @@ public partial class PlayerBarViewModel : ViewModelBase
|
||||
public PlayerBarViewModel(SyncChannel channel)
|
||||
{
|
||||
_channel = channel;
|
||||
CurrentFileName = Loc["NoFileSelected"];
|
||||
|
||||
_channel.PropertyChanged += (_, e) =>
|
||||
{
|
||||
@@ -39,7 +40,7 @@ public partial class PlayerBarViewModel : ViewModelBase
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
CurrentFileName = "未选择文件";
|
||||
CurrentFileName = Loc["NoFileSelected"];
|
||||
Duration = 0;
|
||||
HasFile = false;
|
||||
return;
|
||||
|
||||
@@ -15,6 +15,9 @@ public partial class ScanProgressViewModel : ViewModelBase
|
||||
[ObservableProperty] private double _progress;
|
||||
[ObservableProperty] private string _currentFile = string.Empty;
|
||||
|
||||
public string AddedText => Loc.GetFormatted("AddedFormat", AddedFiles);
|
||||
public string SkippedText => Loc.GetFormatted("SkippedFormat", SkippedFiles);
|
||||
|
||||
public event Action? ScanCompleted;
|
||||
|
||||
[RelayCommand]
|
||||
@@ -22,6 +25,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(directory)) return;
|
||||
|
||||
StatusText = Loc["ScanPreparing"];
|
||||
Task.Run(() => ScanDirectoryInternal(directory));
|
||||
}
|
||||
|
||||
@@ -32,7 +36,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
||||
{
|
||||
IsScanning = false;
|
||||
HasError = false;
|
||||
StatusText = "准备扫描...";
|
||||
StatusText = Loc["ScanPreparing"];
|
||||
}
|
||||
|
||||
private void ScanDirectoryInternal(string directory)
|
||||
@@ -41,7 +45,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
||||
{
|
||||
HasError = false;
|
||||
IsScanning = true;
|
||||
StatusText = "正在发现文件...";
|
||||
StatusText = Loc["ScanDiscovering"];
|
||||
|
||||
Database.InitializeDatabase();
|
||||
|
||||
@@ -56,7 +60,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
||||
SkippedFiles = 0;
|
||||
Progress = 0;
|
||||
|
||||
StatusText = $"发现 {TotalFiles} 个文件,正在解析元数据...";
|
||||
StatusText = Loc.GetFormatted("ScanParsing", TotalFiles);
|
||||
|
||||
foreach (string filePath in files)
|
||||
{
|
||||
@@ -100,12 +104,12 @@ public partial class ScanProgressViewModel : ViewModelBase
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
StatusText = $"扫描完成:新增 {AddedFiles},跳过 {SkippedFiles}";
|
||||
StatusText = Loc.GetFormatted("ScanComplete", AddedFiles, SkippedFiles);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HasError = true;
|
||||
StatusText = $"扫描出错:{ex.Message}";
|
||||
StatusText = Loc.GetFormatted("ScanError", ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -119,4 +123,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
||||
{
|
||||
Progress = TotalFiles > 0 ? (double)ProcessedFiles / TotalFiles * 100 : 0;
|
||||
}
|
||||
|
||||
partial void OnAddedFilesChanged(int value) => OnPropertyChanged(nameof(AddedText));
|
||||
partial void OnSkippedFilesChanged(int value) => OnPropertyChanged(nameof(SkippedText));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using GUI.Services;
|
||||
|
||||
namespace GUI.ViewModels;
|
||||
|
||||
public abstract class ViewModelBase : ObservableObject
|
||||
{
|
||||
public LocalizationService Loc => LocalizationService.Instance;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:converters="using:GUI.Converters"
|
||||
xmlns:l10n="using:GUI.Converters"
|
||||
mc:Ignorable="d"
|
||||
x:Class="GUI.Views.FileListView"
|
||||
x:DataType="vm:FileListViewModel">
|
||||
@@ -25,22 +26,22 @@
|
||||
BorderThickness="0,0,0,1">
|
||||
<StackPanel Spacing="6">
|
||||
<Grid ColumnDefinitions="*,Auto" Margin="0,0,0,4">
|
||||
<TextBox Grid.Column="0" PlaceholderText="搜索文件..." Text="{Binding SearchText}"/>
|
||||
<TextBox Grid.Column="0" PlaceholderText="{l10n:Localize SearchFilesHint}" Text="{Binding SearchText}"/>
|
||||
<Button Grid.Column="1" Content="⟳" Command="{Binding RefreshFilesCommand}"
|
||||
Width="28" Height="28" ToolTip.Tip="刷新" Margin="4,0,0,0"/>
|
||||
Width="28" Height="28" ToolTip.Tip="{l10n:Localize Refresh}" Margin="4,0,0,0"/>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<ComboBox ItemsSource="{Binding SortOptions}" SelectedItem="{Binding SelectedSortBy}"
|
||||
Width="140" ToolTip.Tip="排序字段"/>
|
||||
<Button Content="重置筛选" Command="{Binding ResetFiltersCommand}"
|
||||
Width="140" ToolTip.Tip="{l10n:Localize SortByField}"/>
|
||||
<Button Content="{l10n:Localize ResetFilters}" Command="{Binding ResetFiltersCommand}"
|
||||
Height="28" Margin="8,0,0,0"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Row="1">
|
||||
<TextBlock Text="暂无文件,请先扫描目录添加音频文件。"
|
||||
<TextBlock Text="{l10n:Localize NoFilesHint}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"
|
||||
FontSize="14"
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
xmlns:views="using:GUI.Views"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:l10n="using:GUI.Converters"
|
||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
||||
x:Class="GUI.Views.MainWindow"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
@@ -19,10 +20,10 @@
|
||||
|
||||
<NativeMenu.Menu>
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Header="Database" IsVisible="True">
|
||||
<NativeMenuItem Header="{l10n:Localize Database}" IsVisible="True">
|
||||
<NativeMenu>
|
||||
<NativeMenuItem Header="Delete Unused Artworks" Command="{Binding CleanOrphanedArtworkCommand}"></NativeMenuItem>
|
||||
<NativeMenuItem Header="Open database file in Finder..."/>
|
||||
<NativeMenuItem Header="{l10n:Localize MenuDeleteOrphanArtwork}" Command="{Binding CleanOrphanedArtworkCommand}"></NativeMenuItem>
|
||||
<NativeMenuItem Header="{l10n:Localize MenuOpenDatabaseFolder}"/>
|
||||
</NativeMenu>
|
||||
</NativeMenuItem>
|
||||
</NativeMenu>
|
||||
@@ -34,8 +35,8 @@
|
||||
<Border Grid.Row="0" Padding="6,4" BorderBrush="{DynamicResource SystemBaseLowColorBrush}"
|
||||
BorderThickness="0,0,0,1" Background="{DynamicResource SystemAltMediumColorBrush}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<Button Content="扫描目录" x:Name="ScanButton" Height="28" Padding="8,0"/>
|
||||
<Button Content="偏好设置" Height="28" Padding="8,0" IsEnabled="False"/>
|
||||
<Button Content="{l10n:Localize ScanDirectory}" x:Name="ScanButton" Height="28" Padding="8,0"/>
|
||||
<Button Content="{l10n:Localize Preferences}" Height="28" Padding="8,0" IsEnabled="False"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Platform.Storage;
|
||||
using GUI.Services;
|
||||
using GUI.ViewModels;
|
||||
|
||||
namespace GUI.Views;
|
||||
@@ -19,7 +20,7 @@ public partial class MainWindow : Window
|
||||
|
||||
IReadOnlyList<IStorageFolder> folders = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = "选择音频目录",
|
||||
Title = LocalizationService.Instance["SelectAudioDirectory"],
|
||||
AllowMultiple = false,
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
xmlns:vm="using:GUI.ViewModels"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:converters="using:GUI.Converters"
|
||||
xmlns:l10n="using:GUI.Converters"
|
||||
mc:Ignorable="d"
|
||||
x:Class="GUI.Views.MetadataPanelView"
|
||||
x:DataType="vm:MetadataPanelViewModel">
|
||||
@@ -14,8 +14,8 @@
|
||||
<ScrollViewer>
|
||||
<Grid RowDefinitions="Auto,*" Margin="8">
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="4" Margin="0,0,0,8">
|
||||
<TextBlock Text="元数据" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
|
||||
<Button Content="保存" Command="{Binding SaveCommand}"
|
||||
<TextBlock Text="{l10n:Localize Metadata}" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
|
||||
<Button Content="{l10n:Localize Save}" Command="{Binding SaveCommand}"
|
||||
IsEnabled="{Binding IsDirty}"
|
||||
Height="28" Width="60" HorizontalAlignment="Right"/>
|
||||
</StackPanel>
|
||||
@@ -27,14 +27,29 @@
|
||||
<Border Padding="6" BorderBrush="{DynamicResource SystemBaseLowColorBrush}" BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="基础信息" FontWeight="SemiBold" FontSize="12"
|
||||
<TextBlock Text="{l10n:Localize BasicInfo}" FontWeight="SemiBold" FontSize="12"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.Filename}" FontSize="13" FontWeight="Bold"/>
|
||||
<TextBlock Text="{Binding CurrentFile.Duration, StringFormat='时长: {0:F2}s'}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.SampleRate, StringFormat='采样率: {0} Hz'}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.BitDepth, StringFormat='位深: {0} bit'}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.Channels, StringFormat='声道: {0}'}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.Type, StringFormat='格式: {0}'}"/>
|
||||
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||
<TextBlock Text="{l10n:Localize DurationLabel}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.Duration, StringFormat='{}{0:F2}s'}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||
<TextBlock Text="{l10n:Localize SampleRateLabel}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.SampleRate, StringFormat='{}{0} Hz'}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||
<TextBlock Text="{l10n:Localize BitDepthLabel}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.BitDepth, StringFormat='{}{0} bit'}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||
<TextBlock Text="{l10n:Localize ChannelsLabel}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.Channels}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||
<TextBlock Text="{l10n:Localize TypeLabel}"/>
|
||||
<TextBlock Text="{Binding CurrentFile.Type}"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -42,7 +57,7 @@
|
||||
<Border Padding="6" BorderBrush="{DynamicResource SystemBaseLowColorBrush}" BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<StackPanel Spacing="4">
|
||||
<TextBlock Text="评分" FontWeight="SemiBold" FontSize="12"
|
||||
<TextBlock Text="{l10n:Localize Rating}" FontWeight="SemiBold" FontSize="12"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||
<Button Content="☆" Command="{Binding SetRatingCommand}" CommandParameter="0"
|
||||
@@ -65,29 +80,29 @@
|
||||
<Border Padding="6" BorderBrush="{DynamicResource SystemBaseLowColorBrush}" BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Text="标签与描述" FontWeight="SemiBold" FontSize="12"
|
||||
<TextBlock Text="{l10n:Localize TagsAndDescription}" FontWeight="SemiBold" FontSize="12"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
|
||||
<TextBlock Text="分类"/>
|
||||
<TextBox Text="{Binding CategoryText}" PlaceholderText="输入分类..."/>
|
||||
<TextBlock Text="{l10n:Localize Category}"/>
|
||||
<TextBox Text="{Binding CategoryText}" PlaceholderText="{l10n:Localize CategoryPlaceholder}"/>
|
||||
|
||||
<TextBlock Text="关键词(逗号分隔)"/>
|
||||
<TextBox Text="{Binding KeywordsText}" PlaceholderText="输入关键词..." MinHeight="40"
|
||||
<TextBlock Text="{l10n:Localize KeywordsCommaSeparated}"/>
|
||||
<TextBox Text="{Binding KeywordsText}" PlaceholderText="{l10n:Localize KeywordsPlaceholder}" MinHeight="40"
|
||||
AcceptsReturn="True"/>
|
||||
|
||||
<TextBlock Text="描述"/>
|
||||
<TextBox Text="{Binding DescriptionText}" PlaceholderText="输入描述..." MinHeight="40"
|
||||
<TextBlock Text="{l10n:Localize Description}"/>
|
||||
<TextBox Text="{Binding DescriptionText}" PlaceholderText="{l10n:Localize DescriptionPlaceholder}" MinHeight="40"
|
||||
AcceptsReturn="True"/>
|
||||
|
||||
<TextBlock Text="备注"/>
|
||||
<TextBox Text="{Binding NotesText}" PlaceholderText="输入备注..." MinHeight="40"
|
||||
<TextBlock Text="{l10n:Localize Notes}"/>
|
||||
<TextBox Text="{Binding NotesText}" PlaceholderText="{l10n:Localize NotesPlaceholder}" MinHeight="40"
|
||||
AcceptsReturn="True"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 未选择文件 -->
|
||||
<TextBlock Grid.Row="1" Text="选择文件查看元数据"
|
||||
<TextBlock Grid.Row="1" Text="{l10n:Localize SelectFileToViewMetadata}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"
|
||||
IsVisible="{Binding !HasFile}"/>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:GUI.ViewModels"
|
||||
xmlns:converters="using:GUI.Converters"
|
||||
xmlns:l10n="using:GUI.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
@@ -21,21 +22,25 @@
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="{Binding StatusText}" FontWeight="SemiBold" FontSize="13" TextWrapping="Wrap"/>
|
||||
<ProgressBar Minimum="0" Maximum="100" Value="{Binding Progress}" Height="8"/>
|
||||
<TextBlock FontSize="11" Foreground="{DynamicResource SystemBaseMediumColorBrush}">
|
||||
<Run Text="已处理: "/>
|
||||
<Run Text="{Binding ProcessedFiles}"/>
|
||||
<Run Text=" / "/>
|
||||
<Run Text="{Binding TotalFiles}"/>
|
||||
</TextBlock>
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBlock Text="{Binding AddedFiles, StringFormat='新增: {0}'}" FontSize="11"
|
||||
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||
<TextBlock Text="{l10n:Localize Processed}" FontSize="11"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
<TextBlock Text="{Binding SkippedFiles, StringFormat='跳过: {0}'}" FontSize="11"
|
||||
<TextBlock Text="{Binding ProcessedFiles}" FontSize="11"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
<TextBlock Text=" / " FontSize="11"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
<TextBlock Text="{Binding TotalFiles}" FontSize="11"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBlock Text="{Binding AddedText}" FontSize="11"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
<TextBlock Text="{Binding SkippedText}" FontSize="11"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding CurrentFile, Converter={StaticResource MiddleTruncate}, ConverterParameter=60}"
|
||||
FontSize="11"/>
|
||||
<Button Content="关闭" Command="{Binding DismissCommand}"
|
||||
<Button Content="{l10n:Localize Close}" Command="{Binding DismissCommand}"
|
||||
IsVisible="{Binding HasError}" HorizontalAlignment="Right"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
xmlns:vm="using:GUI.ViewModels"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:l10n="using:GUI.Converters"
|
||||
mc:Ignorable="d"
|
||||
x:Class="GUI.Views.TagTreeView"
|
||||
x:DataType="vm:TagTreeViewModel">
|
||||
@@ -12,18 +13,18 @@
|
||||
|
||||
<Grid RowDefinitions="Auto,*">
|
||||
<StackPanel Orientation="Horizontal" Margin="8,8,8,4" Spacing="4">
|
||||
<TextBlock Text="浏览" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
|
||||
<Button Content="⟳" Command="{Binding LoadDataCommand}" ToolTip.Tip="刷新"
|
||||
<TextBlock Text="{l10n:Localize Browse}" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
|
||||
<Button Content="⟳" Command="{Binding LoadDataCommand}" ToolTip.Tip="{l10n:Localize Refresh}"
|
||||
Width="28" Height="28" HorizontalAlignment="Right"/>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<StackPanel Spacing="2" Margin="8,0,8,8">
|
||||
<Button Content="全部文件" Command="{Binding ClearFilterCommand}"
|
||||
<Button Content="{l10n:Localize AllFiles}" Command="{Binding ClearFilterCommand}"
|
||||
HorizontalAlignment="Stretch" HorizontalContentAlignment="Left"
|
||||
Height="28" Margin="0,0,0,4"/>
|
||||
|
||||
<TextBlock Text="分类" FontWeight="SemiBold" Margin="0,8,0,2" FontSize="12"
|
||||
<TextBlock Text="{l10n:Localize Category}" FontWeight="SemiBold" Margin="0,8,0,2" FontSize="12"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
|
||||
<ListBox ItemsSource="{Binding CategoryNodes}"
|
||||
@@ -36,7 +37,7 @@
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<TextBlock Text="标签" FontWeight="SemiBold" Margin="0,8,0,2" FontSize="12"
|
||||
<TextBlock Text="{l10n:Localize Tags}" FontWeight="SemiBold" Margin="0,8,0,2" FontSize="12"
|
||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||
|
||||
<ListBox ItemsSource="{Binding KeywordNodes}"
|
||||
|
||||
@@ -34,9 +34,11 @@
|
||||
<TestId>xUnit::24F92458-FB39-44BE-A32F-41275183AF1B::net10.0::Core.Tests.DatabaseTests</TestId>
|
||||
</TestAncestor>
|
||||
</SessionState></s:String>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=GUI_002FAssets_002FLocale/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=GUI_002FAssets_002FLocale/@EntryIndexedValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=GUI_002FAssets_002FLocale/@EntryIndexRemoved">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=GUI_002FLocale/@EntryIndexedValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=GUI_002FLocale/@EntryIndexRemoved">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=GUI_002FResources1/@EntryIndexedValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=GUI_002FResources1/@EntryIndexRemoved">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/Initialized/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/ShowComments/@EntryValue">False</s:Boolean></wpf:ResourceDictionary>
|
||||
Reference in New Issue
Block a user