",
+ "",
+ ""
+ ' "$JSON" > "$HTML"
+
+ echo "Generated $HTML"
+
+ # --------------------------------------------------------
+ # Build application
+ # --------------------------------------------------------
+
+ - name: Build NebulaAuth
+ run: |
+ dotnet publish src/NebulaAuth/NebulaAuth.csproj \
+ -c Release \
+ -o build \
+ -p:EnableWindowsTargeting=true
+
+ # --------------------------------------------------------
+ # Package ZIP
+ # --------------------------------------------------------
+
+ - name: Package release
+ run: |
+ cd build
+ zip -r ../NebulaAuth.${{ steps.ver.outputs.version }}.zip *
+
+ # --------------------------------------------------------
+ # Generate SHA256 checksum
+ # --------------------------------------------------------
+
+ - name: Generate checksum
+ id: checksum
+ run: |
+ FILE="NebulaAuth.${{ steps.ver.outputs.version }}.zip"
+ HASH=$(sha256sum "$FILE" | awk '{print $1}')
+ echo "checksum=$HASH" >> $GITHUB_OUTPUT
+
+ # --------------------------------------------------------
+ # Generate update.xml
+ # --------------------------------------------------------
+
+ - name: Generate update.xml
+ run: |
+ VERSION="${{ steps.ver.outputs.version }}"
+ CHECKSUM="${{ steps.checksum.outputs.checksum }}"
+
+ cat > NebulaAuth/update.xml <
+
+ $VERSION
+ https://github.com/${{ github.repository }}/releases/download/$VERSION/NebulaAuth.$VERSION.zip
+ https://achiez.github.io/${{ github.event.repository.name }}/changelog/$VERSION.html
+ false
+ $CHECKSUM
+
+ EOF
+
+ echo "Generated update.xml"
+ cat NebulaAuth/update.xml
+
+ # --------------------------------------------------------
+ # Generate GitHub Release Notes
+ # --------------------------------------------------------
+
+ - name: Generate release notes
+ id: notes
+ run: |
+ VERSION="${{ steps.ver.outputs.version }}"
+ FILE="changelog/${VERSION}.json"
+
+ BODY=$(jq -r '
+ .changes[] |
+ "- **\(.type):** \(.text)" +
+ (if .link then " (\(.link))" else "" end)
+ ' "$FILE")
+
+ echo "body<> $GITHUB_OUTPUT
+ echo "$BODY" >> $GITHUB_OUTPUT
+ echo "EOF" >> $GITHUB_OUTPUT
+
+ # --------------------------------------------------------
+ # Commit generated files
+ # --------------------------------------------------------
+
+ - name: Commit generated files
+ run: |
+ git config user.name "github-actions"
+ git config user.email "actions@github.com"
+
+ git add NebulaAuth/update.xml
+ git add changelog/*.html
+
+ git commit -m "chore(release): ${{ steps.ver.outputs.version }}" || echo "No changes"
+ git push
+
+ # --------------------------------------------------------
+ # Create git tag
+ # --------------------------------------------------------
+
+ - name: Create tag
+ run: |
+ VERSION="${{ steps.ver.outputs.version }}"
+ git tag -a "$VERSION" -m "Release $VERSION"
+ git push origin "$VERSION"
+
+ # --------------------------------------------------------
+ # Publish GitHub Release
+ # --------------------------------------------------------
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ steps.ver.outputs.version }}
+ name: Release ${{ steps.ver.outputs.version }}
+ body: ${{ steps.notes.outputs.body }}
+ files: |
+ NebulaAuth.${{ steps.ver.outputs.version }}.zip
\ No newline at end of file
diff --git a/src/NebulaAuth/Core/DialogsController.cs b/src/NebulaAuth/Core/DialogsController.cs
index 4bb0f4c..831398a 100644
--- a/src/NebulaAuth/Core/DialogsController.cs
+++ b/src/NebulaAuth/Core/DialogsController.cs
@@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.Threading.Tasks;
+using AutoUpdaterDotNET;
using MaterialDesignThemes.Wpf;
using Microsoft.Win32;
using NebulaAuth.Model;
using NebulaAuth.Model.Entities;
+using NebulaAuth.Model.Update;
using NebulaAuth.View;
using NebulaAuth.View.Dialogs;
using NebulaAuth.ViewModel.Linker;
@@ -143,6 +145,17 @@ public static class DialogsController
await DialogHost.Show(dialog);
}
+ public static async Task ShowUpdateDialog(UpdateInfoEventArgs args, ChangelogEntry? changelog,
+ string? htmlFallbackUrl)
+ {
+ var vm = new UpdateDialogVM(args, changelog, htmlFallbackUrl);
+ var dialog = new UpdateDialog
+ {
+ DataContext = vm
+ };
+ await DialogHost.Show(dialog);
+ }
+
#region CommonDialogs
public static async Task ShowConfirmCancelDialog(string? msg = null)
diff --git a/src/NebulaAuth/Core/UpdateManager.cs b/src/NebulaAuth/Core/UpdateManager.cs
index 600f978..f6610e6 100644
--- a/src/NebulaAuth/Core/UpdateManager.cs
+++ b/src/NebulaAuth/Core/UpdateManager.cs
@@ -1,16 +1,36 @@
-using System;
+using AutoUpdaterDotNET;
+using NebulaAuth.Model;
+using NebulaAuth.Model.Update;
+using Newtonsoft.Json;
+using System;
using System.IO;
-using AutoUpdaterDotNET;
+using System.Net.Http;
+using System.Windows;
namespace NebulaAuth.Core;
public static class UpdateManager
{
- private const string UPDATE_URL =
- "https://raw.githubusercontent.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/master/NebulaAuth/update.xml";
+ private const string BASE_URL =
+ "https://raw.githubusercontent.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/master/";
+ private const string UPDATE_URL = BASE_URL + "NebulaAuth/update.xml";
- public static void CheckForUpdates()
+ private const string CHANGELOG_BASE_URL = BASE_URL + "changelog/";
+
+ private static readonly HttpClient HttpClient = new();
+ private static bool _isManualCheck;
+
+ public static bool HasPendingUpdate { get; private set; }
+ public static event Action? PendingUpdateDetected;
+
+ static UpdateManager()
{
+ AutoUpdater.CheckForUpdateEvent += HandleCheckForUpdateEvent;
+ }
+
+ public static void CheckForUpdates(bool manual = false)
+ {
+ _isManualCheck = manual;
var jsonPath = Path.Combine(Environment.CurrentDirectory, "update-settings.json");
AutoUpdater.PersistenceProvider = new JsonFilePersistenceProvider(jsonPath);
AutoUpdater.ShowSkipButton = false;
@@ -18,6 +38,88 @@ public static class UpdateManager
AutoUpdater.Start(UPDATE_URL);
}
+ public static void SkipVersion(string version)
+ {
+ var settings = UpdateSettings.Load();
+ settings.SkipVersion(version);
+ }
+
+ public static void SetRemindAfter(TimeSpan delay)
+ {
+ var settings = UpdateSettings.Load();
+ settings.SetRemindAfter(DateTime.Now + delay);
+ }
+
+ private static async void HandleCheckForUpdateEvent(UpdateInfoEventArgs args)
+ {
+ try
+ {
+ var isManual = _isManualCheck;
+ _isManualCheck = false;
+
+ if (args.Error != null)
+ {
+ if (isManual)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ SnackbarController.SendSnackbar(LocManager.GetCommonOrDefault("Request error", "RequestError")));
+ }
+
+ return;
+ }
+
+ if (!args.IsUpdateAvailable)
+ {
+ if (isManual)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ SnackbarController.SendSnackbar(
+ LocManager.GetCodeBehindOrDefault("You are using the latest version", "UpdateService",
+ "LatestVersion")));
+ }
+
+ return;
+ }
+
+ var version = args.CurrentVersion.ToString();
+ var settings = UpdateSettings.Load();
+
+ if (!isManual && !settings.ShouldShow(version))
+ {
+ HasPendingUpdate = true;
+ Application.Current.Dispatcher.Invoke(() => PendingUpdateDetected?.Invoke());
+ return;
+ }
+
+ ChangelogEntry? changelog = null;
+ try
+ {
+ var jsonUrl = $"{CHANGELOG_BASE_URL}{version}.json";
+ var response = await HttpClient.GetAsync(jsonUrl).ConfigureAwait(false);
+ if (response.IsSuccessStatusCode)
+ {
+ var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ changelog = JsonConvert.DeserializeObject(json);
+ }
+ }
+ catch
+ {
+ // fallback to HTML changelog URL
+ }
+
+ var htmlFallbackUrl = changelog == null ? args.ChangelogURL : null;
+
+ await Application.Current.Dispatcher.BeginInvoke(async () =>
+ {
+ await DialogsController.ShowUpdateDialog(args, changelog, htmlFallbackUrl);
+ });
+ }
+ catch (Exception e)
+ {
+ Shell.Logger.Error(e, "Error while checking updates");
+ }
+ }
+
private static bool RequiresAdminAccess()
{
try
@@ -35,57 +137,4 @@ public static class UpdateManager
return true;
}
}
-
-
- //static UpdateManager()
- //{
- // //AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent;
-
- //}
-
- //private static async void AutoUpdaterOnCheckForUpdateEvent(UpdateInfoEventArgs args)
- //{
- // if (args.Error == null)
- // {
- // if (args.IsUpdateAvailable)
- // {
- // DialogResult dialogResult;
- // var dialog = new UpdaterView()
- // {
- // DataContext = new UpdaterVM(args)
- // };
-
- // await DialogHost.Show(dialog);
- // Application.Current.Shutdown();
-
- // }
- // else
- // {
-
- // }
- // }
- // else
- // {
- // if (args.Error is WebException)
- // {
-
- // }
- // else
- // {
-
- // }
- // }
-
- //}
-
- //private static void RunUpdate(UpdateInfoEventArgs args)
- //{
- // Application.Current.Dispatcher.Invoke(() =>
- // {
- // if (AutoUpdater.DownloadUpdate(args))
- // {
- // Application.Current.Shutdown();
- // }
- // }, DispatcherPriority.ContextIdle);
- //}
}
\ No newline at end of file
diff --git a/src/NebulaAuth/MainWindow.xaml b/src/NebulaAuth/MainWindow.xaml
index ab49375..6eb2983 100644
--- a/src/NebulaAuth/MainWindow.xaml
+++ b/src/NebulaAuth/MainWindow.xaml
@@ -519,16 +519,32 @@
Text="{Binding SelectedMafile.AccountName, TargetNullValue=' ', FallbackValue=' ', Mode=OneWay}" />
-
-
+
+
+
+
+
+
+
- by achies
-
-
+
+ Command="{Binding OpenLinksViewCommand}">
+
+
+
+ by achies
+
+
+
diff --git a/src/NebulaAuth/MainWindow.xaml.cs b/src/NebulaAuth/MainWindow.xaml.cs
index 760919d..5796f0a 100644
--- a/src/NebulaAuth/MainWindow.xaml.cs
+++ b/src/NebulaAuth/MainWindow.xaml.cs
@@ -1,16 +1,18 @@
-using System;
-using System.Reflection;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using System.Windows.Threading;
-using MaterialDesignThemes.Wpf;
+using MaterialDesignThemes.Wpf;
using NebulaAuth.Core;
using NebulaAuth.Model;
using NebulaAuth.View.Dialogs;
using NebulaAuth.ViewModel;
using NebulaAuth.ViewModel.Other;
+using System;
+using System.Reflection;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.Windows.Threading;
namespace NebulaAuth;
@@ -25,6 +27,43 @@ public partial class MainWindow
ThemeManager.InitializeTheme();
Title = Title + " " + Assembly.GetExecutingAssembly().GetName().Version?.ToString(3);
Loaded += OnApplicationStarted;
+ UpdateManager.PendingUpdateDetected += OnPendingUpdateDetected;
+ }
+
+ private void OnPendingUpdateDetected()
+ {
+ Dispatcher.BeginInvoke(StartUpdateBlink);
+ }
+
+ private void StartUpdateBlink()
+ {
+ UpdateBadgeIcon.Visibility = Visibility.Visible;
+ var baseColor = FindResource("AccentBrush") is SolidColorBrush accent
+ ? accent.Color
+ : ByAchiesBrush.Color;
+ var duration = new Duration(TimeSpan.FromSeconds(6));
+ var sb = new Storyboard();
+ sb.Children.Add(BuildBlinkAnimation(duration, "UpdateBadgeBrush"));
+ sb.Children.Add(BuildBlinkAnimation(duration, "ByAchiesBrush"));
+ sb.Completed += (_, _) =>
+ {
+ UpdateBadgeIcon.Visibility = Visibility.Collapsed;
+ ByAchiesBrush.Color = baseColor;
+ };
+ sb.Begin(this);
+ }
+
+ private static ColorAnimationUsingKeyFrames BuildBlinkAnimation(Duration duration, string targetName)
+ {
+ var anim = new ColorAnimationUsingKeyFrames { Duration = duration };
+ for (var i = 0; i <= 6; i++)
+ {
+ var color = i % 2 == 0 ? Colors.DodgerBlue : Colors.OrangeRed;
+ anim.KeyFrames.Add(new DiscreteColorKeyFrame(color, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(i))));
+ }
+ Storyboard.SetTargetName(anim, targetName);
+ Storyboard.SetTargetProperty(anim, new PropertyPath(SolidColorBrush.ColorProperty));
+ return anim;
}
private async void OnApplicationStarted(object? sender, EventArgs e)
diff --git a/src/NebulaAuth/Model/Update/ChangelogEntry.cs b/src/NebulaAuth/Model/Update/ChangelogEntry.cs
new file mode 100644
index 0000000..6d494eb
--- /dev/null
+++ b/src/NebulaAuth/Model/Update/ChangelogEntry.cs
@@ -0,0 +1,28 @@
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace NebulaAuth.Model.Update;
+
+public class ChangelogEntry
+{
+ [JsonProperty("version")]
+ public string Version { get; set; } = string.Empty;
+
+ [JsonProperty("date")]
+ public string Date { get; set; } = string.Empty;
+
+ [JsonProperty("changes")]
+ public List Changes { get; set; } = new();
+}
+
+public class ChangeItem
+{
+ [JsonProperty("type")]
+ public string Type { get; set; } = string.Empty;
+
+ [JsonProperty("text")]
+ public string Text { get; set; } = string.Empty;
+
+ [JsonProperty("link")]
+ public string? Link { get; set; }
+}
diff --git a/src/NebulaAuth/Model/Update/UpdateSettings.cs b/src/NebulaAuth/Model/Update/UpdateSettings.cs
new file mode 100644
index 0000000..b025d74
--- /dev/null
+++ b/src/NebulaAuth/Model/Update/UpdateSettings.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+using Newtonsoft.Json;
+
+namespace NebulaAuth.Model.Update;
+
+public class UpdateSettings
+{
+ private static readonly string FilePath = Path.Combine("settings", "update.json");
+
+ [JsonProperty("skippedVersion")]
+ public string? SkippedVersion { get; set; }
+
+ [JsonProperty("remindAfter")]
+ public DateTime? RemindAfter { get; set; }
+
+ public static UpdateSettings Load()
+ {
+ try
+ {
+ if (!File.Exists(FilePath)) return new UpdateSettings();
+ var json = File.ReadAllText(FilePath);
+ return JsonConvert.DeserializeObject(json) ?? new UpdateSettings();
+ }
+ catch
+ {
+ return new UpdateSettings();
+ }
+ }
+
+ public void Save()
+ {
+ var dir = Path.GetDirectoryName(FilePath)!;
+ if (!string.IsNullOrEmpty(dir))
+ Directory.CreateDirectory(dir);
+ var json = JsonConvert.SerializeObject(this, Formatting.Indented);
+ File.WriteAllText(FilePath, json);
+ }
+
+ public void SkipVersion(string version)
+ {
+ SkippedVersion = version;
+ RemindAfter = null;
+ Save();
+ }
+
+ public void SetRemindAfter(DateTime remindAfter)
+ {
+ RemindAfter = remindAfter;
+ Save();
+ }
+
+ public bool ShouldShow(string version)
+ {
+ if (SkippedVersion == version) return false;
+ if (RemindAfter.HasValue && DateTime.Now < RemindAfter.Value) return false;
+ return true;
+ }
+}
diff --git a/src/NebulaAuth/View/Dialogs/UpdateDialog.xaml b/src/NebulaAuth/View/Dialogs/UpdateDialog.xaml
new file mode 100644
index 0000000..f5def78
--- /dev/null
+++ b/src/NebulaAuth/View/Dialogs/UpdateDialog.xaml
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/NebulaAuth/View/Dialogs/UpdateDialog.xaml.cs b/src/NebulaAuth/View/Dialogs/UpdateDialog.xaml.cs
new file mode 100644
index 0000000..51bac8f
--- /dev/null
+++ b/src/NebulaAuth/View/Dialogs/UpdateDialog.xaml.cs
@@ -0,0 +1,9 @@
+namespace NebulaAuth.View.Dialogs;
+
+public partial class UpdateDialog
+{
+ public UpdateDialog()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/NebulaAuth/View/LinksView.xaml b/src/NebulaAuth/View/LinksView.xaml
index 47bd926..f859483 100644
--- a/src/NebulaAuth/View/LinksView.xaml
+++ b/src/NebulaAuth/View/LinksView.xaml
@@ -58,7 +58,7 @@
+
+
diff --git a/src/NebulaAuth/View/LinksView.xaml.cs b/src/NebulaAuth/View/LinksView.xaml.cs
index 1660a89..2f37767 100644
--- a/src/NebulaAuth/View/LinksView.xaml.cs
+++ b/src/NebulaAuth/View/LinksView.xaml.cs
@@ -1,4 +1,6 @@
-using System.Diagnostics;
+using MaterialDesignThemes.Wpf;
+using NebulaAuth.Core;
+using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
@@ -31,4 +33,10 @@ public partial class LinksView : UserControl
{
Process.Start(new ProcessStartInfo("https://yourwebsite.com") {UseShellExecute = true});
}
+
+ private void CheckForUpdates_Click(object sender, RoutedEventArgs e)
+ {
+ DialogHost.Close(null);
+ UpdateManager.CheckForUpdates(manual: true);
+ }
}
\ No newline at end of file
diff --git a/src/NebulaAuth/View/UpdaterView.xaml b/src/NebulaAuth/View/UpdaterView.xaml
deleted file mode 100644
index 7340a81..0000000
--- a/src/NebulaAuth/View/UpdaterView.xaml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/NebulaAuth/View/UpdaterView.xaml.cs b/src/NebulaAuth/View/UpdaterView.xaml.cs
deleted file mode 100644
index 859e234..0000000
--- a/src/NebulaAuth/View/UpdaterView.xaml.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace NebulaAuth.View;
-
-public partial class UpdaterView
-{
- public UpdaterView()
- {
- InitializeComponent();
- }
-}
\ No newline at end of file
diff --git a/src/NebulaAuth/ViewModel/MainVM.cs b/src/NebulaAuth/ViewModel/MainVM.cs
index f451df9..164c68c 100644
--- a/src/NebulaAuth/ViewModel/MainVM.cs
+++ b/src/NebulaAuth/ViewModel/MainVM.cs
@@ -42,7 +42,7 @@ public partial class MainVM : ObservableObject
new MaProxy(kvp.Key, kvp.Value)));
Storage.MaFiles.CollectionChanged += MaFilesOnCollectionChanged;
QueryGroups();
- UpdateManager.CheckForUpdates();
+ UpdateManager.CheckForUpdates(manual: false);
}
[RelayCommand]
diff --git a/src/NebulaAuth/ViewModel/Other/UpdateDialogVM.cs b/src/NebulaAuth/ViewModel/Other/UpdateDialogVM.cs
new file mode 100644
index 0000000..eb4311a
--- /dev/null
+++ b/src/NebulaAuth/ViewModel/Other/UpdateDialogVM.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Diagnostics;
+using System.Windows;
+using AutoUpdaterDotNET;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using MaterialDesignThemes.Wpf;
+using NebulaAuth.Model.Update;
+
+namespace NebulaAuth.ViewModel.Other;
+
+public partial class UpdateDialogVM : ObservableObject
+{
+ public UpdateInfoEventArgs Args { get; }
+ public ChangelogEntry? Changelog { get; }
+ public string? HtmlFallbackUrl { get; }
+ public string Version => Args.CurrentVersion.ToString();
+ public bool HasJsonChangelog => Changelog?.Changes?.Count > 0;
+
+ [ObservableProperty] private bool _showRemindOptions;
+
+ public UpdateDialogVM(UpdateInfoEventArgs args, ChangelogEntry? changelog, string? htmlFallbackUrl)
+ {
+ Args = args;
+ Changelog = changelog;
+ HtmlFallbackUrl = htmlFallbackUrl;
+ }
+
+ [RelayCommand]
+ private void UpdateNow()
+ {
+ DialogHost.Close(null);
+ if (AutoUpdater.DownloadUpdate(Args))
+ {
+ Application.Current.Shutdown();
+ }
+ }
+
+ [RelayCommand]
+ private void ToggleRemindOptions()
+ {
+ ShowRemindOptions = !ShowRemindOptions;
+ }
+
+ [RelayCommand]
+ private void SelectRemindOption(string option)
+ {
+ var delay = option switch
+ {
+ "1H" => TimeSpan.FromHours(1),
+ "1D" => TimeSpan.FromDays(1),
+ "3D" => TimeSpan.FromDays(3),
+ "7D" => TimeSpan.FromDays(7),
+ _ => TimeSpan.FromDays(1)
+ };
+ Core.UpdateManager.SetRemindAfter(delay);
+ DialogHost.Close(null);
+ }
+
+ [RelayCommand]
+ private void SkipVersion()
+ {
+ Core.UpdateManager.SkipVersion(Version);
+ DialogHost.Close(null);
+ }
+
+ [RelayCommand]
+ private void OpenLink(string url)
+ {
+ if (string.IsNullOrEmpty(url)) return;
+ Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
+ }
+}
diff --git a/src/NebulaAuth/ViewModel/Other/UpdaterVM.cs b/src/NebulaAuth/ViewModel/Other/UpdaterVM.cs
deleted file mode 100644
index 167282c..0000000
--- a/src/NebulaAuth/ViewModel/Other/UpdaterVM.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using AutoUpdaterDotNET;
-using CommunityToolkit.Mvvm.ComponentModel;
-
-namespace NebulaAuth.ViewModel.Other;
-
-public class UpdaterVM : ObservableObject
-{
- public UpdateInfoEventArgs UpdateInfoEventArgs { get; }
-
- public UpdaterVM(UpdateInfoEventArgs args)
- {
- UpdateInfoEventArgs = args;
- }
-}
\ No newline at end of file
diff --git a/src/NebulaAuth/localization.loc.json b/src/NebulaAuth/localization.loc.json
index 339a3a4..3e254b6 100644
--- a/src/NebulaAuth/localization.loc.json
+++ b/src/NebulaAuth/localization.loc.json
@@ -694,12 +694,10 @@
},
"LegacyMafileModeHint": {
"en": "Save mafiles in compatibility format with Steam Desktop Authenticator and other applications",
- "ru":
- "Сохранять мафайлы в старом формате для совместимости со Steam Desktop Authenticator и другими приложениями",
+ "ru": "Сохранять мафайлы в старом формате для совместимости со Steam Desktop Authenticator и другими приложениями",
"zh": "以与 Steam 桌面认证器及其他应用程序兼容的格式保存 mafiles",
"ua": "Зберігати мафайли у старому форматі для сумісності зі Steam Desktop Authenticator та іншими додатками",
- "fr":
- "Enregistrer les mafiles dans un format compatible avec Steam Desktop Authenticator et d'autres applications"
+ "fr": "Enregistrer les mafiles dans un format compatible avec Steam Desktop Authenticator et d'autres applications"
},
"AllowAutoUpdate": {
"en": "Allow auto update",
@@ -730,13 +728,10 @@
"fr": "Ignorer les erreurs dues à la maintenance Steam du mardi soir dans le timer (exp.)"
},
"IgnorePatchTuesdayErrorsHint": {
- "en":
- "Ignore errors that occur every Tuesday (Wednesday night in GMT) when Steam doing technical works and auto-confirm timers turns off unnecessarily",
- "ru":
- "Игнорировать ошибки, которые возникают каждый вторник (ночь среды по GMT) когда Steam проводит технические работы и автоподтверждения отключаются без надобности",
+ "en": "Ignore errors that occur every Tuesday (Wednesday night in GMT) when Steam doing technical works and auto-confirm timers turns off unnecessarily",
+ "ru": "Игнорировать ошибки, которые возникают каждый вторник (ночь среды по GMT) когда Steam проводит технические работы и автоподтверждения отключаются без надобности",
"zh": "忽略每周三(格林尼治标准时间周三夜间)发生的错误,因为 Steam 进行技术维护时自动确认功能会被无故关闭",
- "ua":
- "Ігнорувати помилки, які виникають щосереди (ніч середи за GMT) коли Steam проводить технічні роботи і автопідтвердження вимикаються без потреби",
+ "ua": "Ігнорувати помилки, які виникають щосереди (ніч середи за GMT) коли Steam проводить технічні роботи і автопідтвердження вимикаються без потреби",
"fr": "Ignorer les erreurs dues à la maintenance Steam du mardi soir (Mercredi à minuit en GMT)"
}
},
@@ -988,15 +983,11 @@
"fr": "Mot de passe de chiffrement"
},
"DialogText": {
- "en":
- "You have previously set an encryption password for passwords in mafiles, specify it so that the application can automatically log in in case of problems with the session.\r\n(Optional)",
- "ru":
- "Ранее вы устанавливали пароль шифрования для паролей в мафайлах, укажите его, чтобы приложение могло автоматически авторизоваться в случае проблем с сессией.\r\n(Не обязательно)",
+ "en": "You have previously set an encryption password for passwords in mafiles, specify it so that the application can automatically log in in case of problems with the session.\r\n(Optional)",
+ "ru": "Ранее вы устанавливали пароль шифрования для паролей в мафайлах, укажите его, чтобы приложение могло автоматически авторизоваться в случае проблем с сессией.\r\n(Не обязательно)",
"zh": "之前您为密码文件设置了加密密码,请输入它,以便应用程序在会话出现问题时可以自动登录。\r\n(非必填)",
- "ua":
- "Раніше ви встановлювали пароль шифрування для паролів в мафайлах, вкажіть його, щоб додаток міг автоматично авторизуватися у випадку проблем з сесією.\r\n(Не обов'язково)",
- "fr":
- "Vous avez déjà défini un mot de passe de chiffrement pour les mots de passe des mafiles, spécifiez-le pour que l'appli puisse se connecter automatiquement en cas de problèmes avec la session.\r\n(Facultatif)"
+ "ua": "Раніше ви встановлювали пароль шифрування для паролів в мафайлах, вкажіть його, щоб додаток міг автоматично авторизуватися у випадку проблем з сесією.\r\n(Не обов'язково)",
+ "fr": "Vous avez déjà défini un mot de passe de chiffrement pour les mots de passe des mafiles, spécifiez-le pour que l'appli puisse se connecter automatiquement en cas de problèmes avec la session.\r\n(Facultatif)"
},
"Ok": {
"en": "Ok",
@@ -1034,6 +1025,10 @@
"zh": "文档",
"ua": "Документація",
"fr": "Documentation"
+ },
+ "CheckForUpdates": {
+ "en": "Check for updates",
+ "ru": "Проверить обновления"
}
},
"MafileMoverView": {
@@ -1233,22 +1228,16 @@
},
"IncludeSharedSecret": {
"ru": "Позволяет генерировать коды подтверждения и входить в аккаунт при наличии пароля или активной сессии.",
- "en":
- "Allows generating confirmation codes and logging into the account if a password or active session is available.",
+ "en": "Allows generating confirmation codes and logging into the account if a password or active session is available.",
"ua": "Дозволяє генерувати коди підтвердження та входити в акаунт за наявності пароля або активної сесії.",
- "fr":
- "Permet de générer des codes de confirmation et de se connecter au compte si un mot de passe ou une session active est disponible.",
+ "fr": "Permet de générer des codes de confirmation et de se connecter au compte si un mot de passe ou une session active est disponible.",
"zh": "允许生成确认代码,并在有密码或活动会话的情况下登录账户。"
},
"IncludeIdentitySecret": {
- "ru":
- "Включает identity_secret и device_id. Необходимы для отправки подтверждений и дают доступ к трейдам и торговой площадке.",
- "en":
- "Includes identity_secret and device_id. Required for confirmations and provides access to trades and the market.",
- "ua":
- "Включає identity_secret і device_id. Потрібні для надсилання підтверджень і надають доступ до трейдів та торгового майданчика.",
- "fr":
- "Inclut identity_secret et device_id. Nécessaires pour les confirmations et donnent accès aux échanges et au marché.",
+ "ru": "Включает identity_secret и device_id. Необходимы для отправки подтверждений и дают доступ к трейдам и торговой площадке.",
+ "en": "Includes identity_secret and device_id. Required for confirmations and provides access to trades and the market.",
+ "ua": "Включає identity_secret і device_id. Потрібні для надсилання підтверджень і надають доступ до трейдів та торгового майданчика.",
+ "fr": "Inclut identity_secret et device_id. Nécessaires pour les confirmations et donnent accès aux échanges et au marché.",
"zh": "包含 identity_secret 和 device_id。用于发送确认,并可访问交易和市场。"
},
"IncludeRCode": {
@@ -1259,25 +1248,17 @@
"zh": "恢复代码。在失去访问权限时可将 Steam Guard 与账户解绑。"
},
"IncludeSessionData": {
- "ru":
- "Включает данные текущей сессии. Если сессия активна и не истекла, позволяет получить доступ к аккаунту без ввода пароля с помощью mafile.",
- "en":
- "Includes current session data. If the session is active and valid, allows accessing the account without entering a password using the mafile.",
- "ua":
- "Включає дані поточної сесії. Якщо сесія активна й не прострочена, дозволяє отримати доступ до акаунта без введення пароля за допомогою mafile.",
- "fr":
- "Inclut les données de la session actuelle. Si la session est active et valide, permet d’accéder au compte sans saisir de mot de passe via le mafile.",
+ "ru": "Включает данные текущей сессии. Если сессия активна и не истекла, позволяет получить доступ к аккаунту без ввода пароля с помощью mafile.",
+ "en": "Includes current session data. If the session is active and valid, allows accessing the account without entering a password using the mafile.",
+ "ua": "Включає дані поточної сесії. Якщо сесія активна й не прострочена, дозволяє отримати доступ до акаунта без введення пароля за допомогою mafile.",
+ "fr": "Inclut les données de la session actuelle. Si la session est active et valide, permet d’accéder au compte sans saisir de mot de passe via le mafile.",
"zh": "包含当前会话数据。如果会话处于活动状态且未过期,可通过 mafile 在无需输入密码的情况下访问账户。"
},
"IncludeOtherInfo": {
- "ru":
- "Включает дополнительные необязательные поля, которые в данный момент не используются Steam, но могут применяться в будущем и содержать чувствительную информацию.",
- "en":
- "Includes additional optional fields not currently used by Steam, but potentially usable in the future and may contain sensitive information.",
- "ua":
- "Включає додаткові необов’язкові поля, які наразі не використовуються Steam, але можуть бути задіяні в майбутньому та містити чутливу інформацію.",
- "fr":
- "Inclut des champs supplémentaires optionnels actuellement non utilisés par Steam, mais susceptibles d’être exploités à l’avenir et de contenir des informations sensibles.",
+ "ru": "Включает дополнительные необязательные поля, которые в данный момент не используются Steam, но могут применяться в будущем и содержать чувствительную информацию.",
+ "en": "Includes additional optional fields not currently used by Steam, but potentially usable in the future and may contain sensitive information.",
+ "ua": "Включає додаткові необов’язкові поля, які наразі не використовуються Steam, але можуть бути задіяні в майбутньому та містити чутливу інформацію.",
+ "fr": "Inclut des champs supplémentaires optionnels actuellement non utilisés par Steam, mais susceptibles d’être exploités à l’avenir et de contenir des informations sensibles.",
"zh": "包含目前未被 Steam 使用的可选附加字段,这些字段将来可能会被使用,并可能包含敏感信息。"
},
"NebulaProxy": {
@@ -1306,8 +1287,7 @@
"ru": "Введите логины аккаунтов или SteamID (по одному на строку) для экспорта в выбранную директорию.",
"en": "Enter account logins or SteamIDs (one per line) to export to the selected directory.",
"ua": "Введіть логіни акаунтів або SteamID (по одному на рядок) для експорту у вибрану директорію.",
- "fr":
- "Entrez les identifiants de compte ou les SteamID (un par ligne) pour exporter vers le répertoire sélectionné.",
+ "fr": "Entrez les identifiants de compte ou les SteamID (un par ligne) pour exporter vers le répertoire sélectionné.",
"zh": "输入要导出到所选目录的账户登录名或 SteamID(每行一个)。"
}
},
@@ -1355,6 +1335,54 @@
"fr": "Formats pris en charge:\r\n\r\nlogin:password\r\nsteamid:password"
}
},
+ "UpdateDialog": {
+ "Title": {
+ "en": "New version available:",
+ "ru": "Доступна новая версия:"
+ },
+ "Changelog": {
+ "en": "What's new",
+ "ru": "Что нового"
+ },
+ "NoChangelog": {
+ "en": "No changelog available",
+ "ru": "Нет информации об изменениях"
+ },
+ "UpdateNow": {
+ "en": "Update now",
+ "ru": "Обновить сейчас"
+ },
+ "RemindLater": {
+ "en": "Remind later",
+ "ru": "Напомнить позже"
+ },
+ "SkipVersion": {
+ "en": "Skip version",
+ "ru": "Пропустить версию"
+ },
+ "RemindAfter": {
+ "en": "Remind after:",
+ "ru": "Напомнить через:"
+ },
+ "RemindOptions": {
+ "1Hour": {
+ "en": "1 hour",
+ "ru": "1 час"
+ },
+ "1Day": {
+ "en": "1 day",
+ "ru": "1 день"
+ },
+ "3Days": {
+ "en": "3 days",
+ "ru": "3 дня"
+ },
+ "7Days": {
+ "en": "7 days",
+ "ru": "7 дней"
+ }
+ }
+ },
"CodeBehind": {
"MainVM": {
"SuccessfulLogin": {
@@ -1379,15 +1407,11 @@
"fr": "RCode manquant"
},
"ConfirmRemovingAuthenticator": {
- "ru":
- "Вы точно уверены, что хотите удалить аутентификатор?\r\nПосле этого вы не сможете обмениваться, использовать торговую площадку 15 дней. Мафайл будет удалён навсегда",
- "en":
- "Are you sure you want to remove the authenticator?\r\nAfter that, you will not be able to trade or use the marketplace for 15 days. Mafile will be deleted permanently",
+ "ru": "Вы точно уверены, что хотите удалить аутентификатор?\r\nПосле этого вы не сможете обмениваться, использовать торговую площадку 15 дней. Мафайл будет удалён навсегда",
+ "en": "Are you sure you want to remove the authenticator?\r\nAfter that, you will not be able to trade or use the marketplace for 15 days. Mafile will be deleted permanently",
"zh": "您确定要移除身份验证器吗?\r\n之后,您将在15天内无法进行交易或使用市场。Mafile 将被永久删除。",
- "ua":
- "Ви точно впевнені, що хочете видалити автентифікатор?\r\nПісля цього ви не зможете обмінюватися, використовувати торгову площадку 15 днів. Мафайл буде видалено назавжди",
- "fr":
- "Êtes-vous sûr de vouloir supprimer l'authenticateur?\r\nAprès cela, vous ne pourrez pas échanger ou utiliser le marché pendant 15 jours. Le mafile sera supprimé définitivement"
+ "ua": "Ви точно впевнені, що хочете видалити автентифікатор?\r\nПісля цього ви не зможете обмінюватися, використовувати торгову площадку 15 днів. Мафайл буде видалено назавжди",
+ "fr": "Êtes-vous sûr de vouloir supprimer l'authenticateur?\r\nAprès cela, vous ne pourrez pas échanger ou utiliser le marché pendant 15 jours. Le mafile sera supprimé définitivement"
},
"AuthenticatorRemoved": {
"ru": "Аутентификатор удален",
@@ -1499,15 +1523,11 @@
"fr": "erreurs:"
},
"RemoveMafileConfirmation": {
- "ru":
- "Удалить mafile?\r\nМафайл будет перемещен в папку 'mafiles_removed'\r\nЭто действие НЕ удаляет Steam Guard с аккаунта",
- "en":
- "Remove mafile?\r\nMafile will be moved to 'mafiles_removed' directory\r\nThis action will NOT remove the Steam Guard from the account",
+ "ru": "Удалить mafile?\r\nМафайл будет перемещен в папку 'mafiles_removed'\r\nЭто действие НЕ удаляет Steam Guard с аккаунта",
+ "en": "Remove mafile?\r\nMafile will be moved to 'mafiles_removed' directory\r\nThis action will NOT remove the Steam Guard from the account",
"zh": "是否删除mafile?\r\nMafile将被移动到“mafiles_removed”目录\r\n此操作不会从账户中移除令牌",
- "ua":
- "Видалити mafile?\r\nМафайл буде переміщено у каталог 'mafiles_removed'\r\nЦя дія НЕ видаляє автентифікатор з облікового запису",
- "fr":
- "Supprimer le mafile?\r\nLe mafile sera déplacé dans le dossier 'mafiles_removed'\r\nCette action NE SUPPRIME PAS le Steam Guard du compte"
+ "ua": "Видалити mafile?\r\nМафайл буде переміщено у каталог 'mafiles_removed'\r\nЦя дія НЕ видаляє автентифікатор з облікового запису",
+ "fr": "Supprimer le mafile?\r\nLe mafile sera déplacé dans le dossier 'mafiles_removed'\r\nCette action NE SUPPRIME PAS le Steam Guard du compte"
},
"CantRemoveAlreadyExist": {
"ru": "Не удалось переместить mafile, возможно в папке уже существует мафайл с таким именем.",
@@ -1542,8 +1562,7 @@
"en": "Can't retrieve SteamID from mafile to save changes. Need to relogin. Canceled",
"zh": "无法从配置文件获取 SteamID 并保存更改。需要重新登录。已取消",
"ua": "Не вдалося отримати SteamID з мафайла та зберегти зміни. Потрібно зробити релогін. Скасовано",
- "fr":
- "Impossible de récupérer le SteamID du mafile pour sauvegarder les modifications. Reconnexion nécessaire. Annulé"
+ "fr": "Impossible de récupérer le SteamID du mafile pour sauvegarder les modifications. Reconnexion nécessaire. Annulé"
},
"LoginCopied": {
"en": "Login copied",
@@ -1582,11 +1601,9 @@
},
"CantDecryptPassword": {
"en": "Can't decrypt password. Maybe password from this mafile was encrypted with another encryption password",
- "ru":
- "Не удалось расшифровать пароль. Возможно пароль из этого мафайла был зашифрован другим паролем шифрования",
+ "ru": "Не удалось расшифровать пароль. Возможно пароль из этого мафайла был зашифрован другим паролем шифрования",
"zh": "无法解密密码。可能此文件中的密码是用其他加密密码加密的。",
- "ua":
- "Не вдалося розшифрувати пароль. Можливо пароль з цього мафайла був зашифрований іншим паролем шифрування",
+ "ua": "Не вдалося розшифрувати пароль. Можливо пароль з цього мафайла був зашифрований іншим паролем шифрування",
"fr": "Impossible de déchiffrer le mot de passe. Le mot de passe de chiffrement est peut-être différent"
},
"CreateGroupTitle": {
@@ -1941,13 +1958,10 @@
},
"InvalidStateWithStatus2": {
"ru": "Неверное состояние (InvalidState) со статусом 2. Откройте ссылку на руководство сверху этого окна.",
- "en":
- "Invalid state (InvalidState) with status 2. Open link to the troubleshooting guide at the top of this window.",
+ "en": "Invalid state (InvalidState) with status 2. Open link to the troubleshooting guide at the top of this window.",
"zh": "状态无效(InvalidState),状态码为 2. 请打开本窗口顶部的故障排除指南链接。",
- "ua":
- "Невірний стан (InvalidState) зі статусом 2. Відкрийте посилання на посібник з усунення несправностей у верхній частині цього вікна.",
- "fr":
- "État invalide (InvalidState) avec le statut 2. Ouvrez le lien vers le guide de dépannage en haut de cette fenêtre."
+ "ua": "Невірний стан (InvalidState) зі статусом 2. Відкрийте посилання на посібник з усунення несправностей у верхній частині цього вікна.",
+ "fr": "État invalide (InvalidState) avec le statut 2. Ouvrez le lien vers le guide de dépannage en haut de cette fenêtre."
},
"GeneralFailure": {
"ru": "General Failure",
@@ -2013,8 +2027,7 @@
"ru": "Ошибка: Был запрошен вход с помощью Steam Guard. На аккаунте уже активирован мобильный аутентификатор",
"en": "Error: Login with Steam Guard was requested. Mobile authenticator is already activated on the account",
"zh": "错误:请求使用令牌登录。该账户已激活移动验证器",
- "ua":
- "Помилка: Був запрошений вхід за допомогою Steam Guard. На акаунті вже активовано мобільний автентифікатор",
+ "ua": "Помилка: Був запрошений вхід за допомогою Steam Guard. На акаунті вже активовано мобільний автентифікатор",
"fr": "Erreur: Connexion avec Steam Guard demandée. Authenticateur mobile est déjà activé sur le compte"
},
"Unknown": {
@@ -2153,15 +2166,11 @@
"fr": "Un email a été envoyé. Ouvrez le lien de l'email, puis cliquez sur 'Continuer'"
},
"ClickOnEmailLinkRetry": {
- "ru":
- "Не удалось подтвердить переход по ссылке. Убедитесь, что вы открыли ссылку из письма, затем нажмите — 'Продолжить'",
- "en":
- "We couldn’t verify that you clicked the link. Make sure you opened the link from the email, then click 'Proceed'",
+ "ru": "Не удалось подтвердить переход по ссылке. Убедитесь, что вы открыли ссылку из письма, затем нажмите — 'Продолжить'",
+ "en": "We couldn’t verify that you clicked the link. Make sure you opened the link from the email, then click 'Proceed'",
"zh": "无法确认通过链接的跳转。请确保您是从邮件中打开的链接,然后点击——“继续”",
- "ua":
- "Не вдалося підтвердити перехід за посиланням. Переконайтесь, що ви відкрили посилання з листа, а потім натисніть — 'Продовжити'",
- "fr":
- "Nous n'avons pas pu vérifier que vous avez cliqué sur le lien. Assurez-vous d'avoir ouvert le lien de l'email, puis cliquez sur 'Continuer'"
+ "ua": "Не вдалося підтвердити перехід за посиланням. Переконайтесь, що ви відкрили посилання з листа, а потім натисніть — 'Продовжити'",
+ "fr": "Nous n'avons pas pu vérifier que vous avez cliqué sur le lien. Assurez-vous d'avoir ouvert le lien de l'email, puis cliquez sur 'Continuer'"
},
"PhoneHint": {
"ru": "На телефон {0} была отправлена СМС",
@@ -2171,15 +2180,11 @@
"fr": "SMS envoyé au téléphone {0}"
},
"EnterPhoneNumber": {
- "ru":
- "Введите номер в международном формате, только цифры (без '+', пробелов и скобок), или оставьте поле пустым, чтобы привязать без номера",
- "en":
- "Enter number in international format, digits only (no '+', spaces or brackets), or leave the field empty to link without a number",
+ "ru": "Введите номер в международном формате, только цифры (без '+', пробелов и скобок), или оставьте поле пустым, чтобы привязать без номера",
+ "en": "Enter number in international format, digits only (no '+', spaces or brackets), or leave the field empty to link without a number",
"zh": "请输入国际格式的号码,仅数字(不含空格和括号),或留空以绑定无号码",
- "ua":
- "Введіть номер у міжнародному форматі, лише цифри (без '+', пробілів і дужок), або залиште поле порожнім, щоб прив’язати без номера",
- "fr":
- "Entrez un numéro au format international, uniquement des chiffres (sans '+', espaces ou parenthèses), ou laissez le champ vide pour lier sans numéro"
+ "ua": "Введіть номер у міжнародному форматі, лише цифри (без '+', пробілів і дужок), або залиште поле порожнім, щоб прив’язати без номера",
+ "fr": "Entrez un numéro au format international, uniquement des chiffres (sans '+', espaces ou parenthèses), ou laissez le champ vide pour lier sans numéro"
}
},
"MafileMoverVM": {
@@ -2212,12 +2217,10 @@
"fr": "RCode: {0}\r\nNom du fichier: {1}.mafile"
},
"GuardIsNotActive": {
- "ru":
- "Steam Guard установлен на данном аккаунте. Чтобы привязать mafile к аккаунту, воспользуйтесь окном \"Привязать\"",
+ "ru": "Steam Guard установлен на данном аккаунте. Чтобы привязать mafile к аккаунту, воспользуйтесь окном \"Привязать\"",
"en": "Steam Guard is set on this account. To link mafile to the account, use the 'Link' window",
"zh": "此账户已启用令牌。要将 mafile 绑定到此账户,请使用“绑定”窗口。",
- "ua":
- "Steam Guard встановлено на цьому акаунті. Щоб прив'язати mafile до акаунту, скористайтеся вікном 'Прив'язати'",
+ "ua": "Steam Guard встановлено на цьому акаунті. Щоб прив'язати mafile до акаунту, скористайтеся вікном 'Прив'язати'",
"fr": "Steam Guard est configuré sur ce compte. Pour lier mafile au compte, utilisez la fenêtre 'Lien'"
},
"SeemsNoPhoneNumber": {
@@ -2265,15 +2268,11 @@
"fr": "Auto "
},
"TimerPreventedOverlap": {
- "ru":
- "Авто: таймер подтверждений пытался начать новый цикл, когда предыдущий еще не завершился. Советуем увеличить задержку",
- "en":
- "Auto: confirmation timer tried to start new cycle when previous one was not finished yet. We recommend to increase delay",
+ "ru": "Авто: таймер подтверждений пытался начать новый цикл, когда предыдущий еще не завершился. Советуем увеличить задержку",
+ "en": "Auto: confirmation timer tried to start new cycle when previous one was not finished yet. We recommend to increase delay",
"zh": "自动: 确认计时器在前一个周期尚未完成时尝试启动新周期。我们建议增加延迟。",
- "ua":
- "Авто: таймер підтверджень намагався запустити новий цикл, коли попередній ще не закінчився. Рекомендуємо збільшити затримку",
- "fr":
- "Auto: le timer de confirmation a tenté de démarrer un nouveau cycle alors que le précédent n'était pas encore terminé. Nous recommandons d'augmenter le délai"
+ "ua": "Авто: таймер підтверджень намагався запустити новий цикл, коли попередній ще не закінчився. Рекомендуємо збільшити затримку",
+ "fr": "Auto: le timer de confirmation a tenté de démarrer un nouveau cycle alors que le précédent n'était pas encore terminé. Nous recommandons d'augmenter le délai"
},
"FailedToLoadStorage": {
"en": "Failed to load auto-confirm timers. File seems to be corrupted",
@@ -2309,15 +2308,11 @@
"fr": "Tous les mafiles ont été renommés avec succès ({0}).\r\nUn backup a été créé: {1}"
},
"PartialSuccess": {
- "en":
- "Total mafiles: {0}\r\nRenamed: {1}\r\nConflicts: {2}\r\nErrors: {3}\r\n\r\nBackup of mafiles saved in file: {4}",
- "ru":
- "Всего мафайлов: {0}\r\nПереименовано: {1}\r\nКонфликты: {2}\r\nОшибок: {3}\r\n\r\nРезервная копия мафайлов сохранена в файле: {4}",
+ "en": "Total mafiles: {0}\r\nRenamed: {1}\r\nConflicts: {2}\r\nErrors: {3}\r\n\r\nBackup of mafiles saved in file: {4}",
+ "ru": "Всего мафайлов: {0}\r\nПереименовано: {1}\r\nКонфликты: {2}\r\nОшибок: {3}\r\n\r\nРезервная копия мафайлов сохранена в файле: {4}",
"zh": "总文件数: {0}\r\n重命名: {1}\r\n冲突: {2}\r\n错误: {3}\r\n\r\n文件备份已保存至: {4}",
- "ua":
- "Всього мафайлів: {0}\r\nПерейменовано: {1}\r\nКонфлікти: {2}\r\nПомилок: {3}\r\n\r\nРезервна копія мафайлів збережена у файлі: {4}",
- "fr":
- "Total des mafiles: {0}\r\nRenommés: {1}\r\nConflits: {2}\r\nErreurs: {3}\r\n\r\nBackup des mafiles enregistré dans le fichier: {4}"
+ "ua": "Всього мафайлів: {0}\r\nПерейменовано: {1}\r\nКонфлікти: {2}\r\nПомилок: {3}\r\n\r\nРезервна копія мафайлів збережена у файлі: {4}",
+ "fr": "Total des mafiles: {0}\r\nRenommés: {1}\r\nConflits: {2}\r\nErreurs: {3}\r\n\r\nBackup des mafiles enregistré dans le fichier: {4}"
}
}
},
@@ -2364,22 +2359,17 @@
"zh": "指定的导出路径无效。"
},
"NebulaUtilizedDirectory": {
- "ru":
- "Невозможно выполнить экспорт в папку, которая используется приложением. Рекомендуется выбрать отдельную директорию.",
- "en":
- "Cannot export to a directory that is currently used by the application. Please choose a separate folder.",
- "ua":
- "Неможливо виконати експорт у папку, яка використовується застосунком. Рекомендується вибрати окрему директорію.",
+ "ru": "Невозможно выполнить экспорт в папку, которая используется приложением. Рекомендуется выбрать отдельную директорию.",
+ "en": "Cannot export to a directory that is currently used by the application. Please choose a separate folder.",
+ "ua": "Неможливо виконати експорт у папку, яка використовується застосунком. Рекомендується вибрати окрему директорію.",
"fr": "Impossible d’exporter vers un dossier utilisé par l’application. Veuillez choisir un dossier distinct.",
"zh": "无法导出到应用程序正在使用的文件夹。请选择一个单独的目录。"
},
"AccessDenied": {
"ru": "Недостаточно прав для записи в указанную папку. Проверьте права доступа или выберите другой путь.",
- "en":
- "Insufficient permissions to write to the specified directory. Please check access rights or choose another path.",
+ "en": "Insufficient permissions to write to the specified directory. Please check access rights or choose another path.",
"ua": "Недостатньо прав для запису в указану папку. Перевірте права доступу або виберіть інший шлях.",
- "fr":
- "Autorisation insuffisante pour écrire dans le dossier spécifié. Vérifiez les droits d’accès ou choisissez un autre chemin.",
+ "fr": "Autorisation insuffisante pour écrire dans le dossier spécifié. Vérifiez les droits d’accès ou choisissez un autre chemin.",
"zh": "没有权限写入指定的文件夹。请检查访问权限或选择其他路径。"
}
},
@@ -2407,17 +2397,23 @@
"zh": "。冲突:{0}"
}
}
+ },
+ "UpdateService": {
+ "LatestVersion": {
+ "en": "You are using the latest version",
+ "ru": "У вас установлена последняя версия"
+ },
+ "UpdateIndicatorHint": {
+ "en": "Update available. Click 'by achies' to check",
+ "ru": "Доступно обновление. Нажмите 'by achies' для проверки"
+ }
}
},
"CantAlignTimeError": {
- "ru":
- "Не удается синхронизировать время со Steam. Возможно отсутствует подключение к интернету или Steam недоступен. Подробная ошибка сохранена в log.log",
- "en":
- "Can't align time with Steam. Maybe no internet connection or Steam is unavailable. Detailed error saved in log.log",
+ "ru": "Не удается синхронизировать время со Steam. Возможно отсутствует подключение к интернету или Steam недоступен. Подробная ошибка сохранена в log.log",
+ "en": "Can't align time with Steam. Maybe no internet connection or Steam is unavailable. Detailed error saved in log.log",
"zh": "无法与 Steam 对齐时间。可能没有互联网连接或 Steam 不可用。详细错误已保存到 log.log 中",
- "ua":
- "Не вдається синхронізувати час з Steam. Можливо відсутнє підключення до інтернету або Steam недоступний. Детальна помилка збережена в log.log",
- "fr":
- "Impossible de synchroniser l'heure avec Steam. Peut-être qu'il n'y a pas de connexion internet ou Steam est indisponible. Détails de l'erreur enregistrés dans log.log"
+ "ua": "Не вдається синхронізувати час з Steam. Можливо відсутнє підключення до інтернету або Steam недоступний. Детальна помилка збережена в log.log",
+ "fr": "Impossible de synchroniser l'heure avec Steam. Peut-être qu'il n'y a pas de connexion internet ou Steam est indisponible. Détails de l'erreur enregistrés dans log.log"
}
}
\ No newline at end of file