mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 02:44:56 +08:00
feat: romaji plugin (without dict)
This commit is contained in:
20
.github/workflows/plugin-registry-check.yml
vendored
Normal file
20
.github/workflows/plugin-registry-check.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: Plugin Registry Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'Community/plugins-registry.json'
|
||||
|
||||
jobs:
|
||||
check-collision:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Run Hash Collision Check
|
||||
run: node Community/scripts/check-hash-collision.js
|
||||
@@ -5,12 +5,8 @@ using System.Text;
|
||||
|
||||
namespace BetterLyrics.Core.Interfaces
|
||||
{
|
||||
public interface ILyricsProvider
|
||||
public interface ILyricsSearchPlugin : IPlugin
|
||||
{
|
||||
string Id { get; }
|
||||
string Name { get; }
|
||||
string Author { get; }
|
||||
|
||||
Task<LyricsSearchResult> GetLyricsAsync(string title, string artist, string album, double duration);
|
||||
}
|
||||
}
|
||||
11
BetterLyrics.Core/Interfaces/ILyricsTransliterationPlugin.cs
Normal file
11
BetterLyrics.Core/Interfaces/ILyricsTransliterationPlugin.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.Core.Interfaces
|
||||
{
|
||||
public interface ILyricsTransliterationPlugin : IPlugin
|
||||
{
|
||||
Task<string?> GetTransliterationAsync(string text, string targetLangCode);
|
||||
}
|
||||
}
|
||||
17
BetterLyrics.Core/Interfaces/IPlugin.cs
Normal file
17
BetterLyrics.Core/Interfaces/IPlugin.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using BetterLyrics.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.Core.Interfaces
|
||||
{
|
||||
public interface IPlugin
|
||||
{
|
||||
string Id { get; }
|
||||
string Name { get; }
|
||||
string Description { get; }
|
||||
string Author { get; }
|
||||
|
||||
void Initialize();
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,16 @@ using BetterLyrics.Core.Models;
|
||||
|
||||
namespace BetterLyrics.Plugins.Demo
|
||||
{
|
||||
public class DemoLyricsProvider : ILyricsProvider
|
||||
public class DemoLyricsProvider : ILyricsSearchPlugin
|
||||
{
|
||||
public string Id => "f7acc86b-6e3d-42c3-a9a9-8c05c5339412";
|
||||
public string Name => "Demo Plugin";
|
||||
public string Name => "Plugin name";
|
||||
public string Author => "jayfunc";
|
||||
|
||||
public string Description => "Plugin description";
|
||||
|
||||
public void Initialize() { }
|
||||
|
||||
public async Task<LyricsSearchResult> GetLyricsAsync(string title, string artist, string album, double duration)
|
||||
{
|
||||
await Task.Delay(300);
|
||||
|
||||
101
BetterLyrics.Plugins.Romaji/BetterLyrics.Plugins.Romaji.csproj
Normal file
101
BetterLyrics.Plugins.Romaji/BetterLyrics.Plugins.Romaji.csproj
Normal file
@@ -0,0 +1,101 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="customizeDict.txt">
|
||||
<PackagePath>contentFiles\any\any\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\AUTHORS">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\BSD">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\ChangeLog">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\char.bin">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\COPYING">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\dicrc">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\GPL">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\INSTALL">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\LGPL">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\matrix.bin">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\sys.dic">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\unidic-mecab.pdf">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
<Content Include="unidic\unk.dic">
|
||||
<PackagePath>contentFiles\any\any\unidic\</PackagePath>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<PackageCopyToOutput>true</PackageCopyToOutput>
|
||||
<Pack>true</Pack>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BetterLyrics.Core\BetterLyrics.Core.csproj" />
|
||||
<ProjectReference Include="..\RomajiConverter.Core\RomajiConverter.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
34
BetterLyrics.Plugins.Romaji/RomajiPlugin.cs
Normal file
34
BetterLyrics.Plugins.Romaji/RomajiPlugin.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using BetterLyrics.Core.Interfaces;
|
||||
using RomajiConverter.Core.Helpers;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BetterLyrics.Plugins.Romaji
|
||||
{
|
||||
public class RomajiPlugin : ILyricsTransliterationPlugin
|
||||
{
|
||||
public string Id => "jayfunc.romaji";
|
||||
|
||||
public string Name => "Romaji";
|
||||
|
||||
public string Description => "Convert Japanese lyrics to Romaji transliteration.";
|
||||
|
||||
public string Author => "jayfunc";
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
string? pluginPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
RomajiHelper.Init(pluginPath);
|
||||
}
|
||||
|
||||
public Task<string?> GetTransliterationAsync(string text, string targetLangCode)
|
||||
{
|
||||
string? result = null;
|
||||
if (targetLangCode == "ja-latin")
|
||||
{
|
||||
var lines = RomajiHelper.ToRomaji(text);
|
||||
result = string.Join("\r\n", lines.Select(p => string.Join(" ", p.Units.Select(q => q.Romaji))));
|
||||
}
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
1
BetterLyrics.Plugins.Romaji/customizeDict.txt
Normal file
1
BetterLyrics.Plugins.Romaji/customizeDict.txt
Normal file
@@ -0,0 +1 @@
|
||||
私 わたし
|
||||
@@ -155,6 +155,10 @@ namespace BetterLyrics.WinUI3
|
||||
}
|
||||
fileSystemService.StartAllFolderTimers();
|
||||
|
||||
// Ensure plugins
|
||||
var pluginService = Ioc.Default.GetRequiredService<IPluginService>();
|
||||
pluginService.LoadPlugins();
|
||||
|
||||
// Init system tray
|
||||
m_window = WindowHook.OpenOrShowWindow<SystemTrayWindow>();
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
<None Remove="Controls\PatronControl.xaml" />
|
||||
<None Remove="Controls\PlaybackSettingsControl.xaml" />
|
||||
<None Remove="Controls\PlayQueue.xaml" />
|
||||
<None Remove="Controls\PluginManagerControl.xaml" />
|
||||
<None Remove="Controls\PropertyRow.xaml" />
|
||||
<None Remove="Controls\RemoteServerConfigControl.xaml" />
|
||||
<None Remove="Controls\ShortcutTextBox.xaml" />
|
||||
@@ -271,6 +272,11 @@
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\PluginManagerControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\PatronControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.PluginManagerControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:dev="using:DevWinUI"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="36,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<TextBlock x:Uid="PluginManagerControlTitle" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
</Grid>
|
||||
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
|
||||
ItemsSource="{x:Bind Plugins, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:PluginDisplayModel">
|
||||
<dev:SettingsExpander
|
||||
Description="{x:Bind Description}"
|
||||
Header="{x:Bind Name}"
|
||||
HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}">
|
||||
<TextBlock Text="{x:Bind Version}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||
<StackPanel Spacing="6">
|
||||
<local:PropertyRow Header="Author" Value="{x:Bind Author}" />
|
||||
<local:PropertyRow Header="ID" Value="{x:Bind Id}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard>
|
||||
<Button
|
||||
x:Uid="PluginManagerControlUninstall"
|
||||
Click="OnUninstallClick"
|
||||
Tag="{x:Bind Plugin}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Visibility="{x:Bind IsListEmpty, Mode=OneWay}">
|
||||
<FontIcon
|
||||
FontSize="48"
|
||||
Glyph=""
|
||||
Opacity="0.3" />
|
||||
<TextBlock
|
||||
x:Uid="PluginManagerControlNoPluginsInstalled"
|
||||
Margin="0,12,0,0"
|
||||
Opacity="0.5" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="2" Margin="0,6,0,20">
|
||||
<Button
|
||||
x:Uid="PluginManagerControlInstall"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Click="OnInstallPluginClick"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,151 @@
|
||||
using BetterLyrics.Core.Interfaces;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Services.PluginService;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class PluginManagerControl : UserControl
|
||||
{
|
||||
public ObservableCollection<PluginDisplayModel> Plugins { get; } = new();
|
||||
|
||||
public Visibility IsListEmpty => Plugins.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
private readonly IPluginService _pluginService;
|
||||
|
||||
public PluginManagerControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
_pluginService = Ioc.Default.GetRequiredService<IPluginService>();
|
||||
|
||||
this.Loaded += (s, e) =>
|
||||
{
|
||||
RefreshPluginList();
|
||||
};
|
||||
}
|
||||
|
||||
private void RefreshPluginList()
|
||||
{
|
||||
Plugins.Clear();
|
||||
|
||||
var allPlugins = _pluginService.Plugins;
|
||||
|
||||
foreach (var plugin in allPlugins)
|
||||
{
|
||||
Plugins.Add(new PluginDisplayModel(plugin));
|
||||
}
|
||||
|
||||
Bindings.Update();
|
||||
}
|
||||
|
||||
private async void OnInstallPluginClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var file = await Helper.PickerHelper.PickSingleFileAsync<SettingsWindow>([".zip"]);
|
||||
|
||||
if (file != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 显示加载条...
|
||||
|
||||
// 3. 调用我们在上一步写的 InstallPlugin 方法
|
||||
_pluginService.InstallPlugin(file.Path);
|
||||
|
||||
// 4. 重新加载所有插件 (这会触发热重载)
|
||||
_pluginService.LoadPlugins();
|
||||
|
||||
// 5. 刷新界面
|
||||
RefreshPluginList();
|
||||
|
||||
ShowTip("安装成功", $"插件 {file.Name} 已安装。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowError("安装失败", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 卸载按钮点击事件
|
||||
private async void OnUninstallClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button btn && btn.Tag is IPlugin plugin)
|
||||
{
|
||||
// 二次确认对话框
|
||||
ContentDialog deleteDialog = new ContentDialog
|
||||
{
|
||||
XamlRoot = this.XamlRoot,
|
||||
Title = "卸载插件?",
|
||||
Content = $"确定要删除 \"{plugin.Name}\" 吗?此操作无法撤销。",
|
||||
PrimaryButtonText = "删除",
|
||||
CloseButtonText = "取消",
|
||||
DefaultButton = ContentDialogButton.Close
|
||||
};
|
||||
|
||||
var result = await deleteDialog.ShowAsync();
|
||||
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: 在 PluginService 里加一个 UninstallPlugin 方法
|
||||
// 逻辑:找到插件对应文件夹,Directory.Delete(path, true)
|
||||
// _pluginService.UninstallPlugin(plugin.Id);
|
||||
|
||||
// 暂时我们只能刷新列表演示
|
||||
RefreshPluginList();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowError("卸载失败", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 简单的弹窗辅助方法
|
||||
private async void ShowTip(string title, string content)
|
||||
{
|
||||
ContentDialog dialog = new ContentDialog
|
||||
{
|
||||
XamlRoot = this.XamlRoot,
|
||||
Title = title,
|
||||
Content = content,
|
||||
CloseButtonText = "好"
|
||||
};
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
|
||||
private async void ShowError(string title, string content)
|
||||
{
|
||||
ContentDialog dialog = new ContentDialog
|
||||
{
|
||||
XamlRoot = this.XamlRoot,
|
||||
Title = title,
|
||||
Content = content,
|
||||
CloseButtonText = "关闭"
|
||||
};
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,15 +47,10 @@ namespace BetterLyrics.WinUI3.Models
|
||||
|
||||
[NotMapped][JsonIgnore] public LyricsSearchProvider? ProviderIfFound => IsFound ? Provider : null;
|
||||
|
||||
[MaxLength(128)]
|
||||
public string? PluginId { get; set; }
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return new LyricsCacheItem()
|
||||
{
|
||||
PluginId = this.PluginId,
|
||||
|
||||
Provider = this.Provider,
|
||||
TranslationProvider = this.TranslationProvider,
|
||||
TransliterationProvider = this.TransliterationProvider,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
using BetterLyrics.Core.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class PluginDisplayModel
|
||||
{
|
||||
public IPlugin Plugin { get; }
|
||||
|
||||
public string Name => Plugin.Name;
|
||||
public string Description => Plugin.Description;
|
||||
public string Id => Plugin.Id;
|
||||
public string Author => Plugin.Author;
|
||||
public string Version => "v1.0.0"; // 如果您的接口有 Version 字段就读接口的
|
||||
|
||||
public string Glyph => !string.IsNullOrEmpty(Name) ? Name.Substring(0, 1) : "?";
|
||||
|
||||
public PluginDisplayModel(IPlugin plugin)
|
||||
{
|
||||
Plugin = plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +212,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
{
|
||||
_logger.LogInformation("SearchAllAsync {SongInfo}", songInfo);
|
||||
var results = new List<LyricsCacheItem>();
|
||||
|
||||
|
||||
foreach (var provider in Enum.GetValues<LyricsSearchProvider>())
|
||||
{
|
||||
if (provider == LyricsSearchProvider.Plugin) continue;
|
||||
@@ -221,13 +221,13 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
results.Add(searchResult);
|
||||
}
|
||||
|
||||
if (_pluginService.Providers.Any())
|
||||
foreach (var plugin in _pluginService.Plugins)
|
||||
{
|
||||
foreach (var plugin in _pluginService.Providers)
|
||||
{
|
||||
if (token.IsCancellationRequested) break;
|
||||
if (token.IsCancellationRequested) break;
|
||||
|
||||
var pluginResult = await SearchPluginAsync(songInfo, plugin, token);
|
||||
if (plugin is ILyricsSearchPlugin lyricsSearchPlugin)
|
||||
{
|
||||
var pluginResult = await SearchPluginAsync(songInfo, lyricsSearchPlugin, token);
|
||||
results.Add(pluginResult);
|
||||
}
|
||||
}
|
||||
@@ -694,12 +694,11 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
|
||||
private async Task<LyricsCacheItem> SearchPluginAsync(SongInfo songInfo, ILyricsProvider plugin, CancellationToken token)
|
||||
private async Task<LyricsCacheItem> SearchPluginAsync(SongInfo songInfo, ILyricsSearchPlugin plugin, CancellationToken token)
|
||||
{
|
||||
var cacheItem = new LyricsCacheItem
|
||||
{
|
||||
Provider = LyricsSearchProvider.Plugin,
|
||||
PluginId = plugin.Id,
|
||||
};
|
||||
|
||||
try
|
||||
|
||||
@@ -7,8 +7,10 @@ namespace BetterLyrics.WinUI3.Services.PluginService
|
||||
{
|
||||
public interface IPluginService
|
||||
{
|
||||
IReadOnlyList<ILyricsProvider> Providers { get; }
|
||||
IReadOnlyList<IPlugin> Plugins { get; }
|
||||
|
||||
void LoadPlugins();
|
||||
void InstallPlugin(string zipPath);
|
||||
void UninstallPlugin(string pluginId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,29 @@ namespace BetterLyrics.WinUI3.Services.PluginService
|
||||
{
|
||||
private AssemblyDependencyResolver _resolver;
|
||||
|
||||
public PluginLoadContext(string pluginPath)
|
||||
public PluginLoadContext(string pluginPath) : base(isCollectible: true)
|
||||
{
|
||||
_resolver = new AssemblyDependencyResolver(pluginPath);
|
||||
}
|
||||
|
||||
protected override Assembly? Load(AssemblyName assemblyName)
|
||||
{
|
||||
var sharedAssemblies = new HashSet<string>
|
||||
{
|
||||
"BetterLyrics.Core",
|
||||
"Microsoft.WindowsAppSDK",
|
||||
"Microsoft.UI",
|
||||
"Microsoft.UI.Xaml",
|
||||
"Microsoft.Graphics",
|
||||
"System.Runtime",
|
||||
"Newtonsoft.Json"
|
||||
};
|
||||
|
||||
if (assemblyName.Name == null || sharedAssemblies.Contains(assemblyName.Name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
|
||||
if (assemblyPath != null)
|
||||
{
|
||||
@@ -26,5 +42,15 @@ namespace BetterLyrics.WinUI3.Services.PluginService
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
||||
{
|
||||
string? libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
|
||||
if (libraryPath != null)
|
||||
{
|
||||
return LoadUnmanagedDllFromPath(libraryPath);
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,188 @@
|
||||
using BetterLyrics.Core.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader; // 必须引用
|
||||
using Windows.Storage;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.PluginService
|
||||
{
|
||||
public class PluginService : IPluginService
|
||||
{
|
||||
private List<ILyricsProvider> _providers = new();
|
||||
// 1. 核心插件列表
|
||||
private List<IPlugin> _plugins = new();
|
||||
public IReadOnlyList<IPlugin> Plugins => _plugins;
|
||||
|
||||
public IReadOnlyList<ILyricsProvider> Providers => _providers;
|
||||
// 2. 新增:上下文管理字典 (Key: 插件ID, Value: 加载上下文)
|
||||
// 我们需要存着它,以便将来执行 Unload
|
||||
private Dictionary<string, PluginLoadContext> _pluginContexts = new();
|
||||
|
||||
// 3. 新增:已加载的文件路径缓存 (防止同一个 DLL 被扫两遍)
|
||||
private HashSet<string> _loadedDllPaths = new();
|
||||
|
||||
public void LoadPlugins()
|
||||
{
|
||||
// 在涉及加载程序集的地方:
|
||||
// var context = new PluginLoadContext(pluginPath);
|
||||
// 它是本文件夹下的 internal 或者是 public 类,直接用即可。
|
||||
string pluginsRoot = Path.Combine(ApplicationData.Current.LocalFolder.Path, "plugins");
|
||||
if (!Directory.Exists(pluginsRoot)) Directory.CreateDirectory(pluginsRoot);
|
||||
|
||||
var pluginFolders = Directory.GetDirectories(pluginsRoot);
|
||||
|
||||
foreach (var folder in pluginFolders)
|
||||
{
|
||||
var dllFiles = Directory.GetFiles(folder, "*.dll");
|
||||
|
||||
foreach (var dllPath in dllFiles)
|
||||
{
|
||||
// 🔥 防御 1:基于路径的检查
|
||||
// 如果这个文件已经在内存里了,绝对不要再 Load 一次
|
||||
if (_loadedDllPaths.Contains(dllPath)) continue;
|
||||
|
||||
TryLoadPlugin(dllPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryLoadPlugin(string dllPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建上下文
|
||||
var loadContext = new PluginLoadContext(dllPath);
|
||||
|
||||
// 加载程序集
|
||||
var assembly = loadContext.LoadFromAssemblyPath(dllPath);
|
||||
|
||||
bool isPluginFound = false;
|
||||
|
||||
foreach (var type in assembly.GetExportedTypes())
|
||||
{
|
||||
if (typeof(IPlugin).IsAssignableFrom(type) && !type.IsAbstract)
|
||||
{
|
||||
// 实例化
|
||||
var plugin = (IPlugin?)Activator.CreateInstance(type);
|
||||
if (plugin == null) continue;
|
||||
|
||||
// 🔥 防御 2:基于 ID 的检查
|
||||
// 防止 "不同 DLL 或者是新版本" 导致 ID 冲突
|
||||
if (_plugins.Any(p => p.Id == plugin.Id))
|
||||
{
|
||||
// 遇到重复 ID,我们选择跳过新的,保留旧的
|
||||
// (或者你也可以设计成卸载旧的加载新的,这取决于策略)
|
||||
// 由于我们已经加载了 assembly,现在决定不用它,必须卸载 context
|
||||
loadContext.Unload();
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化插件 (如果有 Initialize 方法)
|
||||
try
|
||||
{
|
||||
plugin.Initialize();
|
||||
}
|
||||
catch (Exception initEx)
|
||||
{
|
||||
// 如果初始化失败(比如缺字典文件),就不应该把它加到列表里
|
||||
// 记录日志...
|
||||
loadContext.Unload();
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ 成功入库
|
||||
_plugins.Add(plugin);
|
||||
_pluginContexts.Add(plugin.Id, loadContext); // 记录上下文
|
||||
isPluginFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果这个 DLL 里找到了插件,标记路径为已加载
|
||||
if (isPluginFound)
|
||||
{
|
||||
_loadedDllPaths.Add(dllPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果这个 DLL 里一个插件都没找到 (可能是依赖库),
|
||||
// 为了节省内存,我们可以把这个 Context 卸载掉
|
||||
// (前提是其他插件不依赖它,这块比较复杂,简单起见可以先卸载)
|
||||
loadContext.Unload();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录日志...
|
||||
// throw new Exception($"Failed to load plugin from {dllPath}: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void UninstallPlugin(string pluginId)
|
||||
{
|
||||
var plugin = _plugins.FirstOrDefault(p => p.Id == pluginId);
|
||||
if (plugin == null) return;
|
||||
|
||||
// 1. 获取相关信息
|
||||
var dllPath = plugin.GetType().Assembly.Location;
|
||||
var folderPath = Path.GetDirectoryName(dllPath);
|
||||
|
||||
// 2. 从列表中移除插件对象
|
||||
_plugins.Remove(plugin);
|
||||
_loadedDllPaths.Remove(dllPath); // 允许下次重新加载这个路径
|
||||
|
||||
// 3. 💥 核心:卸载上下文 (释放文件锁的关键)
|
||||
if (_pluginContexts.TryGetValue(pluginId, out var context))
|
||||
{
|
||||
context.Unload();
|
||||
_pluginContexts.Remove(pluginId);
|
||||
}
|
||||
|
||||
// 4. 强制 GC (垃圾回收)
|
||||
// 上下文卸载是“软卸载”,必须等 GC 跑过之后,文件锁才会真正释放
|
||||
// 这几行代码对于“热删除”非常重要
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
// 5. 物理删除文件
|
||||
if (Directory.Exists(folderPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.Delete(folderPath, true);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
// 如果 GC 还没来得及释放锁,可能会报错
|
||||
// 实际生产中,通常是标记为“待删除”,下次重启时删
|
||||
// 或者提示用户重启
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void InstallPlugin(string zipPath)
|
||||
{
|
||||
string pluginsRoot = Path.Combine(ApplicationData.Current.LocalFolder.Path, "plugins");
|
||||
string folderName = Path.GetFileNameWithoutExtension(zipPath);
|
||||
string installDir = Path.Combine(pluginsRoot, folderName);
|
||||
|
||||
// 如果已经存在,说明是更新或者重装
|
||||
// 我们需要先根据文件夹找到旧插件的 ID,然后执行标准的卸载流程
|
||||
// (这里简化处理:直接尝试删文件夹,如果删不掉说明被占用)
|
||||
if (Directory.Exists(installDir))
|
||||
{
|
||||
// TODO: 最好是先 Find plugin by path -> UninstallPlugin(id)
|
||||
// 否则文件被锁住无法 Delete
|
||||
try
|
||||
{
|
||||
Directory.Delete(installDir, true);
|
||||
}
|
||||
catch { /* 忽略或报错 */ }
|
||||
}
|
||||
Directory.CreateDirectory(installDir);
|
||||
|
||||
ZipFile.ExtractToDirectory(zipPath, installDir);
|
||||
|
||||
// 安装完后,如果不重启软件,你想立即生效的话:
|
||||
// LoadPlugins(); // 因为加了路径去重,这里重新调一次是安全的
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
using BetterLyrics.WinUI3.Models.Http;
|
||||
using BetterLyrics.Core.Interfaces;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models.Http;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using BetterLyrics.WinUI3.Services.PluginService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
@@ -12,37 +16,56 @@ namespace BetterLyrics.WinUI3.Services.TransliterationService
|
||||
public class TransliterationService : ITransliterationService
|
||||
{
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IPluginService _pluginService;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public TransliterationService(ISettingsService settingsService)
|
||||
public TransliterationService(ISettingsService settingsService, IPluginService pluginService)
|
||||
{
|
||||
_settingsService = settingsService;
|
||||
_pluginService = pluginService;
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
//public async Task<string> TransliterateText(string text, string targetLangCode, CancellationToken token)
|
||||
//{
|
||||
// if (string.IsNullOrWhiteSpace(text))
|
||||
// {
|
||||
// throw new Exception(text + " is empty or null.");
|
||||
// }
|
||||
|
||||
// if (string.IsNullOrEmpty(_settingsService.AppSettings.TranslationSettings.CutletDockerServer))
|
||||
// {
|
||||
// throw new Exception("cutlet-docker server URL is not set in settings.");
|
||||
// }
|
||||
|
||||
// var request = new CutletDockerRequest { Text = text };
|
||||
// var reqJson = System.Text.Json.JsonSerializer.Serialize(request, SourceGenerationContext.Default.CutletDockerRequest);
|
||||
|
||||
// var url = $"{_settingsService.AppSettings.TranslationSettings.CutletDockerServer}/convert";
|
||||
// var response = await _httpClient.PostAsync(url, new StringContent(reqJson, Encoding.UTF8, "application/json"));
|
||||
|
||||
// response.EnsureSuccessStatusCode();
|
||||
// var resJson = await response.Content.ReadAsStringAsync(token);
|
||||
|
||||
// var result = System.Text.Json.JsonSerializer.Deserialize(resJson, SourceGenerationContext.Default.CutletDockerResponse);
|
||||
// return result?.RomajiText ?? string.Empty;
|
||||
//}
|
||||
|
||||
public async Task<string> TransliterateText(string text, string targetLangCode, CancellationToken token)
|
||||
{
|
||||
string? result = null;
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
throw new Exception(text + " is empty or null.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_settingsService.AppSettings.TranslationSettings.CutletDockerServer))
|
||||
var plugin = (ILyricsTransliterationPlugin?)_pluginService.Plugins.FirstOrDefault(x => x is ILyricsTransliterationPlugin);
|
||||
if (plugin != null)
|
||||
{
|
||||
throw new Exception("cutlet-docker server URL is not set in settings.");
|
||||
result = await plugin.GetTransliterationAsync(text, PhoneticHelper.RomanCode);
|
||||
}
|
||||
|
||||
var request = new CutletDockerRequest { Text = text };
|
||||
var reqJson = System.Text.Json.JsonSerializer.Serialize(request, SourceGenerationContext.Default.CutletDockerRequest);
|
||||
|
||||
var url = $"{_settingsService.AppSettings.TranslationSettings.CutletDockerServer}/convert";
|
||||
var response = await _httpClient.PostAsync(url, new StringContent(reqJson, Encoding.UTF8, "application/json"));
|
||||
|
||||
response.EnsureSuccessStatusCode();
|
||||
var resJson = await response.Content.ReadAsStringAsync(token);
|
||||
|
||||
var result = System.Text.Json.JsonSerializer.Deserialize(resJson, SourceGenerationContext.Default.CutletDockerResponse);
|
||||
return result?.RomajiText ?? string.Empty;
|
||||
return result ?? "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>لا يمكن عرض المقطوعات لأن المسار غير موجود</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>سياسة الخصوصية</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>Titel können nicht angezeigt werden, da der Pfad nicht existiert</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Datenschutzrichtlinie</value>
|
||||
</data>
|
||||
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>Unable to view tracks because the path does not exist</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value>Install plugin</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value>No plugins installed</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value>Plugin Manager</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value>Uninstall plugin</value>
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Privacy Policy</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>No se pueden ver las pistas porque la ruta no existe</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Política de privacidad</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>Impossible d'afficher les pistes car le chemin n'existe pas</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Politique de confidentialité</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>ट्रैक देखने में असमर्थ क्योंकि पथ मौजूद नहीं है</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>गोपनीयता नीति</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>Tidak dapat melihat trek karena jalur tidak ada</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Kebijakan Privasi</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>パスが存在しないため、曲を表示できません</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>個人情報保護方針</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>경로가 존재하지 않아 트랙을 볼 수 없습니다</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>개인정보 처리방침</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>Tidak dapat melihat trek kerana laluan tidak wujud</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Dasar Privasi</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>Não foi possível ver as faixas porque o caminho não existe</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Política de Privacidade</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>Невозможно просмотреть треки, так как путь не существует</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Политика конфиденциальности</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>ไม่สามารถดูแทร็กได้เนื่องจากเส้นทางไม่มีอยู่</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>นโยบายความเป็นส่วนตัว</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>Không thể xem các bài hát vì đường dẫn không tồn tại</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>Chính sách bảo mật</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>无法查看曲目,因为路径不存在</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value>安装插件</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value>未安装任何插件</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value>插件管理</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value>卸载插件</value>
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>隐私政策</value>
|
||||
</data>
|
||||
|
||||
@@ -59,46 +59,46 @@
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||
<xsd:attribute name="type" type="xsd:string"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string"/>
|
||||
<xsd:attribute name="name" type="xsd:string"/>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
<xsd:attribute ref="xml:space"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
@@ -555,6 +555,18 @@
|
||||
<data name="PlaylistViewFailed" xml:space="preserve">
|
||||
<value>無法檢視曲目,因為路徑不存在</value>
|
||||
</data>
|
||||
<data name="PluginManagerControlInstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlNoPluginsInstalled.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlTitle.Text" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PluginManagerControlUninstall.Content" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="PrivacyPolicy.Content" xml:space="preserve">
|
||||
<value>隱私權政策</value>
|
||||
</data>
|
||||
|
||||
@@ -56,6 +56,12 @@
|
||||
Glyph=}"
|
||||
Tag="Stats" />
|
||||
|
||||
<NavigationViewItem
|
||||
Content="Plugins"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
Glyph=}"
|
||||
Tag="Plugins" />
|
||||
|
||||
<NavigationViewItem
|
||||
x:Uid="SettingsPageAbout"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
@@ -96,6 +102,11 @@
|
||||
<uc:StatsDashboardControl />
|
||||
</controls:Case>
|
||||
|
||||
<!-- Plugins -->
|
||||
<controls:Case Value="Plugins">
|
||||
<uc:PluginManagerControl />
|
||||
</controls:Case>
|
||||
|
||||
<!-- About -->
|
||||
<controls:Case Value="About">
|
||||
<uc:AboutControl />
|
||||
|
||||
@@ -15,6 +15,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterLyrics.Core", "Better
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterLyrics.Plugins.Demo", "BetterLyrics.Plugins.Demo\BetterLyrics.Plugins.Demo.csproj", "{87D235CA-4311-4766-8186-AD9B193DFABC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BetterLyrics.Plugins.Romaji", "BetterLyrics.Plugins.Romaji\BetterLyrics.Plugins.Romaji.csproj", "{DD2D477F-94CD-4D4B-8B59-C127F2850E34}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RomajiConverter.Core", "RomajiConverter.Core\RomajiConverter.Core.csproj", "{351807DB-CD63-B939-8071-B1FBFF969569}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -103,6 +107,30 @@ Global
|
||||
{87D235CA-4311-4766-8186-AD9B193DFABC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{87D235CA-4311-4766-8186-AD9B193DFABC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{87D235CA-4311-4766-8186-AD9B193DFABC}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{DD2D477F-94CD-4D4B-8B59-C127F2850E34}.Release|x86.Build.0 = Release|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Debug|ARM64.ActiveCfg = Debug|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Debug|ARM64.Build.0 = Debug|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Release|ARM64.ActiveCfg = Release|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Release|ARM64.Build.0 = Release|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Release|x64.Build.0 = Release|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{351807DB-CD63-B939-8071-B1FBFF969569}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
1
Community/plugins-registry.json
Normal file
1
Community/plugins-registry.json
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
40
Community/scripts/check-hash-collision.js
Normal file
40
Community/scripts/check-hash-collision.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// Community/scripts/check-hash-collision.js
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const registryPath = path.join(__dirname, '../plugins-registry.json');
|
||||
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
||||
|
||||
function getStableId(pluginId) {
|
||||
let hash = 23;
|
||||
for (let i = 0; i < pluginId.length; i++) {
|
||||
hash = (hash * 31 + pluginId.charCodeAt(i)) | 0;
|
||||
}
|
||||
return Math.abs(hash) + 1000;
|
||||
}
|
||||
|
||||
const seenIds = new Set();
|
||||
let hasError = false;
|
||||
|
||||
console.log("🔍 Starting to check for plugin ID conflicts...");
|
||||
|
||||
registry.forEach(item => {
|
||||
const stableId = getStableId(item.id);
|
||||
|
||||
console.log(`Checking [${item.id}] -> Hash: ${stableId}`);
|
||||
|
||||
if (seenIds.has(stableId)) {
|
||||
console.error(`⛔ Fatel error! Conflict detected!`);
|
||||
console.error(`The hash value (${stableId}) calculated from the plugin ID [${item.id}] is duplicated with an existing plugin.`);
|
||||
hasError = true;
|
||||
}
|
||||
seenIds.add(stableId);
|
||||
});
|
||||
|
||||
if (hasError) {
|
||||
console.log("⛔ Check failed, please change the plugin ID.");
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log("✅ The check passed; no conflicts were found.");
|
||||
process.exit(0);
|
||||
}
|
||||
13
RomajiConverter.Core/Extensions/StringExtension.cs
Normal file
13
RomajiConverter.Core/Extensions/StringExtension.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace RomajiConverter.Core.Extensions
|
||||
{
|
||||
public static class StringExtension
|
||||
{
|
||||
public static string[] LineToUnits(this string str)
|
||||
{
|
||||
return str.Split(new[] { ' ', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
}
|
||||
}
|
||||
203
RomajiConverter.Core/Helpers/KanaHelper.cs
Normal file
203
RomajiConverter.Core/Helpers/KanaHelper.cs
Normal file
@@ -0,0 +1,203 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace RomajiConverter.Core.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 此类用于片假、平假互转
|
||||
/// </summary>
|
||||
public static class KanaHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 转为片假名
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToKatakana(string str)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (var c in str)
|
||||
{
|
||||
var bytes = Encoding.Unicode.GetBytes(c.ToString());
|
||||
if (bytes.Length == 2 && bytes[1] == 0x30 && bytes[0] >= 0x40 && bytes[0] <= 0x9F)
|
||||
stringBuilder.Append(Encoding.Unicode.GetString(new[] { (byte)(bytes[0] + 0x60), bytes[1] }));
|
||||
else
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转为平假名
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static string ToHiragana(string str)
|
||||
{
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (var c in str)
|
||||
{
|
||||
var bytes = Encoding.Unicode.GetBytes(c.ToString());
|
||||
if (bytes.Length == 2 && bytes[1] == 0x30 && bytes[0] >= 0xA0 && bytes[0] <= 0xFA)
|
||||
stringBuilder.Append(Encoding.Unicode.GetString(new[] { (byte)(bytes[0] - 0x60), bytes[1] }));
|
||||
else
|
||||
stringBuilder.Append(c);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 假名转罗马音
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static string KatakanaToRomaji(string str)
|
||||
{
|
||||
var result = new StringBuilder();
|
||||
|
||||
for (var i = 0; i < str.Length;)
|
||||
{
|
||||
if (i < str.Length - 1)
|
||||
{
|
||||
var extendedWord = str.Substring(i, 2);
|
||||
if (ExtendedKanaDictionary.ContainsKey(extendedWord))
|
||||
{
|
||||
result.Append(ExtendedKanaDictionary[extendedWord]);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var word = str[i].ToString();
|
||||
if (KanaDictionary.ContainsKey(word))
|
||||
{
|
||||
//正常转换
|
||||
result.Append(KanaDictionary[word]);
|
||||
}
|
||||
else if (word == "ー")
|
||||
{
|
||||
//长音,取前一个音
|
||||
result.Append(result.Length > 0 ? result[result.Length - 1].ToString() : word);
|
||||
}
|
||||
else
|
||||
{
|
||||
//不能识别,保持原样
|
||||
result.Append(word);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
//处理促音
|
||||
for (var i = 0; i < result.Length; i++)
|
||||
{
|
||||
if (result[i] == 'っ' || result[i] == 'ッ')
|
||||
if (i < result.Length - 1)
|
||||
if (result[i + 1] == 'c')
|
||||
result[i] = 't';
|
||||
else
|
||||
result[i] = result[i + 1];
|
||||
else
|
||||
result.Remove(i, 1);
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> KanaDictionary = new Dictionary<string, string>
|
||||
{
|
||||
//平假
|
||||
{ "あ", "a" }, { "い", "i" }, { "う", "u" }, { "え", "e" }, { "お", "o" },
|
||||
{ "か", "ka" }, { "き", "ki" }, { "く", "ku" }, { "け", "ke" }, { "こ", "ko" },
|
||||
{ "さ", "sa" }, { "し", "shi" }, { "す", "su" }, { "せ", "se" }, { "そ", "so" },
|
||||
{ "た", "ta" }, { "ち", "chi" }, { "つ", "tsu" }, { "て", "te" }, { "と", "to" },
|
||||
{ "な", "na" }, { "に", "ni" }, { "ぬ", "nu" }, { "ね", "ne" }, { "の", "no" },
|
||||
{ "は", "ha" }, { "ひ", "hi" }, { "ふ", "fu" }, { "へ", "he" }, { "ほ", "ho" },
|
||||
{ "ま", "ma" }, { "み", "mi" }, { "む", "mu" }, { "め", "me" }, { "も", "mo" },
|
||||
{ "や", "ya" }, { "ゆ", "yu" }, { "よ", "yo" },
|
||||
{ "ら", "ra" }, { "り", "ri" }, { "る", "ru" }, { "れ", "re" }, { "ろ", "ro" },
|
||||
{ "わ", "wa" }, { "を", "wo" },
|
||||
{ "が", "ga" }, { "ぎ", "gi" }, { "ぐ", "gu" }, { "げ", "ge" }, { "ご", "go" },
|
||||
{ "ざ", "za" }, { "じ", "ji" }, { "ず", "zu" }, { "ぜ", "ze" }, { "ぞ", "zo" },
|
||||
{ "だ", "da" }, { "ぢ", "ji" }, { "づ", "zu" }, { "で", "de" }, { "ど", "do" },
|
||||
{ "ば", "ba" }, { "び", "bi" }, { "ぶ", "bu" }, { "べ", "be" }, { "ぼ", "bo" },
|
||||
{ "ぱ", "pa" }, { "ぴ", "pi" }, { "ぷ", "pu" }, { "ぺ", "pe" }, { "ぽ", "po" },
|
||||
{ "ん", "n" },
|
||||
|
||||
//片假
|
||||
{ "ア", "a" }, { "イ", "i" }, { "ウ", "u" }, { "エ", "e" }, { "オ", "o" },
|
||||
{ "カ", "ka" }, { "キ", "ki" }, { "ク", "ku" }, { "ケ", "ke" }, { "コ", "ko" },
|
||||
{ "サ", "sa" }, { "シ", "shi" }, { "ス", "su" }, { "セ", "se" }, { "ソ", "so" },
|
||||
{ "タ", "ta" }, { "チ", "chi" }, { "ツ", "tsu" }, { "テ", "te" }, { "ト", "to" },
|
||||
{ "ナ", "na" }, { "ニ", "ni" }, { "ヌ", "nu" }, { "ネ", "ne" }, { "ノ", "no" },
|
||||
{ "ハ", "ha" }, { "ヒ", "hi" }, { "フ", "fu" }, { "ヘ", "he" }, { "ホ", "ho" },
|
||||
{ "マ", "ma" }, { "ミ", "mi" }, { "ム", "mu" }, { "メ", "me" }, { "モ", "mo" },
|
||||
{ "ヤ", "ya" }, { "ユ", "yu" }, { "ヨ", "yo" },
|
||||
{ "ラ", "ra" }, { "リ", "ri" }, { "ル", "ru" }, { "レ", "re" }, { "ロ", "ro" },
|
||||
{ "ワ", "wa" }, { "ヲ", "wo" },
|
||||
{ "ガ", "ga" }, { "ギ", "gi" }, { "グ", "gu" }, { "ゲ", "ge" }, { "ゴ", "go" },
|
||||
{ "ザ", "za" }, { "ジ", "ji" }, { "ズ", "zu" }, { "ゼ", "ze" }, { "ゾ", "zo" },
|
||||
{ "ダ", "da" }, { "ヂ", "ji" }, { "ヅ", "zu" }, { "デ", "de" }, { "ド", "do" },
|
||||
{ "バ", "ba" }, { "ビ", "bi" }, { "ブ", "bu" }, { "ベ", "be" }, { "ボ", "bo" },
|
||||
{ "パ", "pa" }, { "ピ", "pi" }, { "プ", "pu" }, { "ペ", "pe" }, { "ポ", "po" },
|
||||
{ "ン", "n" },
|
||||
|
||||
//小版本
|
||||
{ "ぁ", "a" }, { "ぃ", "i" }, { "ぅ", "u" }, { "ぇ", "e" }, { "ぉ", "o" },
|
||||
{ "ゃ", "ya" }, { "ゅ", "yu" }, { "ょ", "yo" }, { "ゎ", "wa" },
|
||||
{ "ァ", "a" }, { "ィ", "i" }, { "ゥ", "u" }, { "ェ", "e" }, { "ォ", "o" },
|
||||
{ "ャ", "ya" }, { "ュ", "yu" }, { "ョ", "yo" }, { "ヮ", "wa" },
|
||||
};
|
||||
|
||||
public static Dictionary<string, string> ExtendedKanaDictionary = new Dictionary<string, string>
|
||||
{
|
||||
//平假-拗音
|
||||
{ "きゃ", "kya" }, { "きゅ", "kyu" }, { "きょ", "kyo" },
|
||||
{ "しゃ", "sha" }, { "しゅ", "shu" }, { "しょ", "sho" },
|
||||
{ "ちゃ", "cha" }, { "ちゅ", "chu" }, { "ちょ", "cho" },
|
||||
{ "にゃ", "nya" }, { "にゅ", "nyu" }, { "にょ", "nyo" },
|
||||
{ "ひゃ", "hya" }, { "ひゅ", "hyu" }, { "ひょ", "hyo" },
|
||||
{ "みゃ", "mya" }, { "みゅ", "myu" }, { "みょ", "myo" },
|
||||
{ "りゃ", "rya" }, { "りゅ", "ryu" }, { "りょ", "ryo" },
|
||||
{ "ぎゃ", "gya" }, { "ぎゅ", "gyu" }, { "ぎょ", "gyo" },
|
||||
{ "じゃ", "ja" }, { "じゅ", "ju" }, { "じょ", "jo" },
|
||||
{ "ぢゃ", "ja" }, { "ぢゅ", "ju" }, { "ぢょ", "jo" },
|
||||
{ "びゃ", "bya" }, { "びゅ", "byu" }, { "びょ", "byo" },
|
||||
{ "ぴゃ", "pya" }, { "ぴゅ", "pyu" }, { "ぴょ", "pyo" },
|
||||
|
||||
//片假-拗音
|
||||
{ "キャ", "kya" }, { "キュ", "kyu" }, { "キョ", "kyo" },
|
||||
{ "シャ", "sha" }, { "シュ", "shu" }, { "ショ", "sho" },
|
||||
{ "チャ", "cha" }, { "チュ", "chu" }, { "チョ", "cho" },
|
||||
{ "ニャ", "nya" }, { "ニュ", "nyu" }, { "ニョ", "nyo" },
|
||||
{ "ヒャ", "hya" }, { "ヒュ", "hyu" }, { "ヒョ", "hyo" },
|
||||
{ "ミャ", "mya" }, { "ミュ", "myu" }, { "ミョ", "myo" },
|
||||
{ "リャ", "rya" }, { "リュ", "ryu" }, { "リョ", "ryo" },
|
||||
{ "ギャ", "gya" }, { "ギュ", "gyu" }, { "ギョ", "gyo" },
|
||||
{ "ジャ", "ja" }, { "ジュ", "ju" }, { "ジョ", "jo" },
|
||||
{ "ヂャ", "ja" }, { "ヂュ", "ju" }, { "ヂョ", "jo" },
|
||||
{ "ビャ", "bya" }, { "ビュ", "byu" }, { "ビョ", "byo" },
|
||||
{ "ピャ", "pya" }, { "ピュ", "pyu" }, { "ピョ", "pyo" },
|
||||
|
||||
//其他语言
|
||||
{ "イェ", "ye" },
|
||||
{ "ウィ", "wi" }, { "ウェ", "we" }, { "ウォ", "wo" },
|
||||
{ "ヴァ", "va" }, { "ヴィ", "vi" }, { "ヴ", "vu" }, { "ヴェ", "ve" }, { "ヴォ", "vo" },
|
||||
{ "ヴュ", "vyu" },
|
||||
{ "クァ", "kwa" }, { "クィ", "kwi" }, { "クェ", "kwe" }, { "クォ", "kwo" },
|
||||
{ "グァ", "gwa" },
|
||||
{ "シェ", "she" },
|
||||
{ "ジェ", "je" },
|
||||
{ "チェ", "che" },
|
||||
{ "ツァ", "tsa" }, { "ツィ", "tsi" }, { "ツェ", "tse" }, { "ツォ", "tso" },
|
||||
{ "ティ", "ti" }, { "トゥ", "tu" },
|
||||
{ "テュ", "tyu" },
|
||||
{ "ディ", "di" }, { "ドゥ", "du" },
|
||||
{ "デュ", "dyu" },
|
||||
{ "ファ", "fa" }, { "フィ", "fi" }, { "フェ", "fe" }, { "フォ", "fo" },
|
||||
{ "フュ", "fyu" },
|
||||
};
|
||||
}
|
||||
}
|
||||
41
RomajiConverter.Core/Helpers/LrcParser.cs
Normal file
41
RomajiConverter.Core/Helpers/LrcParser.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace RomajiConverter.Core.Helpers
|
||||
{
|
||||
public static class LrcParser
|
||||
{
|
||||
public static readonly Regex LrcLineRegex =
|
||||
new Regex("(\\[(\\d+:)?\\d+:\\d+(\\.\\d+)?\\])+\\s*(?<text>.*?(?=\\[(\\d+:)?\\d+:\\d+(\\.\\d+)?\\]|$|\\r|\\n))",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
public static readonly Regex LrcTimeRegex =
|
||||
new Regex("\\[((?<hour>\\d+):)?(?<minute>\\d+):(?<second>\\d+)(\\.(?<millisecond>\\d+))?\\]", RegexOptions.Compiled);
|
||||
|
||||
public static List<(TimeSpan Time, string Text)> Parse(string lrc)
|
||||
{
|
||||
var result = new List<(TimeSpan Time, string Text)>();
|
||||
|
||||
var lineMatches = LrcLineRegex.Matches(lrc);
|
||||
foreach (Match lineMatch in lineMatches)
|
||||
{
|
||||
var text = lineMatch.Groups["text"].Value;
|
||||
var timeMatches = LrcTimeRegex.Matches(lineMatch.Value);
|
||||
foreach (Match timeMatch in timeMatches)
|
||||
{
|
||||
var hour = timeMatch.Groups["hour"].Success ? int.Parse(timeMatch.Groups["hour"].Value) : 0;
|
||||
var minute = timeMatch.Groups["minute"].Success ? int.Parse(timeMatch.Groups["minute"].Value) : 0;
|
||||
var second = timeMatch.Groups["second"].Success ? int.Parse(timeMatch.Groups["second"].Value) : 0;
|
||||
var millisecond = timeMatch.Groups["millisecond"].Success
|
||||
? int.Parse(timeMatch.Groups["millisecond"].Value)
|
||||
: 0;
|
||||
var time = new TimeSpan(0, hour, minute, second, millisecond);
|
||||
result.Add((time, text));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
239
RomajiConverter.Core/Helpers/RomajiAIHelper.cs
Normal file
239
RomajiConverter.Core/Helpers/RomajiAIHelper.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
using OpenAI;
|
||||
using OpenAI.Chat;
|
||||
using RomajiConverter.Core.Models;
|
||||
using RomajiConverter.Core.Options;
|
||||
using System;
|
||||
using System.ClientModel;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RomajiConverter.Core.Helpers
|
||||
{
|
||||
public static class RomajiAIHelper
|
||||
{
|
||||
public const string Prompt = @"用户将输入一段日文歌词,你需要逐词转换为以下格式:
|
||||
- 每行输出必须严格对应每行输入,禁止额外添加换行,禁止输出空行,不能因为遇到标点符号而换行,换行符必须使用单个\n
|
||||
- 对每行日文进行分词处理,分词应以现代日语常规形态(助词、助动词、词尾变化)为最小单位
|
||||
- 如果一个分词是日文且包含汉字,则需要给出平假名,用小括号在原文后标注,格式为:日文分词(平假名)。禁止在分词中间标注假名(例:x(xx)xx),要么标注整个分词的假名,要么将标注之后的部分拆分为新的分词
|
||||
- 纯假名分词不添加任何假名标注
|
||||
- 遇到仅当 は/へ/を 作为独立分词并起语法助词作用时,在后面添加“|”以及它的口语化假名,非助词情况下只输出原文
|
||||
- 遇到英文单词/字母、数字、标点符号、特殊符号、等非日文的unicode字符时,必须保留且单独作为一个分词,必须只输出原文,不能给出平假名
|
||||
- 每个分词之间必须用半角空格分隔
|
||||
- 如果无法确定某分词是否为助词或其读音,请优先保持原文不转换
|
||||
- 不要包含任何解释、注释、Markdown、额外字段或文本
|
||||
- 示例仅供参考,不能直接输出,任何时候都需要根据上面给出的文本进行转换
|
||||
示例:
|
||||
输入:昨日はColdな夜へ行を歌った
|
||||
输出:昨日(きのう) は|わ Cold な 夜(よる) へ|え 行(い) を|お 歌った(うたった)";
|
||||
|
||||
private static Regex _formatRegex = new Regex(@"^(.*?)(\((.*?)\))*?(\|(.*?))*?$", RegexOptions.Compiled);
|
||||
|
||||
private static ChatCompletionOptions _chatCompletionOptions = new ChatCompletionOptions
|
||||
{
|
||||
Temperature = 0.2f
|
||||
};
|
||||
|
||||
public static async Task LoadRomajiAsync(ICollection<ConvertedLine> convertedLines, string text, ToRomajiAIOptions options, CancellationToken cancellationToken = default)
|
||||
{
|
||||
//预处理为ConvertedLine列表, 其中会包含空行
|
||||
var cacheList = GetCacheList(options, text);
|
||||
|
||||
if (cacheList.Count == 0) return;
|
||||
|
||||
//获取ai结果
|
||||
var client = new ChatClient(
|
||||
model: options.Model,
|
||||
credential: new ApiKeyCredential(options.ApiKey),
|
||||
options: new OpenAIClientOptions
|
||||
{
|
||||
Endpoint = new Uri(options.BaseUrl)
|
||||
}
|
||||
);
|
||||
|
||||
var prompt = string.IsNullOrEmpty(options.Prompt) ? Prompt : options.Prompt;
|
||||
//发送的内容不包含空行
|
||||
var content = string.Join("\n", cacheList.Where(p => !string.IsNullOrWhiteSpace(p.Japanese)).Select(p => p.Japanese));
|
||||
|
||||
var messages = new List<ChatMessage>
|
||||
{
|
||||
new SystemChatMessage(prompt),
|
||||
new UserChatMessage(content)
|
||||
};
|
||||
|
||||
Debug.WriteLine(prompt);
|
||||
Debug.WriteLine(content);
|
||||
|
||||
var completionUpdates = client.CompleteChatStreamingAsync(messages, _chatCompletionOptions, cancellationToken: cancellationToken);
|
||||
|
||||
var stringBuilder = new StringBuilder();
|
||||
ushort lineIndex = 0;
|
||||
var l = 0;
|
||||
var r = 0;
|
||||
|
||||
//插入直到下一个非空行
|
||||
AddNextNotEmptyLine();
|
||||
|
||||
//处理流式返回
|
||||
var enumerator = completionUpdates.GetAsyncEnumerator(cancellationToken);
|
||||
try
|
||||
{
|
||||
while (await enumerator.MoveNextAsync())
|
||||
{
|
||||
var completionUpdate = enumerator.Current;
|
||||
if (completionUpdate.ContentUpdate.Count > 0)
|
||||
{
|
||||
var delta = FixFormat(completionUpdate.ContentUpdate[0].Text);
|
||||
if (string.IsNullOrEmpty(delta)) continue;
|
||||
stringBuilder.Append(delta);
|
||||
Debug.Write(completionUpdate.ContentUpdate[0].Text);
|
||||
|
||||
while (r < stringBuilder.Length)
|
||||
{
|
||||
if (stringBuilder[r] == '\n')
|
||||
{
|
||||
InsertUnit();
|
||||
//插入直到下一个非空行
|
||||
AddNextNotEmptyLine();
|
||||
r++;
|
||||
l = r;
|
||||
}
|
||||
else if (stringBuilder[r] == ' ')
|
||||
{
|
||||
InsertUnit();
|
||||
r++;
|
||||
l = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
r++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await enumerator.DisposeAsync();
|
||||
}
|
||||
|
||||
//处理完成,手动插入最后一个分词
|
||||
if (l != r)
|
||||
{
|
||||
InsertUnit();
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void AddNextNotEmptyLine()
|
||||
{
|
||||
do
|
||||
{
|
||||
var newLine = new ConvertedLine
|
||||
{
|
||||
Time = lineIndex >= cacheList.Count ? (TimeSpan?)null : cacheList[lineIndex].Time,
|
||||
Chinese = lineIndex >= cacheList.Count ? string.Empty : cacheList[lineIndex].Chinese,
|
||||
Index = lineIndex,
|
||||
Japanese = lineIndex >= cacheList.Count ? string.Empty : cacheList[lineIndex].Japanese
|
||||
};
|
||||
convertedLines.Add(newLine);
|
||||
lineIndex++;
|
||||
} while (string.IsNullOrWhiteSpace(convertedLines.Last().Japanese) && lineIndex < cacheList.Count);
|
||||
}
|
||||
|
||||
void InsertUnit()
|
||||
{
|
||||
var lastLine = convertedLines.Last();
|
||||
var lastUnitStr = stringBuilder.ToString(l, r - l);
|
||||
if (!string.IsNullOrEmpty(lastUnitStr))
|
||||
lastLine.Units.Add(GetUnit(lastLine.Index, lastUnitStr, options.IsParticleAsPronunciation));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<ConvertedLine> GetCacheList(ToRomajiAIOptions options, string text)
|
||||
{
|
||||
var timeSpans = new List<TimeSpan?>();
|
||||
var lineTextList = text.Split(Environment.NewLine.ToArray()).Where(p => !string.IsNullOrWhiteSpace(p)).ToList();
|
||||
|
||||
for (var i = 0; i < lineTextList.Count; i++)
|
||||
{
|
||||
if (LrcParser.LrcLineRegex.IsMatch(lineTextList[i]))
|
||||
{
|
||||
var lyrics = LrcParser.Parse(lineTextList[i]);
|
||||
timeSpans.Add(lyrics.Count > 0 ? lyrics[0].Time : (TimeSpan?)null);
|
||||
lineTextList[i] = lyrics.Count > 0 ? lyrics[0].Text : lineTextList[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
timeSpans.Add(null);
|
||||
}
|
||||
}
|
||||
|
||||
var cacheList = new List<ConvertedLine>();
|
||||
for (var index = 0; index < lineTextList.Count; index++)
|
||||
{
|
||||
var line = lineTextList[index];
|
||||
|
||||
if (RomajiHelper.IsChinese(line, options.ChineseRate)) continue;
|
||||
|
||||
var convertedLine = new ConvertedLine
|
||||
{
|
||||
Time = index < timeSpans.Count ? timeSpans[index] : null,
|
||||
Japanese = line.Replace("\0", "")
|
||||
};
|
||||
|
||||
if (index + 1 < lineTextList.Count &&
|
||||
RomajiHelper.IsChinese(lineTextList[index + 1], options.ChineseRate))
|
||||
convertedLine.Chinese = lineTextList[index + 1];
|
||||
|
||||
convertedLine.Index = (ushort)cacheList.Count;
|
||||
cacheList.Add(convertedLine);
|
||||
}
|
||||
|
||||
return cacheList;
|
||||
}
|
||||
|
||||
private static string FixFormat(string content)
|
||||
{
|
||||
content = content.Replace("\r", "");
|
||||
content = content.Replace("\\n", "\n");
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
private static ConvertedUnit GetUnit(ushort lineIndex, string unitString, bool isParticleAsPronunciation)
|
||||
{
|
||||
var match = _formatRegex.Match(unitString);
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
return new ConvertedUnit(lineIndex, unitString, KanaHelper.ToHiragana(unitString),
|
||||
KanaHelper.KatakanaToRomaji(unitString), false);
|
||||
}
|
||||
|
||||
var origin = match.Groups[1].Value;
|
||||
var kanji_gana = match.Groups[3].Value;
|
||||
var particle_gana = match.Groups[5].Value;
|
||||
|
||||
if (!string.IsNullOrEmpty(kanji_gana))
|
||||
{
|
||||
return new ConvertedUnit(lineIndex, origin, kanji_gana,
|
||||
KanaHelper.KatakanaToRomaji(kanji_gana), true);
|
||||
}
|
||||
else if (isParticleAsPronunciation && !string.IsNullOrEmpty(particle_gana))
|
||||
{
|
||||
return new ConvertedUnit(lineIndex, origin, particle_gana,
|
||||
KanaHelper.KatakanaToRomaji(particle_gana), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ConvertedUnit(lineIndex, origin, KanaHelper.ToHiragana(origin),
|
||||
KanaHelper.KatakanaToRomaji(origin), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
372
RomajiConverter.Core/Helpers/RomajiHelper.cs
Normal file
372
RomajiConverter.Core/Helpers/RomajiHelper.cs
Normal file
@@ -0,0 +1,372 @@
|
||||
using MeCab;
|
||||
using MeCab.Extension.UniDic;
|
||||
using RomajiConverter.Core.Extensions;
|
||||
using RomajiConverter.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using RomajiConverter.Core.Options;
|
||||
|
||||
namespace RomajiConverter.Core.Helpers
|
||||
{
|
||||
public static class RomajiHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 分词器
|
||||
/// </summary>
|
||||
private static MeCabTagger _tagger;
|
||||
|
||||
/// <summary>
|
||||
/// 自定义词典<原文, 假名>
|
||||
/// </summary>
|
||||
private static Dictionary<string, string> _customizeDict;
|
||||
|
||||
public static void Init(string baseDirectory = null)
|
||||
{
|
||||
string rootPath = !string.IsNullOrEmpty(baseDirectory)
|
||||
? baseDirectory
|
||||
: AppDomain.CurrentDomain.BaseDirectory;
|
||||
|
||||
//词典路径
|
||||
var dicPath = Path.Combine(rootPath, "unidic");
|
||||
var parameter = new MeCabParam
|
||||
{
|
||||
DicDir = dicPath,
|
||||
LatticeLevel = MeCabLatticeLevel.Zero
|
||||
};
|
||||
_tagger = MeCabTagger.Create(parameter);
|
||||
|
||||
var str = File.ReadAllText(Path.Combine(rootPath, "customizeDict.txt"));
|
||||
var list = str.Split(Environment.NewLine.ToArray());
|
||||
_customizeDict = new Dictionary<string, string>();
|
||||
foreach (var item in list)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(item)) continue;
|
||||
var array = item.Split(' ');
|
||||
if (array.Length < 2) continue;
|
||||
if (!_customizeDict.ContainsKey(array[0]))
|
||||
_customizeDict.Add(array[0], array[1]);
|
||||
}
|
||||
}
|
||||
|
||||
#region 主逻辑
|
||||
|
||||
/// <summary>
|
||||
/// 生成转换结果列表
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<ConvertedLine> ToRomaji(string text, ToRomajiOptions options = null)
|
||||
{
|
||||
options = options ?? new ToRomajiOptions();
|
||||
|
||||
var timeSpans = new List<TimeSpan?>();
|
||||
var lineTextList = text.Split(Environment.NewLine.ToArray()).Where(p => !string.IsNullOrWhiteSpace(p)).ToList();
|
||||
|
||||
for (var i = 0; i < lineTextList.Count; i++)
|
||||
{
|
||||
if (LrcParser.LrcLineRegex.IsMatch(lineTextList[i]))
|
||||
{
|
||||
var lyric = LrcParser.Parse(lineTextList[i]).FirstOrDefault();
|
||||
timeSpans.Add(lyric.Time);
|
||||
lineTextList[i] = lyric.Text;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeSpans.Add(null);
|
||||
}
|
||||
}
|
||||
|
||||
ushort lineIndex = 0;
|
||||
for (var index = 0; index < lineTextList.Count; index++)
|
||||
{
|
||||
var line = lineTextList[index];
|
||||
|
||||
if (IsChinese(line, options.ChineseRate)) continue;
|
||||
|
||||
var convertedLine = new ConvertedLine
|
||||
{
|
||||
Index = lineIndex,
|
||||
Time = index < timeSpans.Count ? timeSpans[index] : null,
|
||||
Japanese = line.Replace("\0", "")
|
||||
};
|
||||
|
||||
foreach (var sentence in convertedLine.Japanese.LineToUnits())
|
||||
{
|
||||
if (IsEnglish(sentence))
|
||||
{
|
||||
convertedLine.Units.Add(new ConvertedUnit(lineIndex, sentence, sentence, sentence, false));
|
||||
}
|
||||
else
|
||||
foreach (var unit in SentenceToRomaji(lineIndex, sentence, options.IsParticleAsPronunciation))
|
||||
convertedLine.Units.Add(unit);
|
||||
}
|
||||
|
||||
if (index + 1 < lineTextList.Count && IsChinese(lineTextList[index + 1], options.ChineseRate))
|
||||
convertedLine.Chinese = lineTextList[index + 1];
|
||||
|
||||
lineIndex++;
|
||||
yield return convertedLine;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分句转为罗马音
|
||||
/// </summary>
|
||||
/// <param name="lineIndex"></param>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="isParticleAsPronunciation"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<ConvertedUnit> SentenceToRomaji(ushort lineIndex, string str, bool isParticleAsPronunciation)
|
||||
{
|
||||
foreach (var item in _tagger.ParseToNodes(str))
|
||||
{
|
||||
var unit = MeCabNodeToUnit(lineIndex, item, isParticleAsPronunciation);
|
||||
|
||||
if (unit != null)
|
||||
yield return unit;
|
||||
}
|
||||
}
|
||||
|
||||
public static ConvertedUnit MeCabNodeToUnit(ushort lineIndex, MeCabNode item, bool isParticleAsPronunciation)
|
||||
{
|
||||
ConvertedUnit unit = null;
|
||||
if (item.CharType > 0)
|
||||
{
|
||||
var features = CustomSplit(item.Feature);
|
||||
if (TryCustomConvert(item.Surface, out var customResult))
|
||||
{
|
||||
//用户自定义词典
|
||||
unit = new ConvertedUnit(lineIndex,
|
||||
item.Surface,
|
||||
customResult,
|
||||
KanaHelper.KatakanaToRomaji(customResult),
|
||||
true);
|
||||
}
|
||||
else if (features.Length > 0 && (!isParticleAsPronunciation || item.GetPos1() != "助詞") && IsJapanese(item.Surface))
|
||||
{
|
||||
//纯假名
|
||||
unit = new ConvertedUnit(lineIndex,
|
||||
item.Surface,
|
||||
KanaHelper.ToHiragana(item.Surface),
|
||||
KanaHelper.KatakanaToRomaji(item.Surface),
|
||||
false);
|
||||
}
|
||||
else if (features.Length <= 6 || new[] { "補助記号" }.Contains(item.GetPos1()))
|
||||
{
|
||||
//标点符号或无法识别的字
|
||||
unit = new ConvertedUnit(lineIndex,
|
||||
item.Surface,
|
||||
item.Surface,
|
||||
item.Surface,
|
||||
false);
|
||||
}
|
||||
else if (IsEnglish(item.Surface))
|
||||
{
|
||||
//英文
|
||||
unit = new ConvertedUnit(lineIndex,
|
||||
item.Surface,
|
||||
item.Surface,
|
||||
item.Surface,
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
//汉字或助词
|
||||
var kana = GetKana(item);
|
||||
|
||||
unit = new ConvertedUnit(lineIndex,
|
||||
item.Surface,
|
||||
KanaHelper.ToHiragana(kana),
|
||||
KanaHelper.KatakanaToRomaji(kana),
|
||||
!IsJapanese(item.Surface));
|
||||
var (replaceHiragana, replaceRomaji) = GetReplaceData(item);
|
||||
unit.ReplaceHiragana = replaceHiragana;
|
||||
unit.ReplaceRomaji = replaceRomaji;
|
||||
}
|
||||
}
|
||||
else if (item.Stat != MeCabNodeStat.Bos && item.Stat != MeCabNodeStat.Eos)
|
||||
{
|
||||
unit = new ConvertedUnit(lineIndex,
|
||||
item.Surface,
|
||||
item.Surface,
|
||||
item.Surface,
|
||||
false);
|
||||
}
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 帮助方法
|
||||
|
||||
/// <summary>
|
||||
/// 自定义分隔方法(Feature可能存在如 a,b,c,"d,e",f 格式的数据,此处不能把双引号中的内容也分隔开)
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
private static string[] CustomSplit(string str)
|
||||
{
|
||||
var list = new List<string>();
|
||||
var item = new List<char>();
|
||||
var haveMark = false;
|
||||
foreach (var c in str)
|
||||
if (c == ',' && !haveMark)
|
||||
{
|
||||
list.Add(new string(item.ToArray()));
|
||||
item.Clear();
|
||||
}
|
||||
else if (c == '"')
|
||||
{
|
||||
item.Add(c);
|
||||
haveMark = !haveMark;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Add(c);
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有发音
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
private static (ObservableCollection<ReplaceString> replaceHiragana, ObservableCollection<ReplaceString>
|
||||
replaceRomaji) GetReplaceData(MeCabNode node)
|
||||
{
|
||||
var length = node.Length;
|
||||
var replaceNodeList = new List<MeCabNode>();
|
||||
|
||||
GetAllReplaceNode(replaceNodeList, node);
|
||||
|
||||
void GetAllReplaceNode(List<MeCabNode> list, MeCabNode n)
|
||||
{
|
||||
if (n != null && !list.Contains(n) && n.Length == length)
|
||||
{
|
||||
list.Add(n);
|
||||
GetAllReplaceNode(list, n.BNext);
|
||||
GetAllReplaceNode(list, n.ENext);
|
||||
}
|
||||
}
|
||||
|
||||
var replaceHiragana = new ObservableCollection<ReplaceString>();
|
||||
var replaceRomaji = new ObservableCollection<ReplaceString>();
|
||||
|
||||
ushort i = 1;
|
||||
foreach (var meCabNode in replaceNodeList
|
||||
.GroupBy(GetKana)
|
||||
.Select(g => g.First()))
|
||||
{
|
||||
var kana = GetKana(meCabNode);
|
||||
if (kana != null)
|
||||
{
|
||||
replaceHiragana.Add(new ReplaceString(i, KanaHelper.ToHiragana(kana), true));
|
||||
replaceRomaji.Add(new ReplaceString(i, KanaHelper.KatakanaToRomaji(kana), true));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return (replaceHiragana, replaceRomaji);
|
||||
}
|
||||
|
||||
private static string GetKana(MeCabNode node)
|
||||
{
|
||||
return node.GetPos1() == "助詞" ? node.GetPron() : node.GetKana();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义转换规则
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
private static bool TryCustomConvert(string str, out string result)
|
||||
{
|
||||
if (_customizeDict.ContainsKey(str))
|
||||
{
|
||||
result = _customizeDict[str];
|
||||
return true;
|
||||
}
|
||||
|
||||
result = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串(句子)是否简体中文
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="rate">容错率(0-1)</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsChinese(string str, float rate)
|
||||
{
|
||||
if (str.Length < 2)
|
||||
return false;
|
||||
|
||||
var wordArray = str.ToCharArray();
|
||||
var total = wordArray.Length;
|
||||
var chCount = 0f;
|
||||
var enCount = 0f;
|
||||
|
||||
foreach (var word in wordArray)
|
||||
{
|
||||
if (word != 'ー' && IsJapanese(word.ToString()))
|
||||
//含有日文直接返回否
|
||||
return false;
|
||||
|
||||
var gbBytes = Encoding.Unicode.GetBytes(word.ToString());
|
||||
|
||||
if (gbBytes.Length == 2) // double bytes char.
|
||||
{
|
||||
if (gbBytes[1] >= 0x4E && gbBytes[1] <= 0x9F) //中文
|
||||
chCount++;
|
||||
else
|
||||
total--;
|
||||
}
|
||||
else if (gbBytes.Length == 1)
|
||||
{
|
||||
var byteAscii = int.Parse(gbBytes[0].ToString());
|
||||
if ((byteAscii >= 65 && byteAscii <= 90) || (byteAscii >= 97 && byteAscii <= 122)) //英文字母
|
||||
enCount++;
|
||||
else
|
||||
total--;
|
||||
}
|
||||
}
|
||||
|
||||
if (chCount == 0) return false; //一个简体中文都没有
|
||||
|
||||
return (chCount + enCount) / total >= rate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串是否全为单字节
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsEnglish(string str)
|
||||
{
|
||||
return new Regex("^[\x20-\x7E]+$", RegexOptions.Compiled).IsMatch(str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串是否全为假名
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
private static bool IsJapanese(string str)
|
||||
{
|
||||
return Regex.IsMatch(str, @"^[\u3040-\u30ff]+$", RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
18
RomajiConverter.Core/Models/ConvertedLine.cs
Normal file
18
RomajiConverter.Core/Models/ConvertedLine.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace RomajiConverter.Core.Models
|
||||
{
|
||||
public class ConvertedLine
|
||||
{
|
||||
public ushort Index { get; set; } = 0;
|
||||
|
||||
public TimeSpan? Time { get; set; }
|
||||
|
||||
public string Chinese { get; set; } = string.Empty;
|
||||
|
||||
public string Japanese { get; set; } = string.Empty;
|
||||
|
||||
public ObservableCollection<ConvertedUnit> Units { get; set; } = new ObservableCollection<ConvertedUnit>();
|
||||
}
|
||||
}
|
||||
125
RomajiConverter.Core/Models/ConvertedUnit.cs
Normal file
125
RomajiConverter.Core/Models/ConvertedUnit.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace RomajiConverter.Core.Models
|
||||
{
|
||||
public class ConvertedUnit : INotifyPropertyChanged
|
||||
{
|
||||
private string _hiragana;
|
||||
private bool _isKanji;
|
||||
private string _japanese;
|
||||
private ObservableCollection<ReplaceString> _replaceHiragana;
|
||||
private ObservableCollection<ReplaceString> _replaceRomaji;
|
||||
private string _romaji;
|
||||
private ushort _selectId;
|
||||
private ushort _lineIndex;
|
||||
|
||||
public ConvertedUnit(ushort lineIndex, string japanese, string hiragana, string romaji, bool isKanji)
|
||||
{
|
||||
LineIndex = lineIndex;
|
||||
Japanese = japanese;
|
||||
Romaji = romaji;
|
||||
Hiragana = hiragana;
|
||||
IsKanji = isKanji;
|
||||
SelectId = 1;
|
||||
ReplaceHiragana = new ObservableCollection<ReplaceString> { new ReplaceString(1, hiragana, true) };
|
||||
ReplaceRomaji = new ObservableCollection<ReplaceString> { new ReplaceString(1, romaji, true) };
|
||||
}
|
||||
|
||||
public ushort LineIndex
|
||||
{
|
||||
get => _lineIndex;
|
||||
set
|
||||
{
|
||||
if (value == _lineIndex) return;
|
||||
_lineIndex = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Japanese
|
||||
{
|
||||
get => _japanese;
|
||||
set
|
||||
{
|
||||
if (value == _japanese) return;
|
||||
_japanese = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Romaji
|
||||
{
|
||||
get => _romaji;
|
||||
set
|
||||
{
|
||||
if (value == _romaji) return;
|
||||
_romaji = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ReplaceString> ReplaceRomaji
|
||||
{
|
||||
get => _replaceRomaji;
|
||||
set
|
||||
{
|
||||
if (Equals(value, _replaceRomaji)) return;
|
||||
_replaceRomaji = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public string Hiragana
|
||||
{
|
||||
get => _hiragana;
|
||||
set
|
||||
{
|
||||
if (value == _hiragana) return;
|
||||
_hiragana = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ReplaceString> ReplaceHiragana
|
||||
{
|
||||
get => _replaceHiragana;
|
||||
set
|
||||
{
|
||||
if (Equals(value, _replaceHiragana)) return;
|
||||
_replaceHiragana = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsKanji
|
||||
{
|
||||
get => _isKanji;
|
||||
set
|
||||
{
|
||||
if (value == _isKanji) return;
|
||||
_isKanji = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ushort SelectId
|
||||
{
|
||||
get => _selectId;
|
||||
set
|
||||
{
|
||||
if (value == _selectId) return;
|
||||
_selectId = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RomajiConverter.Core/Models/ReplaceString.cs
Normal file
23
RomajiConverter.Core/Models/ReplaceString.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace RomajiConverter.Core.Models
|
||||
{
|
||||
public class ReplaceString
|
||||
{
|
||||
public ReplaceString(ushort id, string value, bool isSystem)
|
||||
{
|
||||
Id = id;
|
||||
Value = value;
|
||||
IsSystem = isSystem;
|
||||
}
|
||||
|
||||
public ushort Id { get; set; }
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
public bool IsSystem { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
RomajiConverter.Core/Options/ToRomajiAIOptions.cs
Normal file
16
RomajiConverter.Core/Options/ToRomajiAIOptions.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace RomajiConverter.Core.Options
|
||||
{
|
||||
public class ToRomajiAIOptions : ToRomajiOptions
|
||||
{
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
public string Model { get; set; }
|
||||
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 提示词,可以不传,使用默认提示词
|
||||
/// </summary>
|
||||
public string Prompt { get; set; }
|
||||
}
|
||||
}
|
||||
9
RomajiConverter.Core/Options/ToRomajiOptions.cs
Normal file
9
RomajiConverter.Core/Options/ToRomajiOptions.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace RomajiConverter.Core.Options
|
||||
{
|
||||
public class ToRomajiOptions
|
||||
{
|
||||
public float ChineseRate { get; set; } = 1f;
|
||||
|
||||
public bool IsParticleAsPronunciation { get; set; } = true;
|
||||
}
|
||||
}
|
||||
25
RomajiConverter.Core/RomajiConverter.Core.csproj
Normal file
25
RomajiConverter.Core/RomajiConverter.Core.csproj
Normal file
@@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
|
||||
<PackageId>RomajiConverter.Core</PackageId>
|
||||
<Version>2.1.0</Version>
|
||||
<Authors>WL</Authors>
|
||||
<RepositoryUrl>https://github.com/xyh20180101/RomajiConverter.WinUI</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/xyh20180101/RomajiConverter.WinUI</PackageProjectUrl>
|
||||
<PackageLicenseExpression>GPL-2.0-or-later</PackageLicenseExpression>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<MeCabUseDefaultDictionary>False</MeCabUseDefaultDictionary>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MeCab.DotNet" Version="1.2.0" />
|
||||
<PackageReference Include="OpenAI" Version="2.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="RomajiConverter.Core.targets" PackagePath="build/RomajiConverter.Core.targets" />
|
||||
<None Include="docs\README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
13
RomajiConverter.Core/RomajiConverter.Core.targets
Normal file
13
RomajiConverter.Core/RomajiConverter.Core.targets
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<UniDicFiles Include="$(MSBuildThisFileDirectory)\..\contentFiles\any\any\unidic\**\*" />
|
||||
<CustomizeDict Include="$(MSBuildThisFileDirectory)\..\contentFiles\any\any\customizeDict.txt" />
|
||||
<VariantFiles Include="$(MSBuildThisFileDirectory)\..\contentFiles\any\any\Variants\**\*" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyFiles" BeforeTargets="Build">
|
||||
<Copy SourceFiles="@(UniDicFiles)" DestinationFolder="$(TargetDir)unidic\%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(CustomizeDict)" DestinationFolder="$(TargetDir)" />
|
||||
<Copy SourceFiles="@(VariantFiles)" DestinationFolder="$(TargetDir)Variants\%(RecursiveDir)" />
|
||||
</Target>
|
||||
</Project>
|
||||
58
RomajiConverter.Core/docs/README.md
Normal file
58
RomajiConverter.Core/docs/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# RomajiConverter.Core
|
||||
用于[RomajiConverter.WinUI](https://github.com/xyh20180101/RomajiConverter.WinUI)项目的罗马音转换相关逻辑的包
|
||||
|
||||
|
||||
## 使用
|
||||
```C#
|
||||
//离线分词器
|
||||
IEnumerable<ConvertedLine> list = RomajiHelper.ToRomaji(jpnStr);
|
||||
|
||||
//AI
|
||||
var list = new ObservableCollection<ConvertedLine>();
|
||||
await RomajiAIHelper.ToRomajiStreamingAsync(list, jpnStr, new ToRomajiAIOptions
|
||||
{
|
||||
BaseUrl = "",
|
||||
Model = "",
|
||||
ApiKey = ""
|
||||
}); //流式插入
|
||||
|
||||
//以下是类结构
|
||||
public class ConvertedLine
|
||||
{
|
||||
public ushort Index { get; set; } = 0;
|
||||
|
||||
public string Chinese { get; set; } = string.Empty;
|
||||
|
||||
public string Japanese { get; set; } = string.Empty;
|
||||
|
||||
public ObservableCollection<ConvertedUnit> Units { get; set; } = new ObservableCollection<ConvertedUnit>();
|
||||
}
|
||||
|
||||
public class ConvertedUnit
|
||||
{
|
||||
public ushort LineIndex { get; set; }
|
||||
|
||||
public string Japanese { get; set; }
|
||||
|
||||
public string Romaji { get; set; }
|
||||
|
||||
public ObservableCollection<ReplaceString> ReplaceRomaji { get; set; }
|
||||
|
||||
public string Hiragana { get; set; }
|
||||
|
||||
public ObservableCollection<ReplaceString> ReplaceHiragana { get; set; }
|
||||
|
||||
public bool IsKanji { get; set; }
|
||||
|
||||
public ushort SelectId { get; set; }
|
||||
}
|
||||
|
||||
public class ReplaceString
|
||||
{
|
||||
public ushort Id { get; set; }
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
public bool IsSystem { get; set; }
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user