Compare commits
2 Commits
5ab48c4db6
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b3bf46097 | |||
| 6e664a7762 |
@@ -17,12 +17,12 @@
|
|||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
数据库: %APPDATA%\Local\OCES\Resonance\Databases
|
数据库: %APPDATA%\Local\OCES\Resonance\Databases
|
||||||
偏好设置: %APPDATA%\Roaming\OCES\Resonance\
|
偏好设置: %APPDATA%\Roaming\OCES\Resonance\preferences.json
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
数据库: XDG_DATA_HOME
|
数据库: XDG_DATA_HOME
|
||||||
偏好设置: XDG_CONFIG_HOME
|
偏好设置: XDG_CONFIG_HOME/preferences.json
|
||||||
缓存数据: XDG_CACHE_HOME
|
缓存数据: XDG_CACHE_HOME
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
@@ -30,7 +30,8 @@
|
|||||||
- [ ] 扫描时如果报错,报错信息可能会填满整个窗口,导致Overlay无法关闭。
|
- [ ] 扫描时如果报错,报错信息可能会填满整个窗口,导致Overlay无法关闭。
|
||||||
- [ ] 读取时如果报错,没有任何警告,会静默报错。需要有一个界面右下方的toast,或是发送系统通知告知用户遇到了错误。
|
- [ ] 读取时如果报错,没有任何警告,会静默报错。需要有一个界面右下方的toast,或是发送系统通知告知用户遇到了错误。
|
||||||
- [x] 指针化Artwork字段。如果artwork的md5
|
- [x] 指针化Artwork字段。如果artwork的md5
|
||||||
- [ ] 本地化框架
|
- [x] 本地化框架
|
||||||
|
- [ ] 偏好设置/控制面板
|
||||||
|
|
||||||
## 技术栈
|
## 技术栈
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -3,22 +3,22 @@
|
|||||||
x:Class="GUI.App"
|
x:Class="GUI.App"
|
||||||
Name="Resonance"
|
Name="Resonance"
|
||||||
xmlns:local="using:GUI"
|
xmlns:local="using:GUI"
|
||||||
|
xmlns:l10n="using:GUI.Converters"
|
||||||
RequestedThemeVariant="Default">
|
RequestedThemeVariant="Default">
|
||||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||||
|
|
||||||
<Application.DataTemplates>
|
<Application.DataTemplates>
|
||||||
<local:ViewLocator/>
|
<local:ViewLocator/>
|
||||||
</Application.DataTemplates>
|
</Application.DataTemplates>
|
||||||
|
|
||||||
|
|
||||||
<NativeMenu.Menu>
|
<NativeMenu.Menu>
|
||||||
<NativeMenu>
|
<NativeMenu>
|
||||||
<NativeMenuItem Header="关于…" />
|
<NativeMenuItem Header="{l10n:Localize About}" />
|
||||||
<NativeMenuItem Header="偏好设置…"/>
|
<NativeMenuItem Header="{l10n:Localize Preferences}"/>
|
||||||
</NativeMenu>
|
</NativeMenu>
|
||||||
</NativeMenu.Menu>
|
</NativeMenu.Menu>
|
||||||
|
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<FluentTheme />
|
<FluentTheme />
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
</Application>
|
</Application>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.Data.Core;
|
|
||||||
using Avalonia.Data.Core.Plugins;
|
|
||||||
using System.Linq;
|
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
|
using GUI.Services;
|
||||||
using GUI.ViewModels;
|
using GUI.ViewModels;
|
||||||
using GUI.Views;
|
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 System.Globalization;
|
||||||
using Avalonia.Data.Converters;
|
using Avalonia.Data.Converters;
|
||||||
|
using GUI.Services;
|
||||||
|
|
||||||
namespace GUI.Converters;
|
namespace GUI.Converters;
|
||||||
|
|
||||||
@@ -36,8 +37,8 @@ public class ChannelsFormatConverter : IValueConverter
|
|||||||
{
|
{
|
||||||
return value switch
|
return value switch
|
||||||
{
|
{
|
||||||
int ch when ch == 1 => "Mono",
|
int ch when ch == 1 => LocalizationService.Instance["ChannelsMono"],
|
||||||
int ch when ch == 2 => "Stereo",
|
int ch when ch == 2 => LocalizationService.Instance["ChannelsStereo"],
|
||||||
null => "--",
|
null => "--",
|
||||||
int ch => $"{ch}ch",
|
int ch => $"{ch}ch",
|
||||||
_ => "--",
|
_ => "--",
|
||||||
|
|||||||
+6
-11
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Models\"/>
|
<Folder Include="Models\"/>
|
||||||
|
<Folder Include="Resources\"/>
|
||||||
<AvaloniaResource Include="Assets\**"/>
|
<AvaloniaResource Include="Assets\**"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -32,17 +33,11 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Update="Assets\Locale.resx">
|
<EmbeddedResource Include="Resources\LocaleZh.xml">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<LogicalName>GUI.LocaleZh.xml</LogicalName>
|
||||||
<LastGenOutput>Locale.Designer.cs</LastGenOutput>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="Resources\LocaleEn.xml">
|
||||||
|
<LogicalName>GUI.LocaleEn.xml</LogicalName>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Update="Assets\Locale.Designer.cs">
|
|
||||||
<DesignTime>True</DesignTime>
|
|
||||||
<AutoGen>True</AutoGen>
|
|
||||||
<DependentUpon>Locale.resx</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</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 AudioFileMeta? _currentFile;
|
||||||
[ObservableProperty] private bool _hasFile;
|
[ObservableProperty] private bool _hasFile;
|
||||||
[ObservableProperty] private string _currentFileName = "未选择文件";
|
[ObservableProperty] private string _currentFileName = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty] private bool _isPlaying;
|
[ObservableProperty] private bool _isPlaying;
|
||||||
[ObservableProperty] private double _currentPosition;
|
[ObservableProperty] private double _currentPosition;
|
||||||
@@ -21,6 +21,7 @@ public partial class PlayerBarViewModel : ViewModelBase
|
|||||||
public PlayerBarViewModel(SyncChannel channel)
|
public PlayerBarViewModel(SyncChannel channel)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
|
CurrentFileName = Loc["NoFileSelected"];
|
||||||
|
|
||||||
_channel.PropertyChanged += (_, e) =>
|
_channel.PropertyChanged += (_, e) =>
|
||||||
{
|
{
|
||||||
@@ -39,7 +40,7 @@ public partial class PlayerBarViewModel : ViewModelBase
|
|||||||
|
|
||||||
if (file == null)
|
if (file == null)
|
||||||
{
|
{
|
||||||
CurrentFileName = "未选择文件";
|
CurrentFileName = Loc["NoFileSelected"];
|
||||||
Duration = 0;
|
Duration = 0;
|
||||||
HasFile = false;
|
HasFile = false;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ public partial class ScanProgressViewModel : ViewModelBase
|
|||||||
[ObservableProperty] private double _progress;
|
[ObservableProperty] private double _progress;
|
||||||
[ObservableProperty] private string _currentFile = string.Empty;
|
[ObservableProperty] private string _currentFile = string.Empty;
|
||||||
|
|
||||||
|
public string AddedText => Loc.GetFormatted("AddedFormat", AddedFiles);
|
||||||
|
public string SkippedText => Loc.GetFormatted("SkippedFormat", SkippedFiles);
|
||||||
|
|
||||||
public event Action? ScanCompleted;
|
public event Action? ScanCompleted;
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
@@ -22,6 +25,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(directory)) return;
|
if (string.IsNullOrWhiteSpace(directory)) return;
|
||||||
|
|
||||||
|
StatusText = Loc["ScanPreparing"];
|
||||||
Task.Run(() => ScanDirectoryInternal(directory));
|
Task.Run(() => ScanDirectoryInternal(directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +36,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
IsScanning = false;
|
IsScanning = false;
|
||||||
HasError = false;
|
HasError = false;
|
||||||
StatusText = "准备扫描...";
|
StatusText = Loc["ScanPreparing"];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ScanDirectoryInternal(string directory)
|
private void ScanDirectoryInternal(string directory)
|
||||||
@@ -41,7 +45,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
HasError = false;
|
HasError = false;
|
||||||
IsScanning = true;
|
IsScanning = true;
|
||||||
StatusText = "正在发现文件...";
|
StatusText = Loc["ScanDiscovering"];
|
||||||
|
|
||||||
Database.InitializeDatabase();
|
Database.InitializeDatabase();
|
||||||
|
|
||||||
@@ -56,7 +60,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
|||||||
SkippedFiles = 0;
|
SkippedFiles = 0;
|
||||||
Progress = 0;
|
Progress = 0;
|
||||||
|
|
||||||
StatusText = $"发现 {TotalFiles} 个文件,正在解析元数据...";
|
StatusText = Loc.GetFormatted("ScanParsing", TotalFiles);
|
||||||
|
|
||||||
foreach (string filePath in files)
|
foreach (string filePath in files)
|
||||||
{
|
{
|
||||||
@@ -100,12 +104,12 @@ public partial class ScanProgressViewModel : ViewModelBase
|
|||||||
UpdateProgress();
|
UpdateProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusText = $"扫描完成:新增 {AddedFiles},跳过 {SkippedFiles}";
|
StatusText = Loc.GetFormatted("ScanComplete", AddedFiles, SkippedFiles);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
HasError = true;
|
HasError = true;
|
||||||
StatusText = $"扫描出错:{ex.Message}";
|
StatusText = Loc.GetFormatted("ScanError", ex.Message);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -119,4 +123,7 @@ public partial class ScanProgressViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
Progress = TotalFiles > 0 ? (double)ProcessedFiles / TotalFiles * 100 : 0;
|
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 CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using GUI.Services;
|
||||||
|
|
||||||
namespace GUI.ViewModels;
|
namespace GUI.ViewModels;
|
||||||
|
|
||||||
public abstract class ViewModelBase : ObservableObject
|
public abstract class ViewModelBase : ObservableObject
|
||||||
{
|
{
|
||||||
|
public LocalizationService Loc => LocalizationService.Instance;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:converters="using:GUI.Converters"
|
xmlns:converters="using:GUI.Converters"
|
||||||
|
xmlns:l10n="using:GUI.Converters"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:Class="GUI.Views.FileListView"
|
x:Class="GUI.Views.FileListView"
|
||||||
x:DataType="vm:FileListViewModel">
|
x:DataType="vm:FileListViewModel">
|
||||||
@@ -25,22 +26,22 @@
|
|||||||
BorderThickness="0,0,0,1">
|
BorderThickness="0,0,0,1">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<Grid ColumnDefinitions="*,Auto" Margin="0,0,0,4">
|
<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}"
|
<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>
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||||
<ComboBox ItemsSource="{Binding SortOptions}" SelectedItem="{Binding SelectedSortBy}"
|
<ComboBox ItemsSource="{Binding SortOptions}" SelectedItem="{Binding SelectedSortBy}"
|
||||||
Width="140" ToolTip.Tip="排序字段"/>
|
Width="140" ToolTip.Tip="{l10n:Localize SortByField}"/>
|
||||||
<Button Content="重置筛选" Command="{Binding ResetFiltersCommand}"
|
<Button Content="{l10n:Localize ResetFilters}" Command="{Binding ResetFiltersCommand}"
|
||||||
Height="28" Margin="8,0,0,0"/>
|
Height="28" Margin="8,0,0,0"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Grid Grid.Row="1">
|
<Grid Grid.Row="1">
|
||||||
<TextBlock Text="暂无文件,请先扫描目录添加音频文件。"
|
<TextBlock Text="{l10n:Localize NoFilesHint}"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"
|
Foreground="{DynamicResource SystemBaseMediumColorBrush}"
|
||||||
FontSize="14"
|
FontSize="14"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
xmlns:views="using:GUI.Views"
|
xmlns:views="using:GUI.Views"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:l10n="using:GUI.Converters"
|
||||||
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
mc:Ignorable="d" d:DesignWidth="1200" d:DesignHeight="800"
|
||||||
x:Class="GUI.Views.MainWindow"
|
x:Class="GUI.Views.MainWindow"
|
||||||
x:DataType="vm:MainWindowViewModel"
|
x:DataType="vm:MainWindowViewModel"
|
||||||
@@ -19,10 +20,10 @@
|
|||||||
|
|
||||||
<NativeMenu.Menu>
|
<NativeMenu.Menu>
|
||||||
<NativeMenu>
|
<NativeMenu>
|
||||||
<NativeMenuItem Header="Database" IsVisible="True">
|
<NativeMenuItem Header="{l10n:Localize Database}" IsVisible="True">
|
||||||
<NativeMenu>
|
<NativeMenu>
|
||||||
<NativeMenuItem Header="Delete Unused Artworks" Command="{Binding CleanOrphanedArtworkCommand}"></NativeMenuItem>
|
<NativeMenuItem Header="{l10n:Localize MenuDeleteOrphanArtwork}" Command="{Binding CleanOrphanedArtworkCommand}"></NativeMenuItem>
|
||||||
<NativeMenuItem Header="Open database file in Finder..."/>
|
<NativeMenuItem Header="{l10n:Localize MenuOpenDatabaseFolder}"/>
|
||||||
</NativeMenu>
|
</NativeMenu>
|
||||||
</NativeMenuItem>
|
</NativeMenuItem>
|
||||||
</NativeMenu>
|
</NativeMenu>
|
||||||
@@ -34,8 +35,8 @@
|
|||||||
<Border Grid.Row="0" Padding="6,4" BorderBrush="{DynamicResource SystemBaseLowColorBrush}"
|
<Border Grid.Row="0" Padding="6,4" BorderBrush="{DynamicResource SystemBaseLowColorBrush}"
|
||||||
BorderThickness="0,0,0,1" Background="{DynamicResource SystemAltMediumColorBrush}">
|
BorderThickness="0,0,0,1" Background="{DynamicResource SystemAltMediumColorBrush}">
|
||||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||||
<Button Content="扫描目录" x:Name="ScanButton" Height="28" Padding="8,0"/>
|
<Button Content="{l10n:Localize ScanDirectory}" x:Name="ScanButton" Height="28" Padding="8,0"/>
|
||||||
<Button Content="偏好设置" Height="28" Padding="8,0" IsEnabled="False"/>
|
<Button Content="{l10n:Localize Preferences}" Height="28" Padding="8,0" IsEnabled="False"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
|
using GUI.Services;
|
||||||
using GUI.ViewModels;
|
using GUI.ViewModels;
|
||||||
|
|
||||||
namespace GUI.Views;
|
namespace GUI.Views;
|
||||||
@@ -19,7 +20,7 @@ public partial class MainWindow : Window
|
|||||||
|
|
||||||
IReadOnlyList<IStorageFolder> folders = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
IReadOnlyList<IStorageFolder> folders = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||||
{
|
{
|
||||||
Title = "选择音频目录",
|
Title = LocalizationService.Instance["SelectAudioDirectory"],
|
||||||
AllowMultiple = false,
|
AllowMultiple = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
xmlns:vm="using:GUI.ViewModels"
|
xmlns:vm="using:GUI.ViewModels"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:converters="using:GUI.Converters"
|
xmlns:l10n="using:GUI.Converters"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:Class="GUI.Views.MetadataPanelView"
|
x:Class="GUI.Views.MetadataPanelView"
|
||||||
x:DataType="vm:MetadataPanelViewModel">
|
x:DataType="vm:MetadataPanelViewModel">
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<Grid RowDefinitions="Auto,*" Margin="8">
|
<Grid RowDefinitions="Auto,*" Margin="8">
|
||||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="4" Margin="0,0,0,8">
|
<StackPanel Grid.Row="0" Orientation="Horizontal" Spacing="4" Margin="0,0,0,8">
|
||||||
<TextBlock Text="元数据" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
|
<TextBlock Text="{l10n:Localize Metadata}" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
|
||||||
<Button Content="保存" Command="{Binding SaveCommand}"
|
<Button Content="{l10n:Localize Save}" Command="{Binding SaveCommand}"
|
||||||
IsEnabled="{Binding IsDirty}"
|
IsEnabled="{Binding IsDirty}"
|
||||||
Height="28" Width="60" HorizontalAlignment="Right"/>
|
Height="28" Width="60" HorizontalAlignment="Right"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -27,14 +27,29 @@
|
|||||||
<Border Padding="6" BorderBrush="{DynamicResource SystemBaseLowColorBrush}" BorderThickness="1"
|
<Border Padding="6" BorderBrush="{DynamicResource SystemBaseLowColorBrush}" BorderThickness="1"
|
||||||
CornerRadius="4">
|
CornerRadius="4">
|
||||||
<StackPanel Spacing="4">
|
<StackPanel Spacing="4">
|
||||||
<TextBlock Text="基础信息" FontWeight="SemiBold" FontSize="12"
|
<TextBlock Text="{l10n:Localize BasicInfo}" FontWeight="SemiBold" FontSize="12"
|
||||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||||
<TextBlock Text="{Binding CurrentFile.Filename}" FontSize="13" FontWeight="Bold"/>
|
<TextBlock Text="{Binding CurrentFile.Filename}" FontSize="13" FontWeight="Bold"/>
|
||||||
<TextBlock Text="{Binding CurrentFile.Duration, StringFormat='时长: {0:F2}s'}"/>
|
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||||
<TextBlock Text="{Binding CurrentFile.SampleRate, StringFormat='采样率: {0} Hz'}"/>
|
<TextBlock Text="{l10n:Localize DurationLabel}"/>
|
||||||
<TextBlock Text="{Binding CurrentFile.BitDepth, StringFormat='位深: {0} bit'}"/>
|
<TextBlock Text="{Binding CurrentFile.Duration, StringFormat='{}{0:F2}s'}"/>
|
||||||
<TextBlock Text="{Binding CurrentFile.Channels, StringFormat='声道: {0}'}"/>
|
</StackPanel>
|
||||||
<TextBlock Text="{Binding CurrentFile.Type, StringFormat='格式: {0}'}"/>
|
<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>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
@@ -42,7 +57,7 @@
|
|||||||
<Border Padding="6" BorderBrush="{DynamicResource SystemBaseLowColorBrush}" BorderThickness="1"
|
<Border Padding="6" BorderBrush="{DynamicResource SystemBaseLowColorBrush}" BorderThickness="1"
|
||||||
CornerRadius="4">
|
CornerRadius="4">
|
||||||
<StackPanel Spacing="4">
|
<StackPanel Spacing="4">
|
||||||
<TextBlock Text="评分" FontWeight="SemiBold" FontSize="12"
|
<TextBlock Text="{l10n:Localize Rating}" FontWeight="SemiBold" FontSize="12"
|
||||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||||
<StackPanel Orientation="Horizontal" Spacing="2">
|
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||||
<Button Content="☆" Command="{Binding SetRatingCommand}" CommandParameter="0"
|
<Button Content="☆" Command="{Binding SetRatingCommand}" CommandParameter="0"
|
||||||
@@ -65,29 +80,29 @@
|
|||||||
<Border Padding="6" BorderBrush="{DynamicResource SystemBaseLowColorBrush}" BorderThickness="1"
|
<Border Padding="6" BorderBrush="{DynamicResource SystemBaseLowColorBrush}" BorderThickness="1"
|
||||||
CornerRadius="4">
|
CornerRadius="4">
|
||||||
<StackPanel Spacing="6">
|
<StackPanel Spacing="6">
|
||||||
<TextBlock Text="标签与描述" FontWeight="SemiBold" FontSize="12"
|
<TextBlock Text="{l10n:Localize TagsAndDescription}" FontWeight="SemiBold" FontSize="12"
|
||||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||||
|
|
||||||
<TextBlock Text="分类"/>
|
<TextBlock Text="{l10n:Localize Category}"/>
|
||||||
<TextBox Text="{Binding CategoryText}" PlaceholderText="输入分类..."/>
|
<TextBox Text="{Binding CategoryText}" PlaceholderText="{l10n:Localize CategoryPlaceholder}"/>
|
||||||
|
|
||||||
<TextBlock Text="关键词(逗号分隔)"/>
|
<TextBlock Text="{l10n:Localize KeywordsCommaSeparated}"/>
|
||||||
<TextBox Text="{Binding KeywordsText}" PlaceholderText="输入关键词..." MinHeight="40"
|
<TextBox Text="{Binding KeywordsText}" PlaceholderText="{l10n:Localize KeywordsPlaceholder}" MinHeight="40"
|
||||||
AcceptsReturn="True"/>
|
AcceptsReturn="True"/>
|
||||||
|
|
||||||
<TextBlock Text="描述"/>
|
<TextBlock Text="{l10n:Localize Description}"/>
|
||||||
<TextBox Text="{Binding DescriptionText}" PlaceholderText="输入描述..." MinHeight="40"
|
<TextBox Text="{Binding DescriptionText}" PlaceholderText="{l10n:Localize DescriptionPlaceholder}" MinHeight="40"
|
||||||
AcceptsReturn="True"/>
|
AcceptsReturn="True"/>
|
||||||
|
|
||||||
<TextBlock Text="备注"/>
|
<TextBlock Text="{l10n:Localize Notes}"/>
|
||||||
<TextBox Text="{Binding NotesText}" PlaceholderText="输入备注..." MinHeight="40"
|
<TextBox Text="{Binding NotesText}" PlaceholderText="{l10n:Localize NotesPlaceholder}" MinHeight="40"
|
||||||
AcceptsReturn="True"/>
|
AcceptsReturn="True"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- 未选择文件 -->
|
<!-- 未选择文件 -->
|
||||||
<TextBlock Grid.Row="1" Text="选择文件查看元数据"
|
<TextBlock Grid.Row="1" Text="{l10n:Localize SelectFileToViewMetadata}"
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"
|
Foreground="{DynamicResource SystemBaseMediumColorBrush}"
|
||||||
IsVisible="{Binding !HasFile}"/>
|
IsVisible="{Binding !HasFile}"/>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:vm="using:GUI.ViewModels"
|
xmlns:vm="using:GUI.ViewModels"
|
||||||
xmlns:converters="using:GUI.Converters"
|
xmlns:converters="using:GUI.Converters"
|
||||||
|
xmlns:l10n="using:GUI.Converters"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
@@ -21,21 +22,25 @@
|
|||||||
<StackPanel Spacing="8">
|
<StackPanel Spacing="8">
|
||||||
<TextBlock Text="{Binding StatusText}" FontWeight="SemiBold" FontSize="13" TextWrapping="Wrap"/>
|
<TextBlock Text="{Binding StatusText}" FontWeight="SemiBold" FontSize="13" TextWrapping="Wrap"/>
|
||||||
<ProgressBar Minimum="0" Maximum="100" Value="{Binding Progress}" Height="8"/>
|
<ProgressBar Minimum="0" Maximum="100" Value="{Binding Progress}" Height="8"/>
|
||||||
<TextBlock FontSize="11" Foreground="{DynamicResource SystemBaseMediumColorBrush}">
|
<StackPanel Orientation="Horizontal" Spacing="2">
|
||||||
<Run Text="已处理: "/>
|
<TextBlock Text="{l10n:Localize Processed}" FontSize="11"
|
||||||
<Run Text="{Binding ProcessedFiles}"/>
|
|
||||||
<Run Text=" / "/>
|
|
||||||
<Run Text="{Binding TotalFiles}"/>
|
|
||||||
</TextBlock>
|
|
||||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
|
||||||
<TextBlock Text="{Binding AddedFiles, StringFormat='新增: {0}'}" FontSize="11"
|
|
||||||
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
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}"/>
|
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<TextBlock Text="{Binding CurrentFile, Converter={StaticResource MiddleTruncate}, ConverterParameter=60}"
|
<TextBlock Text="{Binding CurrentFile, Converter={StaticResource MiddleTruncate}, ConverterParameter=60}"
|
||||||
FontSize="11"/>
|
FontSize="11"/>
|
||||||
<Button Content="关闭" Command="{Binding DismissCommand}"
|
<Button Content="{l10n:Localize Close}" Command="{Binding DismissCommand}"
|
||||||
IsVisible="{Binding HasError}" HorizontalAlignment="Right"/>
|
IsVisible="{Binding HasError}" HorizontalAlignment="Right"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:vm="using:GUI.ViewModels"
|
xmlns:vm="using:GUI.ViewModels"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:l10n="using:GUI.Converters"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:Class="GUI.Views.TagTreeView"
|
x:Class="GUI.Views.TagTreeView"
|
||||||
x:DataType="vm:TagTreeViewModel">
|
x:DataType="vm:TagTreeViewModel">
|
||||||
@@ -12,18 +13,18 @@
|
|||||||
|
|
||||||
<Grid RowDefinitions="Auto,*">
|
<Grid RowDefinitions="Auto,*">
|
||||||
<StackPanel Orientation="Horizontal" Margin="8,8,8,4" Spacing="4">
|
<StackPanel Orientation="Horizontal" Margin="8,8,8,4" Spacing="4">
|
||||||
<TextBlock Text="浏览" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
|
<TextBlock Text="{l10n:Localize Browse}" FontWeight="Bold" FontSize="14" VerticalAlignment="Center"/>
|
||||||
<Button Content="⟳" Command="{Binding LoadDataCommand}" ToolTip.Tip="刷新"
|
<Button Content="⟳" Command="{Binding LoadDataCommand}" ToolTip.Tip="{l10n:Localize Refresh}"
|
||||||
Width="28" Height="28" HorizontalAlignment="Right"/>
|
Width="28" Height="28" HorizontalAlignment="Right"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<ScrollViewer Grid.Row="1">
|
<ScrollViewer Grid.Row="1">
|
||||||
<StackPanel Spacing="2" Margin="8,0,8,8">
|
<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"
|
HorizontalAlignment="Stretch" HorizontalContentAlignment="Left"
|
||||||
Height="28" Margin="0,0,0,4"/>
|
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}"/>
|
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||||
|
|
||||||
<ListBox ItemsSource="{Binding CategoryNodes}"
|
<ListBox ItemsSource="{Binding CategoryNodes}"
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</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}"/>
|
Foreground="{DynamicResource SystemBaseMediumColorBrush}"/>
|
||||||
|
|
||||||
<ListBox ItemsSource="{Binding KeywordNodes}"
|
<ListBox ItemsSource="{Binding KeywordNodes}"
|
||||||
|
|||||||
@@ -34,9 +34,11 @@
|
|||||||
<TestId>xUnit::24F92458-FB39-44BE-A32F-41275183AF1B::net10.0::Core.Tests.DatabaseTests</TestId>
|
<TestId>xUnit::24F92458-FB39-44BE-A32F-41275183AF1B::net10.0::Core.Tests.DatabaseTests</TestId>
|
||||||
</TestAncestor>
|
</TestAncestor>
|
||||||
</SessionState></s:String>
|
</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/@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_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/@EntryIndexedValue">False</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/ResxEditorPersonal/CheckedGroups/=GUI_002FResources1/@EntryIndexRemoved">True</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