├── Assets ├── AppIcon.png ├── StoreLogo.png ├── SettingsIcons.ttf ├── LargeTile.scale-100.png ├── LargeTile.scale-125.png ├── LargeTile.scale-150.png ├── LargeTile.scale-200.png ├── LargeTile.scale-400.png ├── SmallTile.scale-100.png ├── SmallTile.scale-125.png ├── SmallTile.scale-150.png ├── SmallTile.scale-200.png ├── SmallTile.scale-400.png ├── StoreLogo.scale-100.png ├── StoreLogo.scale-125.png ├── StoreLogo.scale-150.png ├── StoreLogo.scale-200.png ├── StoreLogo.scale-400.png ├── SplashScreen.scale-100.png ├── SplashScreen.scale-125.png ├── SplashScreen.scale-150.png ├── SplashScreen.scale-200.png ├── SplashScreen.scale-400.png ├── Square44x44Logo.scale-100.png ├── Square44x44Logo.scale-125.png ├── Square44x44Logo.scale-150.png ├── Square44x44Logo.scale-200.png ├── Square44x44Logo.scale-400.png ├── Wide310x150Logo.scale-100.png ├── Wide310x150Logo.scale-125.png ├── Wide310x150Logo.scale-150.png ├── Wide310x150Logo.scale-200.png ├── Wide310x150Logo.scale-400.png ├── Square150x150Logo.scale-100.png ├── Square150x150Logo.scale-125.png ├── Square150x150Logo.scale-150.png ├── Square150x150Logo.scale-200.png ├── Square150x150Logo.scale-400.png ├── Square44x44Logo.targetsize-16.png ├── Square44x44Logo.targetsize-24.png ├── Square44x44Logo.targetsize-256.png ├── Square44x44Logo.targetsize-32.png ├── Square44x44Logo.targetsize-48.png ├── Square44x44Logo.altform-unplated_targetsize-16.png ├── Square44x44Logo.altform-unplated_targetsize-24.png ├── Square44x44Logo.altform-unplated_targetsize-32.png ├── Square44x44Logo.altform-unplated_targetsize-48.png ├── Square44x44Logo.altform-unplated_targetsize-256.png ├── Square44x44Logo.altform-lightunplated_targetsize-16.png ├── Square44x44Logo.altform-lightunplated_targetsize-24.png ├── Square44x44Logo.altform-lightunplated_targetsize-32.png ├── Square44x44Logo.altform-lightunplated_targetsize-48.png └── Square44x44Logo.altform-lightunplated_targetsize-256.png ├── Constants └── ApiConstants.cs ├── Enums └── SupportedTranslationServices.cs ├── Models ├── DbObject.cs ├── Libre │ ├── LibreSupportedLanguageItem.cs │ ├── LibreDetectedLanguageInfo.cs │ ├── LibreTranslationResponse.cs │ └── LibreTranslationInfo.cs ├── TranslationHistory.cs ├── LanguageInfo.cs └── NavigationItem.cs ├── Helpers └── BindingHelpers.cs ├── privacy-policy.md ├── JsonSerializationContext.cs ├── README.md ├── Services ├── IRepositoryService.cs ├── ITranslationService.cs └── Impl │ ├── RepositoryService.cs │ ├── LibreTranslateService.cs │ ├── SettingsService.cs │ └── GoogleTranslateService.cs ├── Converters ├── EmptyStringToVisibilityConverter.cs ├── IntToVisibilityConverter.cs ├── LocaleToLanguageLabelConverter.cs └── ObjectToStringConverter.cs ├── LoadingPage.xaml ├── UserControls ├── NavigationItemControl.xaml.cs ├── TranslationItemControl.xaml.cs ├── NavigationItemControl.xaml └── TranslationItemControl.xaml ├── LICENSE ├── Properties ├── AssemblyInfo.cs └── Default.rd.xml ├── Pages ├── SettingsPage.xaml.cs ├── HistoryPage.xaml.cs ├── HistoryPage.xaml ├── HomePage.xaml.cs ├── SettingsPage.xaml └── HomePage.xaml ├── ViewModels ├── HistoryPageViewModel.cs └── HomePageViewModel.cs ├── App.xaml ├── Package.appxmanifest ├── LoadingPage.xaml.cs ├── MainPage.xaml.cs ├── .gitattributes ├── WordWeaver.sln ├── MainPage.xaml ├── App.xaml.cs ├── .gitignore └── WordWeaver.csproj /Assets/AppIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/AppIcon.png -------------------------------------------------------------------------------- /Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/StoreLogo.png -------------------------------------------------------------------------------- /Assets/SettingsIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SettingsIcons.ttf -------------------------------------------------------------------------------- /Assets/LargeTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/LargeTile.scale-100.png -------------------------------------------------------------------------------- /Assets/LargeTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/LargeTile.scale-125.png -------------------------------------------------------------------------------- /Assets/LargeTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/LargeTile.scale-150.png -------------------------------------------------------------------------------- /Assets/LargeTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/LargeTile.scale-200.png -------------------------------------------------------------------------------- /Assets/LargeTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/LargeTile.scale-400.png -------------------------------------------------------------------------------- /Assets/SmallTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SmallTile.scale-100.png -------------------------------------------------------------------------------- /Assets/SmallTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SmallTile.scale-125.png -------------------------------------------------------------------------------- /Assets/SmallTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SmallTile.scale-150.png -------------------------------------------------------------------------------- /Assets/SmallTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SmallTile.scale-200.png -------------------------------------------------------------------------------- /Assets/SmallTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SmallTile.scale-400.png -------------------------------------------------------------------------------- /Assets/StoreLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/StoreLogo.scale-100.png -------------------------------------------------------------------------------- /Assets/StoreLogo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/StoreLogo.scale-125.png -------------------------------------------------------------------------------- /Assets/StoreLogo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/StoreLogo.scale-150.png -------------------------------------------------------------------------------- /Assets/StoreLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/StoreLogo.scale-200.png -------------------------------------------------------------------------------- /Assets/StoreLogo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/StoreLogo.scale-400.png -------------------------------------------------------------------------------- /Assets/SplashScreen.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SplashScreen.scale-100.png -------------------------------------------------------------------------------- /Assets/SplashScreen.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SplashScreen.scale-125.png -------------------------------------------------------------------------------- /Assets/SplashScreen.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SplashScreen.scale-150.png -------------------------------------------------------------------------------- /Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /Assets/SplashScreen.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/SplashScreen.scale-400.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.scale-100.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.scale-125.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.scale-150.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.scale-400.png -------------------------------------------------------------------------------- /Assets/Wide310x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Wide310x150Logo.scale-100.png -------------------------------------------------------------------------------- /Assets/Wide310x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Wide310x150Logo.scale-125.png -------------------------------------------------------------------------------- /Assets/Wide310x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Wide310x150Logo.scale-150.png -------------------------------------------------------------------------------- /Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /Assets/Wide310x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Wide310x150Logo.scale-400.png -------------------------------------------------------------------------------- /Assets/Square150x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square150x150Logo.scale-100.png -------------------------------------------------------------------------------- /Assets/Square150x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square150x150Logo.scale-125.png -------------------------------------------------------------------------------- /Assets/Square150x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square150x150Logo.scale-150.png -------------------------------------------------------------------------------- /Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /Assets/Square150x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square150x150Logo.scale-400.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.targetsize-16.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.targetsize-24.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.targetsize-256.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.targetsize-32.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.targetsize-48.png -------------------------------------------------------------------------------- /Constants/ApiConstants.cs: -------------------------------------------------------------------------------- 1 | namespace WordWeaver.Constants; 2 | 3 | public static class ApiConstants 4 | { 5 | public const string AppCenterSecret = ""; 6 | } 7 | -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-unplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-unplated_targetsize-16.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-unplated_targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-unplated_targetsize-24.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-unplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-unplated_targetsize-32.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-unplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-unplated_targetsize-48.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-unplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-unplated_targetsize-256.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-lightunplated_targetsize-48.png -------------------------------------------------------------------------------- /Enums/SupportedTranslationServices.cs: -------------------------------------------------------------------------------- 1 | namespace WordWeaver.Enums; 2 | 3 | public enum SupportedTranslationServices: int 4 | { 5 | LibreTranslate, 6 | GoogleTranslate 7 | } -------------------------------------------------------------------------------- /Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/itsWindows11/WordWeaver/HEAD/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png -------------------------------------------------------------------------------- /Models/DbObject.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | using System; 3 | 4 | namespace WordWeaver.Models 5 | { 6 | public class DbObject 7 | { 8 | [PrimaryKey] 9 | public Guid Id { get; set; } = Guid.NewGuid(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Models/Libre/LibreSupportedLanguageItem.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace WordWeaver.Models; 4 | 5 | public sealed class LibreSupportedLanguageItem 6 | { 7 | [JsonPropertyName("code")] 8 | public string Code { get; set; } 9 | 10 | [JsonPropertyName("name")] 11 | public string Name { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /Helpers/BindingHelpers.cs: -------------------------------------------------------------------------------- 1 | using Windows.UI.Xaml; 2 | 3 | namespace WordWeaver.Helpers; 4 | 5 | public static class BindingHelpers 6 | { 7 | public static bool InvertBoolean(bool value) 8 | => !value; 9 | 10 | public static Visibility InvertBooleanToVisibility(bool value) 11 | => !value ? Visibility.Visible : Visibility.Collapsed; 12 | } 13 | -------------------------------------------------------------------------------- /Models/Libre/LibreDetectedLanguageInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace WordWeaver.Models 4 | { 5 | public sealed class LibreDetectedLanguageInfo 6 | { 7 | [JsonPropertyName("confidence")] 8 | public double Confidence { get; set; } 9 | 10 | [JsonPropertyName("language")] 11 | public string Language { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /privacy-policy.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | We collect usage information, device model, Windows build number and crash information through [AppCenter](https://appcenter.ms), to help fix bugs and continue working on the app. 4 | 5 | We cannot identify users' profiles or other information other than the information listed above through AppCenter. 6 | 7 | An option to opt out of this tracking will be implemented soon to comply with GDPR, but crashes will be still tracked. -------------------------------------------------------------------------------- /Models/TranslationHistory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WordWeaver.Models 4 | { 5 | public sealed class TranslationHistory : DbObject 6 | { 7 | public string SourceLanguage { get; set; } 8 | 9 | public string SourceText { get; set; } 10 | 11 | public string TranslationLanguage { get; set; } 12 | 13 | public string TranslatedText { get; set; } 14 | 15 | public DateTime Date { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Models/LanguageInfo.cs: -------------------------------------------------------------------------------- 1 | namespace WordWeaver.Models 2 | { 3 | public sealed class LanguageInfo 4 | { 5 | public string Label { get; set; } 6 | 7 | public string LanguageCode { get; set; } 8 | 9 | public LanguageInfo(string label, string languageCode) 10 | { 11 | Label = label; 12 | LanguageCode = languageCode; 13 | } 14 | 15 | public override string ToString() => Label; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Models/NavigationItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WordWeaver.Models; 4 | 5 | public sealed class NavigationItem 6 | { 7 | public string Label { get; set; } 8 | 9 | public string IconGlyph { get; set; } 10 | 11 | public Type PageType { get; set; } 12 | 13 | public NavigationItem(string label, string iconGlyph, Type pageType) 14 | { 15 | Label = label; 16 | IconGlyph = iconGlyph; 17 | PageType = pageType; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Models/Libre/LibreTranslationResponse.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | 3 | using System.Text.Json.Serialization; 4 | 5 | namespace WordWeaver.Models 6 | { 7 | public sealed class LibreTranslationResponse 8 | { 9 | [JsonPropertyName("detectedLanguage")] 10 | public LibreDetectedLanguageInfo? DetectedLanguageInfo { get; set; } 11 | 12 | [JsonPropertyName("translatedText")] 13 | public string TranslatedText { get; set; } = string.Empty; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /JsonSerializationContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json.Serialization; 3 | using WordWeaver.Models; 4 | 5 | namespace WordWeaver 6 | { 7 | [JsonSerializable(typeof(LibreDetectedLanguageInfo))] 8 | [JsonSerializable(typeof(LibreTranslationResponse))] 9 | [JsonSerializable(typeof(LibreTranslationInfo))] 10 | [JsonSerializable(typeof(IList))] 11 | public sealed partial class JsonSerializationContext : JsonSerializerContext 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #

WordWeaver

2 | 3 |

WordWeaver is a translator app that helps you translate more quickly and efficiently, with an adaptive and beautiful UI.

4 | 5 | ## Current features 6 | 7 | - Translation functionality 8 | - History 9 | - Basic customizability 10 | 11 | ![apps 25475 14629675836069496 bd63ee76-1d46-46b2-a8de-7add4ecc10fb fff612de-c7de-4fef-bd8a-9512b3c9e9d9](https://github.com/itsWindows11/WordWeaver/assets/81253203/621e364c-3f31-42ab-a43e-feb776b433f8) 12 | 13 | More are coming soon in a future update. Stay tuned! 14 | -------------------------------------------------------------------------------- /Services/IRepositoryService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using WordWeaver.Models; 5 | 6 | namespace WordWeaver.Services 7 | { 8 | public interface IRepositoryService 9 | { 10 | Task> GetSavedTranslationsAsync(); 11 | 12 | Task> GetSavedTranslationsAsync(int maxCount); 13 | 14 | Task GetSavedTranslationAsync(Guid id); 15 | 16 | Task DeleteHistoryItemAsync(TranslationHistory item); 17 | 18 | Task AddSavedTranslationAsync(TranslationHistory item); 19 | 20 | Task ClearHistoryAsync(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Converters/EmptyStringToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Windows.UI.Xaml; 3 | using Windows.UI.Xaml.Data; 4 | 5 | namespace WordWeaver.Converters 6 | { 7 | public sealed class EmptyStringToVisibilityConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, string language) 10 | { 11 | var str = (string)value; 12 | return string.IsNullOrEmpty(str) ? Visibility.Collapsed : Visibility.Visible; 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, string language) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Converters/IntToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Windows.UI.Xaml; 3 | using Windows.UI.Xaml.Data; 4 | 5 | namespace WordWeaver.Converters 6 | { 7 | public sealed class IntToVisibilityConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, string language) 10 | { 11 | var val = (int)value; 12 | 13 | if (parameter == null) 14 | return val > 0 ? Visibility.Visible : Visibility.Collapsed; 15 | 16 | return val >= int.Parse((string)parameter) ? Visibility.Visible : Visibility.Collapsed; 17 | } 18 | 19 | public object ConvertBack(object value, Type targetType, object parameter, string language) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Models/Libre/LibreTranslationInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System; 3 | using System.Text.Json.Serialization; 4 | 5 | #nullable enable 6 | 7 | namespace WordWeaver.Models 8 | { 9 | public sealed class LibreTranslationInfo 10 | { 11 | [JsonPropertyName("q")] 12 | public string? Query { get; set; } 13 | 14 | [JsonPropertyName("source")] 15 | public string? Source { get; set; } 16 | 17 | [JsonPropertyName("target")] 18 | public string? Target { get; set; } 19 | 20 | [JsonPropertyName("format")] 21 | public string? Format { get; set; } 22 | 23 | [JsonPropertyName("api_key")] 24 | public string ApiKey { get; set; } = string.Empty; 25 | 26 | [JsonPropertyName("secret")] 27 | public string Secret { get; set; } = string.Empty; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Converters/LocaleToLanguageLabelConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Windows.Globalization; 3 | using Windows.UI.Xaml.Data; 4 | 5 | namespace WordWeaver.Converters 6 | { 7 | public sealed class LocaleToLanguageLabelConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, string language) 10 | { 11 | var str = (string)value; 12 | 13 | if (!string.IsNullOrEmpty(str) && str != "auto") 14 | return new Language((string)value).DisplayName; 15 | else if (str == "auto") 16 | return "Detect Language"; 17 | 18 | throw new ArgumentNullException(nameof(value)); 19 | } 20 | 21 | public object ConvertBack(object value, Type targetType, object parameter, string language) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LoadingPage.xaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Converters/ObjectToStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Windows.UI.Xaml.Data; 3 | using WordWeaver.Enums; 4 | 5 | namespace WordWeaver.Converters; 6 | 7 | public sealed class ObjectToStringConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, string language) 10 | { 11 | if (value is SupportedTranslationServices @enum) 12 | { 13 | return @enum switch 14 | { 15 | SupportedTranslationServices.LibreTranslate => "LibreTranslate", 16 | SupportedTranslationServices.GoogleTranslate => "Google Translate", 17 | _ => @enum.ToString() 18 | }; 19 | } 20 | 21 | return value?.ToString() ?? string.Empty; 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, string language) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | } -------------------------------------------------------------------------------- /UserControls/NavigationItemControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using Windows.UI.Xaml; 2 | using Windows.UI.Xaml.Controls; 3 | 4 | namespace WordWeaver.UserControls 5 | { 6 | public sealed partial class NavigationItemControl : UserControl 7 | { 8 | public static readonly DependencyProperty IconGlyphProperty = 9 | DependencyProperty.Register(nameof(IconGlyph), typeof(string), typeof(NavigationItemControl), null); 10 | 11 | public static readonly DependencyProperty LabelProperty = 12 | DependencyProperty.Register(nameof(Label), typeof(string), typeof(NavigationItemControl), null); 13 | 14 | public string IconGlyph 15 | { 16 | get => (string)GetValue(IconGlyphProperty); 17 | set => SetValue(IconGlyphProperty, value); 18 | } 19 | 20 | public string Label 21 | { 22 | get => (string)GetValue(LabelProperty); 23 | set => SetValue(LabelProperty, value); 24 | } 25 | 26 | public NavigationItemControl() 27 | { 28 | InitializeComponent(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 itsWindows11. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("WordWeaver")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("WordWeaver")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /Pages/SettingsPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.DependencyInjection; 2 | using CommunityToolkit.Mvvm.Input; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Windows.UI.Xaml; 6 | using Windows.UI.Xaml.Controls; 7 | using WordWeaver.Enums; 8 | using WordWeaver.Services; 9 | 10 | namespace WordWeaver.Pages; 11 | 12 | public sealed partial class SettingsPage : Page 13 | { 14 | private SettingsService _settingsService = Ioc.Default.GetRequiredService(); 15 | 16 | public SettingsPage() 17 | { 18 | InitializeComponent(); 19 | } 20 | 21 | [RelayCommand] 22 | private Task ClearHistoryAsync() 23 | => Ioc.Default.GetRequiredService().ClearHistoryAsync(); 24 | 25 | private void OnTranslationServiceComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) 26 | { 27 | if (_settingsService.IsLanguageSavingEnabled) 28 | { 29 | // Get back values to their defaults 30 | _settingsService.SelectedSourceLanguageCode = "auto"; 31 | _settingsService.SelectedTranslationLanguageCode = "en"; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UserControls/TranslationItemControl.xaml.cs: -------------------------------------------------------------------------------- 1 | using Windows.UI.Xaml; 2 | using Windows.UI.Xaml.Controls; 3 | using WordWeaver.Models; 4 | 5 | namespace WordWeaver.UserControls 6 | { 7 | public sealed partial class TranslationItemControl : UserControl 8 | { 9 | public TranslationHistory HistoryItem 10 | { 11 | get => (TranslationHistory)GetValue(HistoryItemProperty); 12 | set => SetValue(HistoryItemProperty, value); 13 | } 14 | 15 | public static DependencyProperty HistoryItemProperty 16 | = DependencyProperty.Register(nameof(HistoryItem), typeof(TranslationHistory), typeof(TranslationItemControl), new(null)); 17 | 18 | public TranslationItemControl() 19 | { 20 | InitializeComponent(); 21 | } 22 | 23 | private void Grid_PointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) 24 | { 25 | _ = VisualStateManager.GoToState(this, "HoveredState", false); 26 | } 27 | 28 | private void Grid_PointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) 29 | { 30 | _ = VisualStateManager.GoToState(this, "NonHoverState", false); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ViewModels/HistoryPageViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.DependencyInjection; 3 | using CommunityToolkit.Mvvm.Input; 4 | using System; 5 | using System.Collections.ObjectModel; 6 | using System.Threading.Tasks; 7 | using WordWeaver.Models; 8 | using WordWeaver.Services; 9 | 10 | namespace WordWeaver.ViewModels; 11 | 12 | public sealed partial class HistoryPageViewModel : ObservableObject 13 | { 14 | public ObservableCollection TranslationHistory { get; } = new(); 15 | 16 | [RelayCommand] 17 | public async Task GetTranslationHistoryAsync() 18 | { 19 | TranslationHistory.Clear(); 20 | 21 | foreach (var item in await Ioc.Default 22 | .GetRequiredService() 23 | .GetSavedTranslationsAsync()) 24 | { 25 | TranslationHistory.Add(item); 26 | } 27 | } 28 | 29 | [RelayCommand] 30 | public async Task ClearTranslationHistoryAsync() 31 | { 32 | await Ioc.Default.GetRequiredService().ClearHistoryAsync(); 33 | TranslationHistory.Clear(); 34 | } 35 | 36 | [RelayCommand] 37 | public async Task RemoveHistoryItemAsync(TranslationHistory history) 38 | { 39 | await Ioc.Default.GetRequiredService().DeleteHistoryItemAsync(history); 40 | TranslationHistory.Remove(history); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Services/ITranslationService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using WordWeaver.Models; 4 | 5 | namespace WordWeaver.Services 6 | { 7 | public interface ITranslationService 8 | { 9 | /// 10 | /// Gets a list of source languages supported by this translation service. 11 | /// 12 | IList SupportedSourceLanguages { get; } 13 | 14 | /// 15 | /// Gets a list of translation languages supported by this translation service. 16 | /// 17 | IList SupportedTranslationLanguages { get; } 18 | 19 | /// 20 | /// Populates and with the supported languages by the remote translation service. 21 | /// 22 | Task FetchSupportedLanguagesAsync(); 23 | 24 | /// 25 | /// Translates text from a certain language to another. 26 | /// 27 | /// The text to translate. 28 | /// The source language. 29 | /// The language that will be translated to. 30 | /// The translated string. 31 | Task TranslateAsync(string text, string fromLocale, string toLocale); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Properties/Default.rd.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /App.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | /Assets/SettingsIcons.ttf#Settings Fluent Icons 17 | 18 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pages/HistoryPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.DependencyInjection; 2 | using System.Linq; 3 | using Windows.UI.Xaml; 4 | using Windows.UI.Xaml.Controls; 5 | using WordWeaver.Models; 6 | using WordWeaver.Services; 7 | using WordWeaver.ViewModels; 8 | 9 | namespace WordWeaver.Pages; 10 | 11 | public sealed partial class HistoryPage : Page 12 | { 13 | public HistoryPageViewModel ViewModel { get; } = Ioc.Default.GetRequiredService(); 14 | 15 | private SettingsService settingsService = Ioc.Default.GetRequiredService(); 16 | 17 | public HistoryPage() 18 | { 19 | InitializeComponent(); 20 | 21 | ViewModel.GetTranslationHistoryCommand?.Execute(null); 22 | 23 | OnTranslationHistoryCollectionChanged(null, null); 24 | ViewModel.TranslationHistory.CollectionChanged += OnTranslationHistoryCollectionChanged; 25 | } 26 | 27 | private void OnTranslationHistoryCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 28 | { 29 | if (!ViewModel.TranslationHistory.Any()) 30 | VisualStateManager.GoToState(this, "NoHistoryState", false); 31 | else 32 | VisualStateManager.GoToState(this, "HistoryAvailableState", false); 33 | } 34 | 35 | private void OnPageUnloaded(object sender, RoutedEventArgs e) 36 | { 37 | ViewModel.TranslationHistory.CollectionChanged -= OnTranslationHistoryCollectionChanged; 38 | } 39 | 40 | private void MenuFlyoutItem_Click(object sender, RoutedEventArgs e) 41 | { 42 | var item = (TranslationHistory)((FrameworkElement)e.OriginalSource).DataContext; 43 | 44 | ViewModel.RemoveHistoryItemCommand?.Execute(item); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | WordWeaver 19 | itsWindows11 20 | Assets\StoreLogo.png 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /UserControls/NavigationItemControl.xaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LoadingPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.DependencyInjection; 2 | using CommunityToolkit.Mvvm.Input; 3 | using Microsoft.AppCenter.Crashes; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | using Windows.UI.Xaml; 8 | using Windows.UI.Xaml.Controls; 9 | using WordWeaver.Services; 10 | 11 | namespace WordWeaver; 12 | 13 | public sealed partial class LoadingPage : Page 14 | { 15 | private ITranslationService service = Ioc.Default.GetRequiredService(); 16 | 17 | public LoadingPage() 18 | { 19 | InitializeComponent(); 20 | CustomTitleBar.SetTitleBarForCurrentView(); 21 | } 22 | 23 | private async void OnPageLoaded(object sender, RoutedEventArgs e) 24 | { 25 | await ((RepositoryService)Ioc.Default.GetRequiredService()).InitializeAsync(); 26 | 27 | try 28 | { 29 | await service.FetchSupportedLanguagesAsync(); 30 | } catch (Exception ex) 31 | { 32 | Crashes.TrackError(ex, new Dictionary() 33 | { 34 | { "fromLoading", "true" } 35 | }); 36 | 37 | var contentDialog = new ContentDialog 38 | { 39 | Title = "Error", 40 | Content = "An error occurred while connecting to the translation service. Please check your internet connection and try again.", 41 | PrimaryButtonText = "Exit", 42 | PrimaryButtonCommand = ExitAppCommand, 43 | SecondaryButtonText = "Retry", 44 | SecondaryButtonCommand = RetryCommand 45 | }; 46 | 47 | _ = await contentDialog.ShowAsync(); 48 | 49 | return; 50 | } 51 | 52 | Frame.Navigate(typeof(MainPage)); 53 | } 54 | 55 | [RelayCommand] 56 | private void ExitApp() 57 | { 58 | Application.Current.Exit(); 59 | } 60 | 61 | [RelayCommand] 62 | private void Retry() 63 | { 64 | OnPageLoaded(null, null); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Services/Impl/RepositoryService.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using Windows.Storage; 6 | using WordWeaver.Models; 7 | 8 | namespace WordWeaver.Services; 9 | 10 | public sealed class RepositoryService : IRepositoryService 11 | { 12 | private SQLiteAsyncConnection _connection; 13 | 14 | public async Task InitializeAsync() 15 | { 16 | var file = await ApplicationData.Current.LocalFolder.CreateFileAsync("Database.db", CreationCollisionOption.OpenIfExists); 17 | _connection = new(file.Path); 18 | 19 | try 20 | { 21 | _ = await _connection.CreateTableAsync(); 22 | await _connection.EnableWriteAheadLoggingAsync(); 23 | 24 | } catch (Exception) 25 | { 26 | await _connection.CloseAsync(); 27 | _ = await ApplicationData.Current.LocalFolder.CreateFileAsync("Database.db", CreationCollisionOption.ReplaceExisting); 28 | 29 | _connection = new(file.Path); 30 | 31 | _ = await _connection.CreateTableAsync(); 32 | await _connection.EnableWriteAheadLoggingAsync(); 33 | } 34 | } 35 | 36 | public Task DeleteHistoryItemAsync(TranslationHistory item) 37 | { 38 | return _connection.DeleteAsync(item); 39 | } 40 | 41 | public Task GetSavedTranslationAsync(Guid id) 42 | { 43 | return _connection.GetAsync(id); 44 | } 45 | 46 | public async Task> GetSavedTranslationsAsync() 47 | { 48 | return await _connection.Table().ToListAsync(); 49 | } 50 | 51 | public Task AddSavedTranslationAsync(TranslationHistory item) 52 | { 53 | return _connection.InsertOrReplaceAsync(item); 54 | } 55 | 56 | public Task ClearHistoryAsync() 57 | { 58 | return _connection.Table().Where(_ => true).DeleteAsync(); 59 | } 60 | 61 | public async Task> GetSavedTranslationsAsync(int maxCount) 62 | { 63 | return await _connection.Table().Take(maxCount).ToListAsync(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Windows.UI.Xaml; 5 | using Windows.UI.Xaml.Controls; 6 | using Windows.UI.Xaml.Media.Animation; 7 | using Windows.UI.Xaml.Navigation; 8 | using WordWeaver.Models; 9 | using WordWeaver.Pages; 10 | 11 | namespace WordWeaver; 12 | 13 | public sealed partial class MainPage : Page 14 | { 15 | public IList NavigationItems { get; } = new List() 16 | { 17 | { new NavigationItem("Home", "\uE10F", typeof(HomePage)) }, 18 | { new NavigationItem("History", "\uE81C", typeof(HistoryPage)) }, 19 | { new NavigationItem("Settings", "\uE115", typeof(SettingsPage)) } 20 | }; 21 | 22 | public static readonly DependencyProperty SelectedNavigationItemIndexProperty = 23 | DependencyProperty.Register(nameof(SelectedNavigationItemIndex), typeof(int), typeof(MainPage), new PropertyMetadata(0, OnSelectedNavigationItemChanged)); 24 | 25 | public int SelectedNavigationItemIndex 26 | { 27 | get => (int)GetValue(SelectedNavigationItemIndexProperty); 28 | set => SetValue(SelectedNavigationItemIndexProperty, value); 29 | } 30 | 31 | public MainPage() 32 | { 33 | InitializeComponent(); 34 | CustomTitleBar.SetTitleBarForCurrentView(); 35 | 36 | MainFrame.Navigate(typeof(HomePage)); 37 | MainFrame.Navigated += OnMainFrameNavigated; 38 | } 39 | 40 | private void OnMainFrameNavigated(object sender, NavigationEventArgs e) 41 | { 42 | SelectedNavigationItemIndex = NavigationItems.IndexOf(NavigationItems.FirstOrDefault(n => n.PageType == e.SourcePageType)); 43 | } 44 | 45 | private static void OnSelectedNavigationItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 46 | { 47 | var page = (MainPage)d; 48 | var mainFrame = page.MainFrame; 49 | 50 | mainFrame.Navigated -= page.OnMainFrameNavigated; 51 | 52 | mainFrame.Navigate(page.NavigationItems[(int)e.NewValue].PageType, null, new SuppressNavigationTransitionInfo()); 53 | 54 | mainFrame.Navigated += page.OnMainFrameNavigated; 55 | } 56 | 57 | private void OnPageUnloaded(object sender, RoutedEventArgs e) 58 | { 59 | MainFrame.Navigated -= OnMainFrameNavigated; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /WordWeaver.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.6.33801.468 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WordWeaver", "WordWeaver.csproj", "{3B782AEA-C32C-4D57-BA48-A14C9498F5D8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|ARM = Debug|ARM 11 | Debug|ARM64 = Debug|ARM64 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|ARM = Release|ARM 15 | Release|ARM64 = Release|ARM64 16 | Release|x64 = Release|x64 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|ARM.ActiveCfg = Debug|ARM 21 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|ARM.Build.0 = Debug|ARM 22 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|ARM.Deploy.0 = Debug|ARM 23 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|ARM64.ActiveCfg = Debug|ARM64 24 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|ARM64.Build.0 = Debug|ARM64 25 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|ARM64.Deploy.0 = Debug|ARM64 26 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|x64.ActiveCfg = Debug|x64 27 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|x64.Build.0 = Debug|x64 28 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|x64.Deploy.0 = Debug|x64 29 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|x86.ActiveCfg = Debug|x86 30 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|x86.Build.0 = Debug|x86 31 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Debug|x86.Deploy.0 = Debug|x86 32 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|ARM.ActiveCfg = Release|ARM 33 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|ARM.Build.0 = Release|ARM 34 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|ARM.Deploy.0 = Release|ARM 35 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|ARM64.ActiveCfg = Release|ARM64 36 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|ARM64.Build.0 = Release|ARM64 37 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|ARM64.Deploy.0 = Release|ARM64 38 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|x64.ActiveCfg = Release|x64 39 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|x64.Build.0 = Release|x64 40 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|x64.Deploy.0 = Release|x64 41 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|x86.ActiveCfg = Release|x86 42 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|x86.Build.0 = Release|x86 43 | {3B782AEA-C32C-4D57-BA48-A14C9498F5D8}.Release|x86.Deploy.0 = Release|x86 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(ExtensibilityGlobals) = postSolution 49 | SolutionGuid = {1BA51FE2-82D5-465C-AF4A-7CD294289FA3} 50 | EndGlobalSection 51 | EndGlobal 52 | -------------------------------------------------------------------------------- /Services/Impl/LibreTranslateService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text.Json; 6 | using System.Threading.Tasks; 7 | using Windows.Storage.Streams; 8 | using Windows.Web.Http; 9 | using Windows.Web.Http.Headers; 10 | using WordWeaver.Models; 11 | 12 | namespace WordWeaver.Services; 13 | 14 | public sealed class LibreTranslateService : ITranslationService, IDisposable 15 | { 16 | private HttpClient _httpClient; 17 | 18 | public IList SupportedSourceLanguages { get; private set; } 19 | 20 | public IList SupportedTranslationLanguages { get; private set; } 21 | 22 | public async Task FetchSupportedLanguagesAsync() 23 | { 24 | _httpClient ??= new HttpClient(); 25 | 26 | using HttpRequestMessage message = new() 27 | { 28 | RequestUri = new("https://libretranslate.org/languages"), 29 | Method = HttpMethod.Get 30 | }; 31 | 32 | using var result = await _httpClient.TrySendRequestAsync(message, HttpCompletionOption.ResponseHeadersRead); 33 | 34 | using var winrtStream = await result.ResponseMessage.Content.ReadAsInputStreamAsync(); 35 | 36 | var translationResponse = await JsonSerializer.DeserializeAsync(winrtStream.AsStreamForRead(), JsonSerializationContext.Default.IListLibreSupportedLanguageItem); 37 | 38 | var items = translationResponse.Select(l => new LanguageInfo(l.Name, l.Code)).ToList(); 39 | 40 | SupportedTranslationLanguages = items; 41 | 42 | var sourceItems = items.ToList(); 43 | sourceItems.Insert(0, new LanguageInfo("Auto", "auto")); 44 | 45 | SupportedSourceLanguages = sourceItems; 46 | } 47 | 48 | public async Task TranslateAsync(string text, string fromLocale, string toLocale) 49 | { 50 | _httpClient ??= new HttpClient(); 51 | 52 | var info = new LibreTranslationInfo() 53 | { 54 | Query = text, 55 | Format = "text", 56 | Source = fromLocale, 57 | Target = toLocale 58 | }; 59 | 60 | using var httpContent = new HttpStringContent(JsonSerializer.Serialize(info), UnicodeEncoding.Utf8); 61 | httpContent.Headers.ContentType = new HttpMediaTypeHeaderValue("application/json"); 62 | 63 | using HttpRequestMessage message = new() 64 | { 65 | Content = httpContent, 66 | RequestUri = new("https://libretranslate.org/translate"), 67 | Method = HttpMethod.Post 68 | }; 69 | 70 | using var result = await _httpClient.TrySendRequestAsync(message, HttpCompletionOption.ResponseHeadersRead); 71 | 72 | var str = await result.ResponseMessage.Content.ReadAsStringAsync(); 73 | 74 | using var winrtStream = await result.ResponseMessage.Content.ReadAsInputStreamAsync(); 75 | 76 | var translationResponse = await JsonSerializer.DeserializeAsync(winrtStream.AsStreamForRead(), JsonSerializationContext.Default.LibreTranslationResponse); 77 | 78 | return translationResponse.TranslatedText; 79 | } 80 | 81 | public void Dispose() 82 | => _httpClient.Dispose(); 83 | } 84 | -------------------------------------------------------------------------------- /Services/Impl/SettingsService.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | using Windows.ApplicationModel; 6 | using Windows.Foundation.Collections; 7 | using Windows.Storage; 8 | using Windows.UI.Xaml; 9 | using WordWeaver.Enums; 10 | 11 | namespace WordWeaver.Services; 12 | 13 | public sealed partial class SettingsService : ObservableObject 14 | { 15 | private readonly IPropertySet _localSettings = ApplicationData.Current.LocalSettings.Values; 16 | 17 | public ElementTheme Theme 18 | { 19 | get => (ElementTheme)Get((int)ElementTheme.Default); 20 | set => Set((int)value); 21 | } 22 | 23 | public SupportedTranslationServices SelectedService 24 | { 25 | get => (SupportedTranslationServices)Get((int)SupportedTranslationServices.GoogleTranslate); 26 | set => Set((int)value); 27 | } 28 | 29 | public bool IsHistoryEnabled 30 | { 31 | get => Get(true); 32 | set => Set(value); 33 | } 34 | 35 | public bool IsLanguageSavingEnabled 36 | { 37 | get => Get(true); 38 | set => Set(value); 39 | } 40 | 41 | public string SelectedSourceLanguageCode 42 | { 43 | get => Get("auto"); 44 | set => Set(value); 45 | } 46 | 47 | public string SelectedTranslationLanguageCode 48 | { 49 | get => Get("en"); 50 | set => Set(value); 51 | } 52 | } 53 | 54 | public partial class SettingsService 55 | { 56 | public string AppVersion => GetAppVersion(); 57 | 58 | /// 59 | /// Gets the current app version. 60 | /// Uses on UWP or if on other .NET platforms. 61 | /// 62 | /// The current app version in the format "major.minor.build.revision". 63 | private static string GetAppVersion() 64 | { 65 | #if WINDOWS_UWP 66 | var package = Package.Current; 67 | var packageId = package.Id; 68 | var version = packageId.Version; 69 | 70 | return $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; 71 | #else 72 | var version = typeof(App).Assembly.GetName().Version; 73 | return $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}"; 74 | #endif 75 | } 76 | } 77 | 78 | public partial class SettingsService 79 | { 80 | public T Get(T defaultValue = default, [CallerMemberName] string propertyName = null) 81 | { 82 | _localSettings[propertyName] ??= defaultValue; 83 | return (T)_localSettings[propertyName]; 84 | } 85 | 86 | public void Set(T newValue, [CallerMemberName] string propertyName = null) 87 | { 88 | var value = (T)_localSettings[propertyName]; 89 | 90 | if (EqualityComparer.Default.Equals(value, newValue)) 91 | return; 92 | 93 | if (!_localSettings.ContainsKey(propertyName)) 94 | { 95 | _localSettings.Add(propertyName, value); 96 | OnPropertyChanged(propertyName); 97 | return; 98 | } 99 | 100 | _localSettings[propertyName] = newValue; 101 | OnPropertyChanged(propertyName); 102 | } 103 | } -------------------------------------------------------------------------------- /MainPage.xaml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | 38 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /UserControls/TranslationItemControl.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 41 | 42 | 43 | 48 | 49 | 50 | 54 | 55 | 56 | 61 | 62 | 63 | 68 | 69 | 70 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /Pages/HistoryPage.xaml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 43 | 44 | 45 | 46 | 50 | 51 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 84 | 85 | 86 | 87 | 88 | 94 | 95 | 96 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /App.xaml.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.DependencyInjection; 2 | using Microsoft.AppCenter; 3 | using Microsoft.AppCenter.Analytics; 4 | using Microsoft.AppCenter.Crashes; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading.Tasks; 9 | using TenMica; 10 | using Windows.ApplicationModel; 11 | using Windows.ApplicationModel.Activation; 12 | using Windows.ApplicationModel.Core; 13 | using Windows.Storage; 14 | using Windows.UI.Xaml; 15 | using Windows.UI.Xaml.Controls; 16 | using Windows.UI.Xaml.Navigation; 17 | using WordWeaver.Enums; 18 | using WordWeaver.Services; 19 | using WordWeaver.ViewModels; 20 | using WordWeaver.Constants; 21 | 22 | namespace WordWeaver; 23 | 24 | /// 25 | /// Provides application-specific behavior to supplement the default Application class. 26 | /// 27 | public sealed partial class App : Application 28 | { 29 | /// 30 | /// Initializes the singleton application object. This is the first line of authored code 31 | /// executed, and as such is the logical equivalent of main() or WinMain(). 32 | /// 33 | public App() 34 | { 35 | InitializeComponent(); 36 | Suspending += OnSuspending; 37 | 38 | #if !DEBUG 39 | AppCenter.Start(ApiConstants.AppCenterSecret, typeof(Analytics), typeof(Crashes)); 40 | 41 | UnhandledException += OnUnhandledException; 42 | TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; 43 | #endif 44 | 45 | ConfigureServices(); 46 | } 47 | 48 | public void ConfigureServices() 49 | { 50 | var serviceCollection = new ServiceCollection(); 51 | 52 | // Services 53 | if (ApplicationData.Current.LocalSettings.Values.TryGetValue(nameof(SettingsService.SelectedService), out object value) 54 | && value is int @enum 55 | && @enum == (int)SupportedTranslationServices.GoogleTranslate) 56 | { 57 | serviceCollection.AddSingleton(); 58 | } 59 | else 60 | { 61 | serviceCollection.AddSingleton(); 62 | } 63 | 64 | serviceCollection.AddSingleton(); 65 | serviceCollection.AddSingleton(); 66 | 67 | // View Models 68 | serviceCollection.AddSingleton(); 69 | serviceCollection.AddSingleton(); 70 | 71 | Ioc.Default.ConfigureServices(serviceCollection.BuildServiceProvider()); 72 | } 73 | 74 | private void OnUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) 75 | { 76 | Crashes.TrackError(e.Exception, new Dictionary() 77 | { 78 | { "source", "XamlUnhandledExceptionEvent" } 79 | }); 80 | } 81 | 82 | private void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 83 | { 84 | Crashes.TrackError(e.Exception, new Dictionary() 85 | { 86 | { "source", "TaskSchedulerUnobservedTaskException" } 87 | }); 88 | } 89 | 90 | /// 91 | /// Invoked when the application is launched normally by the end user. Other entry points 92 | /// will be used such as when the application is launched to open a specific file. 93 | /// 94 | /// Details about the launch request and process. 95 | protected override void OnLaunched(LaunchActivatedEventArgs e) 96 | { 97 | // Do not repeat app initialization when the Window already has content, 98 | // just ensure that the window is active 99 | if (Window.Current.Content is not Frame rootFrame) 100 | { 101 | // Create a Frame to act as the navigation context and navigate to the first page 102 | rootFrame = new Frame(); 103 | 104 | rootFrame.NavigationFailed += OnNavigationFailed; 105 | 106 | // Place the frame in the current Window 107 | Window.Current.Content = rootFrame; 108 | 109 | rootFrame.RequestedTheme = Ioc.Default.GetRequiredService().Theme; 110 | rootFrame.Background = new TenMicaBrush(); 111 | } 112 | 113 | if (!e.PrelaunchActivated) 114 | { 115 | CoreApplication.EnablePrelaunch(true); 116 | 117 | if (rootFrame.Content == null) 118 | { 119 | // When the navigation stack isn't restored navigate to the first page, 120 | // configuring the new page by passing required information as a navigation 121 | // parameter 122 | rootFrame.Navigate(typeof(LoadingPage), e.Arguments); 123 | } 124 | 125 | // Ensure the current window is active 126 | Window.Current.Activate(); 127 | } 128 | } 129 | 130 | /// 131 | /// Invoked when Navigation to a certain page fails 132 | /// 133 | /// The Frame which failed navigation 134 | /// Details about the navigation failure 135 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 136 | { 137 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 138 | } 139 | 140 | /// 141 | /// Invoked when application execution is being suspended. Application state is saved 142 | /// without knowing whether the application will be terminated or resumed with the contents 143 | /// of memory still intact. 144 | /// 145 | /// The source of the suspend request. 146 | /// Details about the suspend request. 147 | private void OnSuspending(object sender, SuspendingEventArgs e) 148 | { 149 | var deferral = e.SuspendingOperation.GetDeferral(); 150 | //TODO: Save application state and stop any background activity 151 | deferral.Complete(); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /ViewModels/HomePageViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.DependencyInjection; 3 | using CommunityToolkit.Mvvm.Input; 4 | using System; 5 | using System.Collections.ObjectModel; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using WordWeaver.Models; 9 | using WordWeaver.Services; 10 | 11 | namespace WordWeaver.ViewModels; 12 | 13 | public sealed partial class HomePageViewModel : ObservableObject 14 | { 15 | [ObservableProperty] 16 | private long _sourceCharCount; 17 | 18 | [ObservableProperty] 19 | private long _translationCharCount; 20 | 21 | [ObservableProperty] 22 | private string _sourceText; 23 | 24 | [ObservableProperty] 25 | private string _translatedText; 26 | 27 | [ObservableProperty] 28 | [NotifyPropertyChangedFor(nameof(SelectedSourceLanguageInfo))] 29 | [NotifyCanExecuteChangedFor(nameof(SwitchLanguagesCommand))] 30 | private int _selectedSourceLangInfoIndex = 0; 31 | 32 | [ObservableProperty] 33 | [NotifyPropertyChangedFor(nameof(SelectedTranslationLangInfo))] 34 | [NotifyCanExecuteChangedFor(nameof(SwitchLanguagesCommand))] 35 | private int _selectedTranslationLangInfoIndex = 1; 36 | 37 | private ITranslationService _translationService; 38 | private SettingsService _settingsService; 39 | 40 | public LanguageInfo SelectedSourceLanguageInfo 41 | { 42 | get => _translationService.SupportedSourceLanguages[SelectedSourceLangInfoIndex]; 43 | set => SelectedSourceLangInfoIndex = _translationService.SupportedSourceLanguages.IndexOf(value); 44 | } 45 | 46 | public LanguageInfo SelectedTranslationLangInfo 47 | { 48 | get => _translationService.SupportedTranslationLanguages[SelectedTranslationLangInfoIndex]; 49 | set => SelectedTranslationLangInfoIndex = _translationService.SupportedTranslationLanguages.IndexOf(value); 50 | } 51 | 52 | public ObservableCollection TranslationHistory { get; } = new(); 53 | 54 | public HomePageViewModel(ITranslationService service, SettingsService settingsService) 55 | { 56 | _translationService = service; 57 | _settingsService = settingsService; 58 | 59 | if (_settingsService.IsLanguageSavingEnabled) 60 | { 61 | SelectedSourceLanguageInfo = _translationService.SupportedSourceLanguages 62 | .FirstOrDefault(x => x.LanguageCode == _settingsService.SelectedSourceLanguageCode); 63 | 64 | SelectedTranslationLangInfo = _translationService.SupportedTranslationLanguages 65 | .FirstOrDefault(x => x.LanguageCode == _settingsService.SelectedTranslationLanguageCode); 66 | } 67 | } 68 | 69 | [RelayCommand] 70 | public async Task GetTranslationHistoryAsync() 71 | { 72 | TranslationHistory.Clear(); 73 | 74 | foreach (var item in await Ioc.Default 75 | .GetRequiredService() 76 | .GetSavedTranslationsAsync(5)) 77 | { 78 | TranslationHistory.Add(item); 79 | } 80 | } 81 | 82 | [RelayCommand] 83 | public async Task ClearTranslationHistoryAsync() 84 | { 85 | await Ioc.Default.GetRequiredService().ClearHistoryAsync(); 86 | TranslationHistory.Clear(); 87 | } 88 | 89 | [RelayCommand] 90 | public async Task RemoveHistoryItemAsync(TranslationHistory history) 91 | { 92 | await Ioc.Default.GetRequiredService().DeleteHistoryItemAsync(history); 93 | TranslationHistory.Remove(history); 94 | } 95 | 96 | [RelayCommand] 97 | private async Task TranslateAsync(bool shouldSaveToHistory) 98 | { 99 | if (string.IsNullOrWhiteSpace(SourceText)) return; 100 | 101 | SourceCharCount = SourceText.Length; 102 | 103 | TranslatedText = await Ioc.Default 104 | .GetRequiredService() 105 | .TranslateAsync(SourceText, 106 | SelectedSourceLanguageInfo.LanguageCode, 107 | SelectedTranslationLangInfo.LanguageCode); 108 | 109 | TranslationCharCount = TranslatedText.Length; 110 | 111 | if (!shouldSaveToHistory || !_settingsService.IsHistoryEnabled) 112 | return; 113 | 114 | var item = new TranslationHistory() 115 | { 116 | SourceText = SourceText, 117 | TranslatedText = TranslatedText, 118 | SourceLanguage = SelectedSourceLanguageInfo.LanguageCode, 119 | TranslationLanguage = SelectedTranslationLangInfo.LanguageCode, 120 | Date = DateTime.UtcNow 121 | }; 122 | 123 | if (TranslationHistory.Count < 5) 124 | TranslationHistory.Add(item); 125 | 126 | await Ioc.Default.GetRequiredService().AddSavedTranslationAsync(item); 127 | } 128 | 129 | [RelayCommand(CanExecute = nameof(IsSourceLanguageAuto))] 130 | private void SwitchLanguages(bool switchFields = false) 131 | { 132 | var previousSourceLangInfo = SelectedSourceLanguageInfo; 133 | var previousTranslationLangInfo = SelectedTranslationLangInfo; 134 | 135 | SelectedTranslationLangInfo = previousSourceLangInfo; 136 | SelectedSourceLanguageInfo = previousTranslationLangInfo; 137 | 138 | if (switchFields) 139 | { 140 | var previousTranslatedText = TranslatedText; 141 | 142 | SourceText = previousTranslatedText; 143 | TranslatedText = string.Empty; 144 | 145 | TranslateCommand.Execute(false); 146 | } 147 | } 148 | 149 | 150 | [RelayCommand] 151 | private Task SaveToHistoryAsync() 152 | { 153 | var item = new TranslationHistory() 154 | { 155 | SourceText = SourceText, 156 | TranslatedText = TranslatedText, 157 | SourceLanguage = SelectedSourceLanguageInfo.LanguageCode, 158 | TranslationLanguage = SelectedTranslationLangInfo.LanguageCode, 159 | Date = DateTime.UtcNow 160 | }; 161 | 162 | if (TranslationHistory.Count < 5) 163 | TranslationHistory.Add(item); 164 | 165 | return Ioc.Default.GetRequiredService().AddSavedTranslationAsync(item); 166 | } 167 | 168 | private bool IsSourceLanguageAuto() 169 | => SelectedSourceLanguageInfo.LanguageCode != "auto"; 170 | } 171 | -------------------------------------------------------------------------------- /Pages/HomePage.xaml.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.DependencyInjection; 2 | using CommunityToolkit.Mvvm.Input; 3 | using Microsoft.Toolkit.Uwp.UI; 4 | using System; 5 | using System.ComponentModel; 6 | using System.Linq; 7 | using Windows.ApplicationModel.DataTransfer; 8 | using Windows.System; 9 | using Windows.UI.Xaml; 10 | using Windows.UI.Xaml.Controls; 11 | using WordWeaver.Models; 12 | using WordWeaver.Services; 13 | using WordWeaver.ViewModels; 14 | 15 | namespace WordWeaver.Pages; 16 | 17 | public sealed partial class HomePage : Page 18 | { 19 | public HomePageViewModel ViewModel { get; } = Ioc.Default.GetRequiredService(); 20 | 21 | private DispatcherTimer _timer; 22 | private bool _shouldTrigger; 23 | 24 | private ITranslationService service = Ioc.Default.GetRequiredService(); 25 | private SettingsService settingsService = Ioc.Default.GetRequiredService(); 26 | 27 | public HomePage() 28 | { 29 | InitializeComponent(); 30 | 31 | _timer = new() 32 | { 33 | Interval = TimeSpan.FromMilliseconds(2000) 34 | }; 35 | 36 | ViewModel.PropertyChanging += OnViewModelPropertyChanging; 37 | ViewModel.PropertyChanged += OnViewModelPropertyChanged; 38 | 39 | ViewModel.GetTranslationHistoryCommand?.Execute(null); 40 | } 41 | 42 | private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs e) 43 | { 44 | if (e.PropertyName == nameof(ViewModel.SourceText)) 45 | SourceTextBox.Text = ViewModel.SourceText; 46 | } 47 | 48 | private void OnPageLoaded(object sender, RoutedEventArgs e) 49 | { 50 | if (settingsService.IsHistoryEnabled) 51 | { 52 | OnTranslationHistoryCollectionChanged(null, null); 53 | ViewModel.TranslationHistory.CollectionChanged += OnTranslationHistoryCollectionChanged; 54 | } 55 | 56 | if (!string.IsNullOrEmpty(ViewModel.SourceText)) 57 | { 58 | SourceTextBox.Text = ViewModel.SourceText; 59 | } 60 | 61 | _timer.Start(); 62 | _timer.Tick += OnTimerTick; 63 | } 64 | 65 | private void OnPageUnloaded(object sender, RoutedEventArgs e) 66 | { 67 | _timer.Stop(); 68 | _timer.Tick -= OnTimerTick; 69 | 70 | ViewModel.PropertyChanging -= OnViewModelPropertyChanging; 71 | ViewModel.PropertyChanged -= OnViewModelPropertyChanged; 72 | 73 | if (settingsService.IsHistoryEnabled) 74 | ViewModel.TranslationHistory.CollectionChanged -= OnTranslationHistoryCollectionChanged; 75 | } 76 | 77 | private void OnSourceComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) 78 | { 79 | if (settingsService.IsLanguageSavingEnabled) 80 | settingsService.SelectedSourceLanguageCode = ViewModel.SelectedSourceLanguageInfo.LanguageCode; 81 | } 82 | 83 | private void OnTranslationComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e) 84 | { 85 | if (settingsService.IsLanguageSavingEnabled) 86 | settingsService.SelectedTranslationLanguageCode = ViewModel.SelectedTranslationLangInfo.LanguageCode; 87 | } 88 | } 89 | 90 | // Events 91 | public partial class HomePage 92 | { 93 | private void OnTranslationHistoryCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 94 | { 95 | if (!ViewModel.TranslationHistory.Any()) 96 | VisualStateManager.GoToState(this, "NoHistoryState", false); 97 | else 98 | VisualStateManager.GoToState(this, "HistoryAvailableState", false); 99 | } 100 | 101 | private void OnViewModelPropertyChanging(object sender, PropertyChangingEventArgs e) 102 | { 103 | ViewModel.PropertyChanging -= OnViewModelPropertyChanging; 104 | 105 | if (e.PropertyName == nameof(ViewModel.SelectedSourceLangInfoIndex)) 106 | { 107 | if (TranslationComboBox.SelectedItem == SourceComboBox.SelectedItem 108 | && ViewModel.SelectedSourceLanguageInfo.LanguageCode != "auto") 109 | { 110 | ViewModel.SwitchLanguagesCommand?.Execute(false); 111 | } 112 | else if (TranslationComboBox.SelectedItem == SourceComboBox.SelectedItem) 113 | { 114 | ViewModel.SelectedTranslationLangInfoIndex = ViewModel.SelectedSourceLangInfoIndex + 2; 115 | } 116 | } else if (e.PropertyName == nameof(ViewModel.SelectedTranslationLangInfoIndex)) 117 | { 118 | if (TranslationComboBox.SelectedItem == SourceComboBox.SelectedItem 119 | && ViewModel.SelectedSourceLanguageInfo.LanguageCode != "auto") 120 | { 121 | ViewModel.SwitchLanguagesCommand?.Execute(false); 122 | } 123 | } 124 | 125 | ViewModel.PropertyChanging += OnViewModelPropertyChanging; 126 | } 127 | 128 | private void OnTimerTick(object sender, object e) 129 | { 130 | var oldText = ViewModel.SourceText; 131 | ViewModel.SourceText = SourceTextBox.Text; 132 | 133 | if (!string.IsNullOrEmpty(ViewModel.SourceText) 134 | && oldText != ViewModel.SourceText 135 | && _shouldTrigger) 136 | { 137 | _shouldTrigger = false; 138 | ViewModel.TranslateCommand?.Execute(false); 139 | } 140 | } 141 | 142 | private void OnSourceTextBoxTextChanged(object sender, TextChangedEventArgs args) 143 | { 144 | _shouldTrigger = true; 145 | } 146 | 147 | private void MenuFlyoutItem_Click(object sender, RoutedEventArgs e) 148 | { 149 | var item = (TranslationHistory)((FrameworkElement)e.OriginalSource).DataContext; 150 | 151 | ViewModel.RemoveHistoryItemCommand?.Execute(item); 152 | } 153 | } 154 | 155 | // Commands 156 | public partial class HomePage 157 | { 158 | [RelayCommand] 159 | private void OpenHistoryPage() 160 | { 161 | Frame.Navigate(typeof(HistoryPage)); 162 | } 163 | 164 | [RelayCommand] 165 | private void Copy(bool isSource = false) 166 | { 167 | var dataPackage = new DataPackage() 168 | { 169 | RequestedOperation = DataPackageOperation.Copy 170 | }; 171 | 172 | dataPackage.SetText(isSource ? ViewModel.SourceText : ViewModel.TranslatedText); 173 | 174 | Clipboard.SetContent(dataPackage); 175 | } 176 | } -------------------------------------------------------------------------------- /Pages/SettingsPage.xaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | LibreTranslate 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 38 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 69 | 70 | 75 | 76 | 86 | 87 | 88 | 95 | 96 | 97 | 104 | 105 | 106 | 121 | 122 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 147 | 148 | 149 | 156 | 157 | 158 | 164 | 165 | 166 | 167 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 183 | 184 | 190 | 191 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 224 | 225 | 235 | 236 | 237 | 241 | 242 | 248 | 249 | 250 | 251 | 252 | 255 | 256 | 257 | 258 | 259 | 265 | 266 | 267 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | --------------------------------------------------------------------------------