From 62c018d4c6931ec34e204da83b781a609a906ce4 Mon Sep 17 00:00:00 2001
From: Luca <106596790+RealLHI@users.noreply.github.com>
Date: Fri, 16 Feb 2024 14:47:59 +0100
Subject: [PATCH 1/2] add(formats): added new JavaProperties format (#59)
---
Ashampoo-Translation-Systems.sln | 12 ++
...Systems.Formats.JavaProperties.Test.csproj | 42 ++++
.../FormatTest.cs | 111 +++++++++++
.../GlobalUsings.cs | 1 +
.../Startup.cs | 12 ++
.../_TestFiles_/messages.properties | 186 ++++++++++++++++++
.../_TestFiles_/messages_de.properties | 186 ++++++++++++++++++
...tion.Systems.Formats.JavaProperties.csproj | 27 +++
.../JavaPropertiesBuilder.cs | 68 +++++++
.../JavaPropertiesFormat.cs | 133 +++++++++++++
LICENSE | 2 +-
11 files changed, 779 insertions(+), 1 deletion(-)
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Ashampoo.Translation.Systems.Formats.JavaProperties.Test.csproj
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties.Test/FormatTest.cs
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties.Test/GlobalUsings.cs
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Startup.cs
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties.Test/_TestFiles_/messages.properties
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties.Test/_TestFiles_/messages_de.properties
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties/Ashampoo.Translation.Systems.Formats.JavaProperties.csproj
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesBuilder.cs
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormat.cs
diff --git a/Ashampoo-Translation-Systems.sln b/Ashampoo-Translation-Systems.sln
index f471c2b..8ff73ec 100644
--- a/Ashampoo-Translation-Systems.sln
+++ b/Ashampoo-Translation-Systems.sln
@@ -38,6 +38,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.System
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.Systems.Formats", "src\Ashampoo.Translation.Systems.Formats\src\Ashampoo.Translation.Systems.Formats.csproj", "{53389A5D-6790-4E38-A2C5-1B0D74B76DCF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.Systems.Formats.JavaProperties", "Ashampoo.Translation.Systems.Formats.JavaProperties\Ashampoo.Translation.Systems.Formats.JavaProperties.csproj", "{4481C61F-6BF8-4FE4-8576-1FFEA0B67BF8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.Systems.Formats.JavaProperties.Test", "Ashampoo.Translation.Systems.Formats.JavaProperties.Test\Ashampoo.Translation.Systems.Formats.JavaProperties.Test.csproj", "{B31DF60F-D7D6-4D9A-BE2A-3A7089E27118}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -124,5 +128,13 @@ Global
{3D6B80E7-4C45-4400-A973-C468C6BB1EE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D6B80E7-4C45-4400-A973-C468C6BB1EE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D6B80E7-4C45-4400-A973-C468C6BB1EE6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4481C61F-6BF8-4FE4-8576-1FFEA0B67BF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4481C61F-6BF8-4FE4-8576-1FFEA0B67BF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4481C61F-6BF8-4FE4-8576-1FFEA0B67BF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4481C61F-6BF8-4FE4-8576-1FFEA0B67BF8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B31DF60F-D7D6-4D9A-BE2A-3A7089E27118}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B31DF60F-D7D6-4D9A-BE2A-3A7089E27118}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B31DF60F-D7D6-4D9A-BE2A-3A7089E27118}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B31DF60F-D7D6-4D9A-BE2A-3A7089E27118}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Ashampoo.Translation.Systems.Formats.JavaProperties.Test.csproj b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Ashampoo.Translation.Systems.Formats.JavaProperties.Test.csproj
new file mode 100644
index 0000000..0eb00a8
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Ashampoo.Translation.Systems.Formats.JavaProperties.Test.csproj
@@ -0,0 +1,42 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
+
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/FormatTest.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/FormatTest.cs
new file mode 100644
index 0000000..a4ddfa5
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/FormatTest.cs
@@ -0,0 +1,111 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
+using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
+using Ashampoo.Translation.Systems.TestBase;
+using FluentAssertions;
+
+namespace Ashampoo.Translation.Systems.Formats.JavaProperties.Test;
+
+public class FormatTest : FormatTestBase
+{
+ private readonly IFormatFactory _formatFactory;
+
+ public FormatTest(IFormatFactory formatFactory)
+ {
+ _formatFactory = formatFactory;
+ }
+
+ [Fact]
+ public void IsAssignableFrom()
+ {
+ IFormat format = CreateFormat();
+ format.Should().BeAssignableTo(typeof(ITranslationUnits));
+ }
+
+ [Fact]
+ public void NewFormat()
+ {
+ IFormat format = CreateFormat();
+
+ format.Should().NotBeNull().And.BeEmpty();
+ format.Header.SourceLanguage.Should().BeNull();
+ format.Header.TargetLanguage.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void ReadFromFile()
+ {
+ IFormat format =
+ CreateAndReadFromFile("messages_de.properties", new FormatReadOptions() { TargetLanguage = "de-DE" });
+ const string id = "aboutTheApp";
+
+ foreach (var unit in format)
+ {
+ unit.Should().ContainSingle();
+ }
+
+ format.Count.Should().Be(186);
+
+ var foundById = format[id];
+ foundById.Should().NotBeNull();
+ var translationString = foundById!["de-DE"] as AbstractTranslationString;
+ translationString.Should().NotBeNull();
+ translationString!.Value.Should().Be("Über Photos");
+ translationString.Comment.Should().BeNull();
+ translationString.Id.Should().Be(id);
+ }
+
+ [Fact]
+ public void ImportSuccessTest()
+ {
+ IFormat format =
+ CreateAndReadFromFile("messages.properties", new FormatReadOptions() { TargetLanguage = "en-US" });
+
+ const string id = "albums";
+ const string value = "Import Test";
+
+ var importedWithUnits = format.ImportMockTranslationWithUnits("en-US", id);
+ importedWithUnits.Should().NotBeNull().And.ContainSingle();
+ (format[id]?["en-US"] as ITranslationString)?.Value.Should().Be(value);
+ }
+
+ [Fact]
+ public void NoMatchImportTest()
+ {
+ IFormat format =
+ CreateAndReadFromFile("messages.properties", new FormatReadOptions() { TargetLanguage = "en-US" });
+
+ const string id = "Not a matching Id";
+
+ var imported = format.ImportMockTranslationWithUnits("en-US", id);
+
+ imported.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void ImportEqualTranslationTest()
+ {
+ IFormat format =
+ CreateAndReadFromFile("messages.properties", new FormatReadOptions() { TargetLanguage = "en-US" });
+
+ const string id = "albums";
+ const string value = "Albums";
+
+ var imported = format.ImportMockTranslationWithUnits("en-US", id, value);
+
+ imported.Should().BeEmpty();
+ }
+
+ [Fact]
+ public async Task ConvertTest()
+ {
+ var mockFormat =
+ MockFormatWithTranslationUnits.CreateMockFormatWithTranslationUnits("en-US", "Convert ID", "Convert Test");
+ var assignOptions = new AssignOptions { TargetLanguage = "en-US", Filter = new DefaultTranslationFilter() };
+ var javaProperties = await mockFormat.ConvertToAsync(_formatFactory, assignOptions);
+
+ javaProperties.Should().NotBeNull().And.ContainSingle();
+ javaProperties.First().Id.Should().Be("Convert ID");
+ (javaProperties["Convert ID"]?["en-US"] as ITranslationString)?.Value.Should().Be("Convert Test");
+ }
+}
\ No newline at end of file
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/GlobalUsings.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/GlobalUsings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/GlobalUsings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Startup.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Startup.cs
new file mode 100644
index 0000000..a2bcddf
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Startup.cs
@@ -0,0 +1,12 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Ashampoo.Translation.Systems.Formats.JavaProperties.Test;
+
+public class Startup
+{
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddFormatFactory().RegisterFormat();
+ }
+}
\ No newline at end of file
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/_TestFiles_/messages.properties b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/_TestFiles_/messages.properties
new file mode 100644
index 0000000..a2e2db6
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/_TestFiles_/messages.properties
@@ -0,0 +1,186 @@
+aboutTheApp=About Photos
+addAnotherSource=Add another
+albumCreatedHint=New album created.
+albumDeleteConfirmationText=Your photos will not be deleted.
+albumDeleteConfirmationTitle=Delete album?
+albumDeletedHint=Album deleted.
+albumRenamedHint=Album renamed.
+albums=Albums
+albumsEmptyPlaceholder=No albums present.
+analyticsUserConsentSendErrorReportsAndStatics=Send errors & statistics
+analyticsUserConsentSendNothing=Send nothing
+analyticsUserConsentSendOnlyErrorReports=Send only errors
+analyticsUserConsentTextPrivacyPolicyHint=For more information please see our privacy policy.
+analyticsUserConsentTextReasoning=To improve the app and to find errors, we would like to collect anonymous usage statistics and error reports.
+analyticsUserConsentTextSettingsHint=You can change your decision at any time in the settings.
+analyticsUserConsentTitle=Help to improve this app!
+androidWritePermissionConfirmationConfirm=Open system dialog
+androidWritePermissionConfirmationText=To modify files, write permissions are required.\n\nPlease grant access to the following folder in the subsequent system dialog:
+androidWritePermissionConfirmationTitle=Write permissions required
+androidWritePermissionsUpdated=Permissions updated
+appCrashed=Apologies, unfortunately, Photos has crashed! :(
+appLaunchFailed=Photos failed to launch.
+authFailed=Auth failed.
+awaitingCode=Waiting for login in browser...
+browserNotFound=Browser not found.
+cancel=Cancel
+cancelSync=Cancel sync
+cantConnectSource=Connection failed.
+changeTakenDateDialogAdjusted=Adjusted
+changeTakenDateDialogConfirmButton=Set new date
+changeTakenDateDialogOriginal=Original
+changeTakenDateDialogSetDate=Custom
+changeTakenDateDialogShiftDate=Time Shift
+changeTakenDateDialogTime=Time
+changeTakenDateDialogTitle=Adjust date taken
+choose=Choose
+chooseEditorPath=Choose your editor
+choosePhotoDirectory=Choose a directory that contains your photos
+chooseSaveDirectory=Choose a save directory
+cloudSharingNotSupportedHint=Sharing of cloud files is not supported.
+commandExecutionFailed=Command execution failed.
+create=Create
+december=December
+deleteAlbum=Delete this album
+deleteMultiplePhotos=Delete photos
+deleteOnePhoto=Delete photo
+deletionFailed=Deletion failed!
+disconnect=Disconnect
+editor=Editor
+emptySearchPlaceholderAlbum=Album names
+emptySearchPlaceholderCamera=Cameras such as "Canon 60D"
+emptySearchPlaceholderDate=Dates such as "March" and "2010"
+emptySearchPlaceholderFolder=Folder names
+emptySearchPlaceholderKeyword=Keywords such as "cat"
+emptySearchPlaceholderLocation=Locations such as "New York"
+emptySearchPlaceholderRating=Ratings from "0" to "5"
+emptySearchPlaceholderTitle=What can I search for?
+february=February
+feedback=Feedback
+feedbackText=Do you miss a feature?\nCan we improve something?\nPlease let us know!
+fileNotFound=The file is not available. Was it deleted or moved?
+fileProtectionHintButtonText=Okay
+fileProtectionHintText=This file is write protected and can't be updated.
+fileProtectionHintTitle=Write protected file
+fileSaved=Saved.
+fileSaving=Saving...
+focusRating=Focus rating
+foldersAlbumGroup=Folders
+galleryAddPhotos=Add your first photo source:
+galleryNoPhotosFound=No photos found.
+genericErrorMessage=An error occured.
+gridStyle=Grid Style
+keywordInputPlaceholder=Add keyword...
+keywordsAlbumGroup=Keywords
+keywordsEmptyPlaceholder=No keywords present
+lastSynced=Last synced
+leadDeveloper=Lead Developer
+librariesWeUse=Libraries we use
+librariesWeUseThanks=We thank developers and supporters of the following libraries that we use in this app.
+licenseStatusCheckFailedText=Communication with the license server failed. Please check your connection and try again.
+licenseStatusCheckFailedTitle=License check failed
+licenseStatusExpiredText=Please connect to the Internet to renew your license.
+licenseStatusExpiredTitle=License expired
+licenseStatusInvalidText=Please contact our support.
+licenseStatusInvalidTitle=Invalid license
+licenseStatusNoLicenseText=If you already have a license, please contact our support.
+licenseStatusNoLicenseTitle=No license found
+licenseStatusNotCheckedTitle=Checking license...
+licenseStatusViewCheckLicenseButton=Retry license check
+licenseStatusViewLogoutButton=Logout
+locationsAlbumGroup=Locations
+loggedIn=You are now logged in.
+loginAccountRequired=To get started, please log in with your existing Ashampoo account or create one for free.
+loginAppPurpose=Ashampoo Photos helps you organize your photo collection.
+loginInBrowser=Login in browser
+managePermissions=Manage permissions
+memoryWarningText=Cleaning up...
+memoryWarningTitle=High memory usage!
+newAlbum=New album
+newAlbumTextFieldPlaceholder=Enter album title
+noCameraInformation=No camera information
+noLensInformation=No lens information
+openFileLocation=Open file location
+openUserFeedbackPortalInBrowser=Open feedback portal
+openWithDefaultApp=Open with default app
+openWithExternalEditor=Open with external editor
+personsAlbumGroup=Persons
+photoAddedToAlbumHint=Photo added to album.
+photoCouldNotBeLoaded=Photo could not be loaded.
+photoCount=Photo count
+photoEditFeedbackText=Here we are planning photo editing tools.
+photoMenu=Photo
+photoSourceApplePhotos=Apple Photos
+photoSourceConnect=Connect
+photoSourceDisconnectConfirmationText=By disconnecting the source all photos will be removed from the app and thereby all ratings, tags and edits will be permanently deleted.
+photoSourceDisconnectConfirmationTitle=Are you sure?
+photoSourceDisconnectedHint=Source disconnected.
+photoSourceDropbox=Dropbox
+photoSourceGoogleDrive=Google Drive
+photoSourceLocalFolder=Local folder
+photoSourceNewConnection=New source
+photoSourceOneDrive=OneDrive
+photoSourceSmbFolder=Network folder
+photoSourceSynologyNasFolder=Synology NAS
+photoSourceSystemPhotoLibrary=Photo library
+photoSourceWhatDoYouMissHere=Missing a source?
+photoSourcesPlaceholder=Connect a photo source!
+photos=Photos
+photosAddedToAlbumHint=Photos added to album.
+photosRemovedFromAlbumHint=Photos removed from album.
+pleaseContactSupport=Please contact our support!
+pleaseRotateYourDevice=Please rotate your device.
+privacyPolicy=Privacy policy
+processors=processors
+ratingsAlbumGroup=Ratings
+rename=Rename
+renameAlbum=Rename album
+runtimeEnvironment=Runtime environment
+saveAs=Save as...
+search=Search
+searchFieldPlaceholder=Search photos
+select=Select
+selectAlbum=Select album
+selectPhotos=Select photos
+settings=Settings
+settingsEmbedMetadata=Embed metadata (JPG & PNG)
+settingsSendAnalyticsData=Send usage statistics
+settingsSendErrorReports=Send error reports
+shortcutAddToAlbum=Add photo to an album
+shortcutClose=Close / Back
+shortcutConfirm=Confirm
+shortcutDeletePhoto=Delete photo
+shortcutDeleteRating=Delete rating
+shortcutRatePhoto=Rate photo
+shortcutSharePhoto=Share photo
+shortcutShowHideInfoBox=Show/hide info box
+shortcutShowPrevNextPhoto=Show prev/next photo
+shortcutTagPhoto=Tag photo
+shortcutToggleFullscreen=Toggle full screen
+shortcuts=Keyboard shortcuts
+showAll=Show all
+software=Software
+sortOrder=Sort order
+sourceAlreadyAdded=Source was already added.
+sources=Sources
+supportId=Support ID
+syncNow=Sync now
+syncStateStepGeocoding=Geocoding...
+syncStateStepGrouping=Grouping images...
+syncStateStepInitializing=Initializing sync...
+syncStateStepReadingFileList=Reading file list...
+syncStateStepReadingMetadata=Reading metadata...
+syncStateStepThumbnailing=Creating thumbnails...
+syncStatusUnknown=Never synced.
+syncing=Syncing...
+systemPhotoLibraryFullAccess=All photos
+systemPhotoLibraryLimitedAccess=Selected photos
+systemPhotoLibraryNoAccess=Access denied
+takenDateChangedHint=Date taken changed.
+theme=Design
+themeDark=Dark
+themeLight=Light
+themeSystemDefault=System
+tileSize=Tile Size
+userAlbumGroup=My albums
+welcomeToPhotos=Welcome to Photos!
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/_TestFiles_/messages_de.properties b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/_TestFiles_/messages_de.properties
new file mode 100644
index 0000000..f7a6408
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/_TestFiles_/messages_de.properties
@@ -0,0 +1,186 @@
+aboutTheApp=Über Photos
+addAnotherSource=Weitere hinzufügen
+albumCreatedHint=Neues Album erstellt.
+albumDeleteConfirmationText=Deine Fotos werden nicht gelöscht.
+albumDeleteConfirmationTitle=Album löschen?
+albumDeletedHint=Album gelöscht.
+albumRenamedHint=Album umbenannt.
+albums=Alben
+albumsEmptyPlaceholder=Keine Alben vorhanden
+analyticsUserConsentSendErrorReportsAndStatics=Fehler & Statistiken senden
+analyticsUserConsentSendNothing=Nichts senden
+analyticsUserConsentSendOnlyErrorReports=Nur Fehler senden
+analyticsUserConsentTextPrivacyPolicyHint=Weitere Informationen findest du in unserer Datenschutzerklärung.
+analyticsUserConsentTextReasoning=Um die App zu verbessern und Fehler zu finden, würden wir gerne anonymisierte Nutzungstatistiken und Fehlerberichte erfassen.
+analyticsUserConsentTextSettingsHint=Du kannst deine Entscheidung jederzeit in den Einstellungen ändern.
+analyticsUserConsentTitle=Hilf die App zu verbessern!
+androidWritePermissionConfirmationConfirm=System-Dialog öffnen
+androidWritePermissionConfirmationText=Um Dateien zu ändern werden Schreibrechte benötigt.\n\nBitte erlaube im nachfolgenden System-Dialog Zugriff auf folgenden Ordner:
+androidWritePermissionConfirmationTitle=Schreibrechte benötigt
+androidWritePermissionsUpdated=Berechtigungen aktualisiert
+appCrashed=Entschuldigung, leider ist Photos abgestürzt! :(
+appLaunchFailed=Photos konnte nicht geladen werden.
+authFailed=Anmeldung fehlgeschlagen.
+awaitingCode=Warte auf Anmeldung im Browser...
+browserNotFound=Es wurde kein Browser gefunden.
+cancel=Abbrechen
+cancelSync=Abgleich abbrechen
+cantConnectSource=Verbindung fehlgeschlagen.
+changeTakenDateDialogAdjusted=Angepasst
+changeTakenDateDialogConfirmButton=Aufnahmedatum setzen
+changeTakenDateDialogOriginal=Original
+changeTakenDateDialogSetDate=Benutzerdefiniert
+changeTakenDateDialogShiftDate=Zeitverschiebung
+changeTakenDateDialogTime=Zeit
+changeTakenDateDialogTitle=Aufnahmedatum anpassen
+choose=Auswählen
+chooseEditorPath=Wähle deinen Editor aus
+choosePhotoDirectory=Wähle einen Ordner mit deinen Bildern aus
+chooseSaveDirectory=Wähle einen Speicherort
+cloudSharingNotSupportedHint=Das Teilen von Cloud-Dateien wird nicht unterstützt.
+commandExecutionFailed=Befehl konnte nicht ausgeführt werden.
+create=Erstellen
+december=Dezember
+deleteAlbum=Dieses Album löschen
+deleteMultiplePhotos= Fotos löschen
+deleteOnePhoto=Foto löschen
+deletionFailed=Löschen fehlgeschlagen!
+disconnect=Trennen
+editor=Editor
+emptySearchPlaceholderAlbum=Namen von Alben
+emptySearchPlaceholderCamera=Kameras wie "Canon 60D"
+emptySearchPlaceholderDate=Zeiträume wie "2010" und "März"
+emptySearchPlaceholderFolder=Namen von Ordnern
+emptySearchPlaceholderKeyword=Schlagwörter wie "Katze"
+emptySearchPlaceholderLocation=Orte wie "Berlin"
+emptySearchPlaceholderRating=Bewertungen von "0" bis "5"
+emptySearchPlaceholderTitle=Wonach kann ich suchen?
+february=Februar
+feedback=Feedback
+feedbackText=Vermisst du ein Feature?\nKönnen wir was verbessern?\nBitte lass es uns wissen!
+fileNotFound=Die Datei ist nicht verfügbar. Wurde sie gelöscht oder verschoben?
+fileProtectionHintButtonText=Okay
+fileProtectionHintText=Diese Datei ist schreibgeschützt und kann nicht aktualisiert werden.
+fileProtectionHintTitle=Geschützte Datei
+fileSaved=Gespeichert.
+fileSaving=Speichern...
+focusRating=Fokusbewertung
+foldersAlbumGroup=Ordner
+galleryAddPhotos=Füge deine erste Fotoquelle hinzu:
+galleryNoPhotosFound=Es wurden keine Fotos gefunden.
+genericErrorMessage=Es ist ein Fehler aufgetreten.
+gridStyle=Anordnung
+keywordInputPlaceholder=Schlagwort hinzufügen...
+keywordsAlbumGroup=Schlagwörter
+keywordsEmptyPlaceholder=Keine Schlagwörter vorhanden
+lastSynced=Letzter Abgleich
+leadDeveloper=Lead Developer
+librariesWeUse=Verwendete Bibliotheken
+librariesWeUseThanks=Wir danken den Entwicklern und Unterstützern folgender Bibliotheken, die wir bei dieser App einsetzen.
+licenseStatusCheckFailedText=Fehler bei der Kommunikation mit dem Lizenzserver. Bitte überprüfe die Internetverbindung und versuche es noch einmal.
+licenseStatusCheckFailedTitle=Lizenzprüfung fehlgeschlagen
+licenseStatusExpiredText=Bitte stelle eine Internetverbindung her, um die Lizenz zu erneuern.
+licenseStatusExpiredTitle=Lizenz abgelaufen
+licenseStatusInvalidText=Bitte kontaktiere unseren Support.
+licenseStatusInvalidTitle=Ungültige Lizenz
+licenseStatusNotCheckedTitle=Überprüfe Lizenz...
+licenseStatusStatusNoLicenseText=Falls du bereits eine Lizenz hast, kontaktiere bitte unseren Support.
+licenseStatusStatusNoLicenseTitle=Lizenz nicht gefunden
+licenseStatusViewCheckLicenseButton=Lizenz erneut prüfen
+licenseStatusViewLogoutButton=Ausloggen
+locationsAlbumGroup=Orte
+loggedIn=Du bist nun angemeldet.
+loginAccountRequired=Um zu starten, melde dich bitte mit deinem bestehenden Ashampoo Konto an oder erstelle kostenlos ein neues.
+loginAppPurpose=Ashampoo Photos hilft dir deine Fotosammlung zu organisieren.
+loginInBrowser=Im Browser anmelden
+managePermissions=Berechtigungen verwalten
+memoryWarningText=Bereinige Speicher...
+memoryWarningTitle=Hoher Speicherverbrauch!
+newAlbum=Neues Album
+newAlbumTextFieldPlaceholder=Albumtitel eingeben
+noCameraInformation=Keine Kamerainformationen
+noLensInformation=Keine Objektivinformationen
+openFileLocation=Dateipfad öffnen
+openUserFeedbackPortalInBrowser=Feedback-Portal öffnen
+openWithDefaultApp=Öffnen mit Standard-App
+openWithExternalEditor=Öffnen mit externem Editor
+personsAlbumGroup=Personen
+photoAddedToAlbumHint=Foto zu Album hinzugefügt.
+photoCouldNotBeLoaded=Foto konnte nicht geladen werden.
+photoCount=Anzahl Fotos
+photoEditFeedbackText=Hier planen wir Tools zum Bearbeiten.
+photoMenu=Foto
+photoSourceApplePhotos=Apple Fotos
+photoSourceConnect=Verbinden
+photoSourceDisconnectConfirmationText=Durch das Trennen der Quelle werden alle Fotos aus der App entfernt und damit Bewertungen, Tags und Bearbeitungen unwiderruflich gelöscht.
+photoSourceDisconnectConfirmationTitle=Bist du sicher?
+photoSourceDisconnectedHint=Quelle getrennt.
+photoSourceDropbox=Dropbox
+photoSourceGoogleDrive=Google Drive
+photoSourceLocalFolder=Lokaler Ordner
+photoSourceNewConnection=Neue Quelle
+photoSourceOneDrive=OneDrive
+photoSourceSmbFolder=Netzwerk-Freigabe
+photoSourceSynologyNasFolder=Synology NAS
+photoSourceSystemPhotoLibrary=Foto-Bibliothek
+photoSourceWhatDoYouMissHere=Was fehlt\ndir hier?
+photoSourcesPlaceholder=Füge eine Fotoquelle hinzu!
+photos=Fotos
+photosAddedToAlbumHint=Fotos zu Album hinzugefügt.
+photosRemovedFromAlbumHint=Fotos aus Album entfernt.
+pleaseContactSupport=Bitte kontaktiere unseren Support!
+pleaseRotateYourDevice=Bitte drehe dein Gerät.
+privacyPolicy=Datenschutzerklärung
+processors=Prozessoren
+ratingsAlbumGroup=Bewertungen
+rename=Umbenennen
+renameAlbum=Album umbenennen
+runtimeEnvironment=Laufzeitumgebung
+saveAs=Speichern unter...
+search=Suchen
+searchFieldPlaceholder=Fotos suchen
+select=Auswählen
+selectAlbum=Album auswählen
+selectPhotos=Fotos auswählen
+settings=Einstellungen
+settingsEmbedMetadata=Metadaten einbetten (JPG & PNG)
+settingsSendAnalyticsData=Nutzungsstatistiken senden
+settingsSendErrorReports=Fehlerberichte senden
+shortcutAddToAlbum=Foto zu einem Album hinzufügen
+shortcutClose=Schließen / Zurück
+shortcutConfirm=Bestätigen
+shortcutDeletePhoto=Foto löschen
+shortcutDeleteRating=Bewertung löschen
+shortcutRatePhoto=Foto bewerten
+shortcutSharePhoto=Foto teilen
+shortcutShowHideInfoBox=Zeige/verstecke Info-Kasten
+shortcutShowPrevNextPhoto=Zeige vorheriges/nächstes Foto
+shortcutTagPhoto=Foto verschlagworten
+shortcutToggleFullscreen=Vollbild umschalten
+shortcuts=Tastaturkürzel
+showAll=Alle anzeigen
+software=Software
+sortOrder=Sortierung
+sourceAlreadyAdded=Diese Quelle ist bereits vorhanden.
+sources=Quellen
+supportId=Support ID
+syncNow=Jetzt abgleichen
+syncStateStepGeocoding=Geocoding...
+syncStateStepGrouping=Gruppiere Bilder...
+syncStateStepInitializing=Starte Sync...
+syncStateStepReadingFileList=Lese Dateiliste...
+syncStateStepReadingMetadata=Lese Metadaten...
+syncStateStepThumbnailing=Erstelle Vorschau...
+syncStatusUnknown=Noch nie abgeglichen.
+syncing=Synchronisiere...
+systemPhotoLibraryFullAccess=Alle Fotos
+systemPhotoLibraryLimitedAccess=Ausgewählte Fotos
+systemPhotoLibraryNoAccess=Kein Zugriff
+takenDateChangedHint=Aufnahmedatum geändert.
+theme=Design
+themeDark=Dunkel
+themeLight=Hell
+themeSystemDefault=System
+tileSize=Kachelgröße
+userAlbumGroup=Meine Alben
+welcomeToPhotos=Willkommen bei Photos!
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties/Ashampoo.Translation.Systems.Formats.JavaProperties.csproj b/Ashampoo.Translation.Systems.Formats.JavaProperties/Ashampoo.Translation.Systems.Formats.JavaProperties.csproj
new file mode 100644
index 0000000..fe444d1
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties/Ashampoo.Translation.Systems.Formats.JavaProperties.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net7.0
+ enable
+ enable
+ ash-logo-icon-big-128x.png
+
+
+
+
+ true
+
+ ash-logo-icon-big-128x.png
+
+
+ true
+
+ LICENSE
+
+
+
+
+
+
+
+
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesBuilder.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesBuilder.cs
new file mode 100644
index 0000000..2e3a1b3
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesBuilder.cs
@@ -0,0 +1,68 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
+using CommunityToolkit.Diagnostics;
+
+namespace Ashampoo.Translation.Systems.Formats.JavaProperties;
+
+public class JavaPropertiesBuilder : IFormatBuilderWithTarget
+{
+ private string _targetLanguage = string.Empty;
+ private readonly Dictionary _translations = new();
+
+ ///
+ public IFormat Build()
+ {
+ Guard.IsNotNullOrWhiteSpace(_targetLanguage);
+
+ JavaPropertiesFormat format = new()
+ {
+ Header =
+ {
+ TargetLanguage = _targetLanguage
+ }
+ };
+
+ foreach (var translation in _translations)
+ {
+ DefaultTranslationUnit unit = new(translation.Key);
+ DefaultTranslationString translationString = new(translation.Key, translation.Value, _targetLanguage);
+ unit.Add(translationString);
+ format.Add(unit);
+ }
+
+ return format;
+ }
+
+ ///
+ public void Add(string id, string target)
+ {
+ _translations.Add(id, target);
+ }
+
+ ///
+ public void SetTargetLanguage(string language)
+ {
+ _targetLanguage = language;
+ }
+
+ ///
+ /// This is not supported by the JavaProperties format
+ ///
+ ///
+ ///
+ public void SetHeaderInformation(IFormatHeader header)
+ {
+ throw new NotSupportedException("Header information's are not supported by the JavaProperties format");
+ }
+
+ ///
+ /// This is not supported by the JavaProperties format
+ ///
+ ///
+ ///
+ ///
+ public void AddHeaderInformation(string key, string value)
+ {
+ throw new NotSupportedException("Header information's are not supported by the JavaProperties format");
+ }
+}
\ No newline at end of file
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormat.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormat.cs
new file mode 100644
index 0000000..e97a893
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormat.cs
@@ -0,0 +1,133 @@
+using System.Text;
+using System.Text.RegularExpressions;
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.IO;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
+using CommunityToolkit.Diagnostics;
+using IFormatProvider = Ashampoo.Translation.Systems.Formats.Abstractions.IFormatProvider;
+
+namespace Ashampoo.Translation.Systems.Formats.JavaProperties;
+
+public partial class JavaPropertiesFormat : AbstractTranslationUnits, IFormat
+{
+ private static readonly Regex KeyValueRegex = MyRegex();
+ public IFormatHeader Header { get; } = new DefaultFormatHeader();
+ public FormatLanguageCount LanguageCount => FormatLanguageCount.OnlyTarget;
+
+ ///
+ public void Read(Stream stream, FormatReadOptions? options = null)
+ {
+ ReadAsync(stream, options).Wait();
+ }
+
+ ///
+ public async Task ReadAsync(Stream stream, FormatReadOptions? options = null)
+ {
+ if (!await ConfigureOptionsAsync(options))
+ {
+ // TODO: Make options a not nullable param to avoid this "Not null" call
+ options!.IsCancelled = true;
+ return;
+ }
+
+ Guard.IsNotNullOrWhiteSpace(Header.TargetLanguage);
+
+ using StreamReader reader = new(stream);
+ using LineReader lineReader = new(reader);
+
+ await ReadTranslations(lineReader);
+ }
+
+ // TODO: Can this be made Protected in a abstract class to avoid duplicate code?
+ private async Task ConfigureOptionsAsync(FormatReadOptions? options)
+ {
+ if (string.IsNullOrWhiteSpace(options?.TargetLanguage))
+ {
+ Guard.IsNotNull(options?.FormatOptionsCallback);
+
+ FormatStringOption targetLanguageOption = new("Target language", true);
+ FormatOptions formatOptions = new()
+ {
+ Options = new FormatOption[]
+ {
+ targetLanguageOption
+ }
+ };
+
+ await options.FormatOptionsCallback.Invoke(formatOptions); // Invoke callback
+ if (formatOptions.IsCanceled) return false;
+
+ Header.TargetLanguage = targetLanguageOption.Value;
+ }
+ else
+ {
+ Header.TargetLanguage = options.TargetLanguage;
+ }
+
+ return true;
+ }
+
+ private async Task ReadTranslations(LineReader reader)
+ {
+ await reader.SkipEmptyLinesAsync();
+ while (await reader.HasMoreLinesAsync())
+ {
+ var translation = ParseLine(await reader.ReadLineAsync(), reader.LineNumber);
+ var unit = new DefaultTranslationUnit(translation.Id) { translation };
+ Add(unit);
+ }
+ }
+
+ private ITranslation ParseLine(string? line, int lineNumber)
+ {
+ Guard.IsNotNullOrWhiteSpace(line);
+
+ var match = KeyValueRegex.Match(line);
+ if (!match.Success)
+ throw new UnsupportedFormatException(this, $"Unsupported line: {line} at line number {lineNumber}.");
+
+ var id = match.Groups["key"].Value;
+ var value = match.Groups["value"].Value;
+
+
+ return new DefaultTranslationString(id, value, Header.TargetLanguage);
+ }
+
+ ///
+ public void Write(Stream stream)
+ {
+ WriteAsync(stream).Wait();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public async Task WriteAsync(Stream stream)
+ {
+ await using StreamWriter writer = new(stream, Encoding.Latin1);
+
+ foreach (var translation in this.SelectMany(translationUnit => translationUnit))
+ {
+ if (translation is AbstractTranslationString translationString)
+ {
+ await writer.WriteLineAsync($"{translationString.Id}={translationString.Value}");
+ }
+ }
+
+ await writer.FlushAsync();
+ }
+
+ ///
+ public Func BuildFormatProvider()
+ {
+ return builder => builder.SetId("javaproperties")
+ .SetSupportedFileExtensions(new[] { ".properties" })
+ .SetFormatType()
+ .SetFormatBuilder()
+ .Create();
+ }
+
+ [GeneratedRegex("(?.*?)=(?.*)")]
+ private static partial Regex MyRegex();
+}
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 58fb437..8cc9587 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2022 Ashampoo GmbH & Co. KG
+Copyright (c) 2024 Ashampoo GmbH & Co. KG
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
From ab430a637269f0384d0ebad0c7988b6a0d391b96 Mon Sep 17 00:00:00 2001
From: Luca <106596790+RealLHI@users.noreply.github.com>
Date: Wed, 21 Feb 2024 15:06:56 +0100
Subject: [PATCH 2/2] Refactore/refactor formats (#60)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* refactor: removed id from ITranslation
* refactor: Removed ITranslationString, ITranslation/AbstractTranslationUnit not deriving from IEnumerable/HashSet
* refactor: Removed Converters & AssignOptions, name refactoring
* refactor: namings, removed ITranslationUnits
* refactor: naming, removed inheritance IDictionary from IFormatHeader
* Add WriteAsync to IFormat
* refactor: moved SetTargetLanguage to IFormatBuilder
* refactor: Removed empty Abstract class, removed unused propertys, refactor naming
* refactor: made AdditionalHeaders abstract, removed unused/unimportant methods
* refactor: ashlang tests rebuild to FluentAssertions
* refactor: rebuild TsProj, PO, ResX, NLang on FluentAssertions
* refactor: rewrite Json and Gengo tests with FluentAssertions
* refactor: now running on .net8
* chore: updated all packages
* refactor: add missing comments
* refactor: switched to collection expressions
* refactor: implementing Stronglys, first format now runs with them
* Add language strongly
* refactor: implement Strongly type Language
* refactor: changed ToString calls on Language to Value
* refactor: add IsNullOrWhitespace to Language
* refactor: add comments
* fix workflows
* refactor: target language of FormatReadOptions not nullable, fix ConfigureOptions in POFormat
* cleanup
* refactor: user generics instead of assembly scanning
* removed package configuration from project files
* updated workflows
* updated dependencies
* cleanup
(major)
---------
Co-authored-by: Tjorven Kämpfer <62958434+tjorvenK@users.noreply.github.com>
---
.github/workflows/build-and-test.yml | 6 +-
.github/workflows/release-packages.yml | 41 +--
...euse-generate-version-and-init-release.yml | 2 +-
.../workflows/reuse-publish-nuget-package.yml | 6 +-
Ashampoo-Translation-Systems.sln | 12 -
...Systems.Formats.JavaProperties.Test.csproj | 14 +-
.../FormatTest.cs | 88 +-----
.../Startup.cs | 12 -
...tion.Systems.Formats.JavaProperties.csproj | 4 +-
.../DependencyInjectionExtension.cs | 31 ++
.../JavaPropertiesFormat.cs | 62 ++--
...lder.cs => JavaPropertiesFormatBuilder.cs} | 22 +-
.../JavaPropertiesFormatProvider.cs | 25 ++
...mpoo.Translation.Systems.Components.csproj | 47 ---
.../src/Components/DisplayFormat.razor | 66 ----
.../src/Components/DisplayFormat.razor.cs | 149 ---------
.../src/Components/ExportFormat.razor | 6 -
.../src/Components/ExportFormat.razor.cs | 66 ----
.../src/Components/UploadFormat.razor | 19 --
.../src/Components/UploadFormat.razor.cs | 120 -------
.../src/DependencyInjection.cs | 28 --
.../ConfigureFormatOptionsDialog.razor | 29 --
.../ConfigureFormatOptionsDialog.razor.cs | 35 ---
.../src/Dialogs/SelectFormatDialog.razor | 18 --
.../src/Dialogs/SelectFormatDialog.razor.cs | 29 --
.../FormatUploadedNotificationHandler.cs | 32 --
...ImportedTranslationsNotificationHandler.cs | 43 ---
.../SimpleWarningNotificationHandler.cs | 32 --
.../FormatUploadedNotification.cs | 8 -
.../ImportedTranslationsNotification.cs | 11 -
.../src/Notifications/SimpleWarning.cs | 11 -
.../src/Pages/Converter.razor | 31 --
.../src/Pages/Converter.razor.cs | 44 ---
.../src/README.md | 2 -
.../src/Services/FormatService.cs | 150 ---------
.../src/Services/IFileService.cs | 39 ---
.../src/Services/IFormatService.cs | 120 -------
.../src/Services/WebFileService.cs | 71 -----
.../src/wwwroot/Scripts/WebFileService.js | 46 ---
.../src/wwwroot/background.png | Bin 378 -> 0 bytes
.../src/AbstractFormatHeader.cs | 11 +-
...lation.Systems.Formats.Abstractions.csproj | 25 +-
.../src/AssignOptions.cs | 29 --
.../src/DefaultFormatFactory.cs | 40 +--
.../src/DefaultFormatHeader.cs | 9 +-
.../src/DefaultFormatProvider.cs | 47 ---
.../src/DependencyInjection.cs | 118 -------
.../src/FormatExtensions.cs | 239 --------------
.../src/FormatOptions.cs | 20 --
.../src/FormatProviderBuilder.cs | 92 ------
.../src/FormatProviderExtensions.cs | 30 --
.../src/FormatProviderLoader.cs | 76 -----
.../src/FormatReadOptions.cs | 6 +-
.../src/GlobalUsings.cs | 1 -
.../src/IFormat.cs | 81 ++++-
.../src/IFormatBuilder.cs | 15 +-
.../src/IFormatBuilderWithSourceAndTarget.cs | 17 +-
.../src/IFormatBuilderWithTarget.cs | 11 +-
.../src/IFormatFactory.cs | 38 +--
.../src/IFormatHeader.cs | 14 +-
.../src/IFormatProvider.cs | 17 +-
.../src/IO/LineReader.cs | 22 +-
...matLanguageCount.cs => LanguageSupport.cs} | 4 +-
.../src/Models/Language.cs | 35 +++
.../Translation/AbstractTranslationString.cs | 23 +-
.../Translation/AbstractTranslationUnit.cs | 55 +---
.../Translation/AbstractTranslationUnits.cs | 72 -----
.../Translation/DefaultTranslationString.cs | 6 +-
.../src/Translation/ITranslation.cs | 13 +-
.../src/Translation/ITranslationString.cs | 12 -
.../src/Translation/ITranslationUnit.cs | 83 +++--
.../TranslationFilter/AndTranslationFilter.cs | 40 ---
.../DefaultTranslationFilter.cs | 24 --
.../TranslationFilter/ITranslationFilter.cs | 28 --
.../IsEmptyTranslationFilter.cs | 41 ---
.../MatchIdTranslationFilter.cs | 67 ----
.../MatchValueTranslationFilter.cs | 88 ------
.../TranslationFilter/OrTranslationFilter.cs | 41 ---
.../Exceptions/ParserExceptions.cs | 68 ----
.../Extensions/EnumeratorTokenExtensions.cs | 61 ----
.../src/TranslationFilterParser/Lexer.cs | 156 ---------
.../src/TranslationFilterParser/Parser.cs | 295 ------------------
.../src/TranslationFilterParser/Token.cs | 61 ----
....Systems.Formats.Abstractions.Tests.csproj | 23 --
.../tests/LanguageParserTests.cs | 34 --
.../tests/Lexer.cs | 50 ---
.../tests/MatchValueFilter.cs | 56 ----
.../tests/Parser.cs | 143 ---------
.../tests/Startup.cs | 10 -
.../src/AshLangFormat.cs | 31 +-
.../src/AshLangFormatBuilder.cs | 44 +--
.../src/AshLangFormatHeader.cs | 29 +-
.../src/AshLangFormatProvider.cs | 27 ++
...Translation.Systems.Formats.AshLang.csproj | 19 +-
.../src/Chunk/ChunkWriter.cs | 20 +-
.../src/Chunk/LanguageChunk.cs | 11 +-
.../src/DependencyInjectionExtension.cs | 31 ++
.../src/SourceTranslationString.cs | 28 +-
.../src/TargetTranslationString.cs | 27 +-
.../tests/AshLangFormatTest.cs | 164 +++-------
...ation.Systems.Formats.AshLang.Tests.csproj | 14 +-
.../tests/ChunkReaderTest.cs | 55 ++--
.../tests/PluginLoaderTest.cs | 20 --
.../tests/Startup.cs | 26 --
...o.Translation.Systems.Formats.Gengo.csproj | 21 +-
.../src/DependencyInjectionExtension.cs | 31 ++
.../src/GengoFormat.cs | 83 ++---
.../src/GengoFormatBuilder.cs | 46 +--
.../src/GengoFormatProvider.cs | 27 ++
...slation.Systems.Formats.Gengo.Tests.csproj | 14 +-
.../tests/FormatTest.cs | 188 +++--------
.../tests/Startup.cs | 14 -
...oo.Translation.Systems.Formats.Json.csproj | 19 +-
.../src/DependencyInjectionExtension.cs | 31 ++
.../src/JsonFormat.cs | 83 ++---
.../src/JsonFormatBuilder.cs | 27 +-
.../src/JsonFormatProvider.cs | 27 ++
...nslation.Systems.Formats.Json.Tests.csproj | 14 +-
.../tests/FormatTest.cs | 148 ++-------
.../tests/Startup.cs | 14 -
...o.Translation.Systems.Formats.NLang.csproj | 19 +-
.../src/DependencyInjectionExtension.cs | 31 ++
.../src/NLangFormat.cs | 60 ++--
.../src/NLangFormatBuilder.cs | 27 +-
.../src/NLangFormatProvider.cs | 27 ++
.../src/TranslationString.cs | 7 +-
.../src/TranslationUnit.cs | 2 +-
...slation.Systems.Formats.NLang.Tests.csproj | 14 +-
.../tests/FormatTest.cs | 108 ++-----
.../tests/Startup.cs | 14 -
...mpoo.Translation.Systems.Formats.PO.csproj | 19 +-
.../src/DependencyInjectionExtension.cs | 31 ++
.../src/Message.cs | 9 +-
.../src/MessageString.cs | 16 +-
.../src/POFormat.cs | 50 ++-
.../src/POFormatBuilder.cs | 32 +-
.../src/POFormatProvider.cs | 28 ++
.../src/POHeader.cs | 24 +-
.../src/TranslationUnit.cs | 2 +-
...ranslation.Systems.Formats.PO.Tests.csproj | 14 +-
.../tests/FormatTest.cs | 107 +------
.../tests/Startup.cs | 14 -
...oo.Translation.Systems.Formats.ResX.csproj | 19 +-
.../src/DependencyInjectionExtension.cs | 31 ++
.../src/ResXFormat.cs | 45 +--
.../src/ResXFormatBuilder.cs | 35 ++-
.../src/ResXFormatProvider.cs | 27 ++
...nslation.Systems.Formats.ResX.Tests.csproj | 14 +-
.../tests/FormatTest.cs | 111 +------
.../tests/Startup.cs | 14 -
....Translation.Systems.Formats.TsProj.csproj | 19 +-
.../src/DependencyInjectionExtension.cs | 31 ++
.../src/TranslationStringSource.cs | 29 +-
.../src/TranslationStringTarget.cs | 24 +-
.../src/TsProjFormat.cs | 81 ++---
.../src/TsProjFormatBuilder.cs | 72 +++--
.../src/TsProjFormatProvider.cs | 25 ++
.../tests/AshLangExportedTest.cs | 12 +-
...lation.Systems.Formats.TsProj.Tests.csproj | 14 +-
.../tests/Startup.cs | 14 -
.../tests/TsProjFormatTest.cs | 125 +-------
.../tests/WebExportedTest.cs | 9 +-
...shampoo.Translation.Systems.Formats.csproj | 5 +-
.../src/DependencyInjection.cs | 47 +++
...hampoo.Translation.Systems.TestBase.csproj | 11 +-
.../FormatTestBase.cs | 2 -
.../Helper.cs | 16 +-
.../MockFormatWithTranslationUnits.cs | 41 +--
.../MockHeader.cs | 7 +-
.../MockTranslationString.cs | 7 +-
170 files changed, 1632 insertions(+), 5367 deletions(-)
delete mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Startup.cs
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties/DependencyInjectionExtension.cs
rename Ashampoo.Translation.Systems.Formats.JavaProperties/{JavaPropertiesBuilder.cs => JavaPropertiesFormatBuilder.cs} (68%)
create mode 100644 Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormatProvider.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Ashampoo.Translation.Systems.Components.csproj
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Components/DisplayFormat.razor
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Components/DisplayFormat.razor.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Components/ExportFormat.razor
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Components/ExportFormat.razor.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Components/UploadFormat.razor
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Components/UploadFormat.razor.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/DependencyInjection.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Dialogs/ConfigureFormatOptionsDialog.razor
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Dialogs/ConfigureFormatOptionsDialog.razor.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Dialogs/SelectFormatDialog.razor
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Dialogs/SelectFormatDialog.razor.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/FormatUploadedNotificationHandler.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/ImportedTranslationsNotificationHandler.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/SimpleWarningNotificationHandler.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Notifications/FormatUploadedNotification.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Notifications/ImportedTranslationsNotification.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Notifications/SimpleWarning.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Pages/Converter.razor
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Pages/Converter.razor.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/README.md
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Services/FormatService.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Services/IFileService.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Services/IFormatService.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/Services/WebFileService.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/wwwroot/Scripts/WebFileService.js
delete mode 100644 src/Ashampoo.Translation.Systems.Components/src/wwwroot/background.png
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/AssignOptions.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatProvider.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DependencyInjection.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatExtensions.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderBuilder.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderExtensions.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderLoader.cs
rename src/Ashampoo.Translation.Systems.Formats.Abstractions/src/{FormatLanguageCount.cs => LanguageSupport.cs} (93%)
create mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Models/Language.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationUnits.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslationString.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/AndTranslationFilter.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/DefaultTranslationFilter.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/ITranslationFilter.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/IsEmptyTranslationFilter.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/MatchIdTranslationFilter.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/MatchValueTranslationFilter.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/OrTranslationFilter.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Exceptions/ParserExceptions.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Extensions/EnumeratorTokenExtensions.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Lexer.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Parser.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Token.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Ashampoo.Translation.Systems.Formats.Abstractions.Tests.csproj
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/LanguageParserTests.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Lexer.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/MatchValueFilter.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Parser.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Startup.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatProvider.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.AshLang/src/DependencyInjectionExtension.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.AshLang/tests/PluginLoaderTest.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.AshLang/tests/Startup.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.Gengo/src/DependencyInjectionExtension.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormatProvider.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Gengo/tests/Startup.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.Json/src/DependencyInjectionExtension.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormatProvider.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.Json/tests/Startup.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.NLang/src/DependencyInjectionExtension.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.NLang/src/NLangFormatProvider.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.NLang/tests/Startup.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.PO/src/DependencyInjectionExtension.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.PO/src/POFormatProvider.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.PO/tests/Startup.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.ResX/src/DependencyInjectionExtension.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.ResX/src/ResXFormatProvider.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.ResX/tests/Startup.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.TsProj/src/DependencyInjectionExtension.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats.TsProj/src/TsProjFormatProvider.cs
delete mode 100644 src/Ashampoo.Translation.Systems.Formats.TsProj/tests/Startup.cs
create mode 100644 src/Ashampoo.Translation.Systems.Formats/src/DependencyInjection.cs
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 58b37c2..809918c 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -12,15 +12,15 @@ on:
- "**.json"
env:
- DOTNET_VERSION: "7.0.x" # The .NET SDK version to use
+ DOTNET_VERSION: "8.x" # The .NET SDK version to use
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Setup .NET Core
- uses: actions/setup-dotnet@v1
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml
index 60c143c..cb62e1e 100644
--- a/.github/workflows/release-packages.yml
+++ b/.github/workflows/release-packages.yml
@@ -27,48 +27,11 @@ jobs:
run: |
TAG=${{ needs.generate-version.outputs.version }}
echo ::set-output name=version::${TAG#v}
-
- create-release-branch:
- needs: generate-version
- if: ${{ contains(github.event.pull_request.labels.*.name, 'release') }}
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v2
- with:
- ref: main
- fetch-depth: 0
- - name: Create Release Branch
- run: git checkout -b release/${{ needs.generate-version.outputs.version }}
- - name: Initialize mandatory git config
- run: |
- git config user.name "GitHub Actions"
- git config user.email noreply@github.com
- - name: Push new branch
- run: git push origin release/${{ needs.generate-version.outputs.version}}
- set-matrix:
- needs: [ generate-version ]
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v3
- with:
- fetch-depth: 0
-
- - name: set-matrix
- id: set-matrix
- run: echo "::set-output name=matrix::$(ls src/Ashampoo.Translation.Systems.*/src/Ashampoo.Translation.Systems.*.csproj | sed -r 's/.*\/(.*).csproj$/\1/' | jq -R -s -c 'split("\n")[:-1]'))"
- outputs:
- matrix: ${{ steps.set-matrix.outputs.matrix }}
-
publish-nuget-packages:
- needs: [ generate-version, remove-prefix-from-version ,set-matrix ]
- strategy:
- matrix:
- project_name: ${{ fromJson(needs.set-matrix.outputs.matrix) }}
+ needs: [ generate-version, remove-prefix-from-version ]
uses: ./.github/workflows/reuse-publish-nuget-package.yml
with:
- project_name: ${{ matrix.project_name }}
+ project_name: "Ashampoo.Translation.Systems.Formats"
version: ${{ needs.remove-prefix-from-version.outputs.version }}
secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/reuse-generate-version-and-init-release.yml b/.github/workflows/reuse-generate-version-and-init-release.yml
index cfd0819..0742105 100644
--- a/.github/workflows/reuse-generate-version-and-init-release.yml
+++ b/.github/workflows/reuse-generate-version-and-init-release.yml
@@ -25,7 +25,7 @@ jobs:
version: ${{ steps.PrereleaseVersion.outputs.version || steps.ReleaseVersion.outputs.version }}
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
diff --git a/.github/workflows/reuse-publish-nuget-package.yml b/.github/workflows/reuse-publish-nuget-package.yml
index 2bd34b2..3587eac 100644
--- a/.github/workflows/reuse-publish-nuget-package.yml
+++ b/.github/workflows/reuse-publish-nuget-package.yml
@@ -13,7 +13,7 @@ on:
type: string
env:
- DOTNET_VERSION: "7.0.x" # The .NET SDK version to use
+ DOTNET_VERSION: "8.x" # The .NET SDK version to use
jobs:
publish-nuget-package:
@@ -23,11 +23,11 @@ jobs:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET Core
- uses: actions/setup-dotnet@v1
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
diff --git a/Ashampoo-Translation-Systems.sln b/Ashampoo-Translation-Systems.sln
index 8ff73ec..1994a81 100644
--- a/Ashampoo-Translation-Systems.sln
+++ b/Ashampoo-Translation-Systems.sln
@@ -22,10 +22,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.System
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.Systems.Formats.TsProj.Tests", "src\Ashampoo.Translation.Systems.Formats.TsProj\tests\Ashampoo.Translation.Systems.Formats.TsProj.Tests.csproj", "{4DDC2740-501A-4760-8384-21B9D2F994DD}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.Systems.Components", "src\Ashampoo.Translation.Systems.Components\src\Ashampoo.Translation.Systems.Components.csproj", "{D8C6BB19-A1D1-4CEC-A605-31B8A2C58FDC}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.Systems.Formats.Abstractions.Tests", "src\Ashampoo.Translation.Systems.Formats.Abstractions\tests\Ashampoo.Translation.Systems.Formats.Abstractions.Tests.csproj", "{5C56057B-2927-4CB0-B9E2-13154699CD8E}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.Systems.TestBase", "src\tests\Ashampoo.Translation.Systems.TestBase\Ashampoo.Translation.Systems.TestBase.csproj", "{44657DFF-FC50-40E9-82E8-8FC9E4EDE1B4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ashampoo.Translation.Systems.Formats.ResX.Tests", "src\Ashampoo.Translation.Systems.Formats.ResX\tests\Ashampoo.Translation.Systems.Formats.ResX.Tests.csproj", "{E0532127-BDF8-4E7A-9A4E-A41C17B6B27E}"
@@ -92,14 +88,6 @@ Global
{4DDC2740-501A-4760-8384-21B9D2F994DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DDC2740-501A-4760-8384-21B9D2F994DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DDC2740-501A-4760-8384-21B9D2F994DD}.Release|Any CPU.Build.0 = Release|Any CPU
- {D8C6BB19-A1D1-4CEC-A605-31B8A2C58FDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D8C6BB19-A1D1-4CEC-A605-31B8A2C58FDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D8C6BB19-A1D1-4CEC-A605-31B8A2C58FDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D8C6BB19-A1D1-4CEC-A605-31B8A2C58FDC}.Release|Any CPU.Build.0 = Release|Any CPU
- {5C56057B-2927-4CB0-B9E2-13154699CD8E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {5C56057B-2927-4CB0-B9E2-13154699CD8E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {5C56057B-2927-4CB0-B9E2-13154699CD8E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {5C56057B-2927-4CB0-B9E2-13154699CD8E}.Release|Any CPU.Build.0 = Release|Any CPU
{44657DFF-FC50-40E9-82E8-8FC9E4EDE1B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44657DFF-FC50-40E9-82E8-8FC9E4EDE1B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44657DFF-FC50-40E9-82E8-8FC9E4EDE1B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Ashampoo.Translation.Systems.Formats.JavaProperties.Test.csproj b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Ashampoo.Translation.Systems.Formats.JavaProperties.Test.csproj
index 0eb00a8..75065aa 100644
--- a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Ashampoo.Translation.Systems.Formats.JavaProperties.Test.csproj
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Ashampoo.Translation.Systems.Formats.JavaProperties.Test.csproj
@@ -1,7 +1,7 @@
- net7.0
+ net8.0
enable
enable
@@ -11,14 +11,10 @@
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/FormatTest.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/FormatTest.cs
index a4ddfa5..8147dd2 100644
--- a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/FormatTest.cs
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/FormatTest.cs
@@ -1,6 +1,6 @@
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
using Ashampoo.Translation.Systems.TestBase;
using FluentAssertions;
@@ -8,104 +8,36 @@ namespace Ashampoo.Translation.Systems.Formats.JavaProperties.Test;
public class FormatTest : FormatTestBase
{
- private readonly IFormatFactory _formatFactory;
-
- public FormatTest(IFormatFactory formatFactory)
- {
- _formatFactory = formatFactory;
- }
-
- [Fact]
- public void IsAssignableFrom()
- {
- IFormat format = CreateFormat();
- format.Should().BeAssignableTo(typeof(ITranslationUnits));
- }
-
[Fact]
public void NewFormat()
{
IFormat format = CreateFormat();
- format.Should().NotBeNull().And.BeEmpty();
+ format.Should().NotBeNull();
+ format.TranslationUnits.Should().BeEmpty();
format.Header.SourceLanguage.Should().BeNull();
- format.Header.TargetLanguage.Should().BeEmpty();
+ format.Header.TargetLanguage.Value.Should().BeEmpty();
}
[Fact]
public void ReadFromFile()
{
IFormat format =
- CreateAndReadFromFile("messages_de.properties", new FormatReadOptions() { TargetLanguage = "de-DE" });
+ CreateAndReadFromFile("messages_de.properties", new FormatReadOptions() { TargetLanguage = new Language("de-DE") });
const string id = "aboutTheApp";
- foreach (var unit in format)
+ foreach (var unit in format.TranslationUnits)
{
- unit.Should().ContainSingle();
+ unit.Translations.Should().ContainSingle();
}
- format.Count.Should().Be(186);
+ format.TranslationUnits.Count.Should().Be(186);
- var foundById = format[id];
+ var foundById = format.TranslationUnits.GetTranslationUnit(id);
foundById.Should().NotBeNull();
- var translationString = foundById!["de-DE"] as AbstractTranslationString;
+ var translationString = foundById.Translations.GetTranslation(new Language("de-DE"));
translationString.Should().NotBeNull();
translationString!.Value.Should().Be("Über Photos");
translationString.Comment.Should().BeNull();
- translationString.Id.Should().Be(id);
- }
-
- [Fact]
- public void ImportSuccessTest()
- {
- IFormat format =
- CreateAndReadFromFile("messages.properties", new FormatReadOptions() { TargetLanguage = "en-US" });
-
- const string id = "albums";
- const string value = "Import Test";
-
- var importedWithUnits = format.ImportMockTranslationWithUnits("en-US", id);
- importedWithUnits.Should().NotBeNull().And.ContainSingle();
- (format[id]?["en-US"] as ITranslationString)?.Value.Should().Be(value);
- }
-
- [Fact]
- public void NoMatchImportTest()
- {
- IFormat format =
- CreateAndReadFromFile("messages.properties", new FormatReadOptions() { TargetLanguage = "en-US" });
-
- const string id = "Not a matching Id";
-
- var imported = format.ImportMockTranslationWithUnits("en-US", id);
-
- imported.Should().BeEmpty();
- }
-
- [Fact]
- public void ImportEqualTranslationTest()
- {
- IFormat format =
- CreateAndReadFromFile("messages.properties", new FormatReadOptions() { TargetLanguage = "en-US" });
-
- const string id = "albums";
- const string value = "Albums";
-
- var imported = format.ImportMockTranslationWithUnits("en-US", id, value);
-
- imported.Should().BeEmpty();
- }
-
- [Fact]
- public async Task ConvertTest()
- {
- var mockFormat =
- MockFormatWithTranslationUnits.CreateMockFormatWithTranslationUnits("en-US", "Convert ID", "Convert Test");
- var assignOptions = new AssignOptions { TargetLanguage = "en-US", Filter = new DefaultTranslationFilter() };
- var javaProperties = await mockFormat.ConvertToAsync(_formatFactory, assignOptions);
-
- javaProperties.Should().NotBeNull().And.ContainSingle();
- javaProperties.First().Id.Should().Be("Convert ID");
- (javaProperties["Convert ID"]?["en-US"] as ITranslationString)?.Value.Should().Be("Convert Test");
}
}
\ No newline at end of file
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Startup.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Startup.cs
deleted file mode 100644
index a2bcddf..0000000
--- a/Ashampoo.Translation.Systems.Formats.JavaProperties.Test/Startup.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Ashampoo.Translation.Systems.Formats.JavaProperties.Test;
-
-public class Startup
-{
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddFormatFactory().RegisterFormat();
- }
-}
\ No newline at end of file
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties/Ashampoo.Translation.Systems.Formats.JavaProperties.csproj b/Ashampoo.Translation.Systems.Formats.JavaProperties/Ashampoo.Translation.Systems.Formats.JavaProperties.csproj
index fe444d1..163d193 100644
--- a/Ashampoo.Translation.Systems.Formats.JavaProperties/Ashampoo.Translation.Systems.Formats.JavaProperties.csproj
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties/Ashampoo.Translation.Systems.Formats.JavaProperties.csproj
@@ -1,10 +1,10 @@
- net7.0
+ net8.0
enable
enable
- ash-logo-icon-big-128x.png
+ true
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties/DependencyInjectionExtension.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties/DependencyInjectionExtension.cs
new file mode 100644
index 0000000..2806023
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties/DependencyInjectionExtension.cs
@@ -0,0 +1,31 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Ashampoo.Translation.Systems.Formats.JavaProperties;
+
+///
+/// Static class that contains extension methods for .
+///
+public static class DependencyInjection
+{
+ ///
+ /// Registers all necessary services for the Gengo format.
+ ///
+ ///
+ /// The to register the services with.
+ ///
+ ///
+ /// The for chaining.
+ ///
+ ///
+ /// Thrown if something went wrong during the registration.
+ ///
+ public static IServiceCollection AddJavaPropertiesFormatFeatures(this IServiceCollection services)
+ {
+ services.AddSingleton()
+ .AddSingleton>(sp => sp.GetRequiredService())
+ .AddSingleton>(sp => sp.GetRequiredService());
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormat.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormat.cs
index e97a893..b9c82ed 100644
--- a/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormat.cs
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormat.cs
@@ -2,17 +2,27 @@
using System.Text.RegularExpressions;
using Ashampoo.Translation.Systems.Formats.Abstractions;
using Ashampoo.Translation.Systems.Formats.Abstractions.IO;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using CommunityToolkit.Diagnostics;
-using IFormatProvider = Ashampoo.Translation.Systems.Formats.Abstractions.IFormatProvider;
namespace Ashampoo.Translation.Systems.Formats.JavaProperties;
-public partial class JavaPropertiesFormat : AbstractTranslationUnits, IFormat
+///
+/// Represents a Java properties file format.
+///
+public partial class JavaPropertiesFormat : IFormat
{
private static readonly Regex KeyValueRegex = MyRegex();
+
+ ///
public IFormatHeader Header { get; } = new DefaultFormatHeader();
- public FormatLanguageCount LanguageCount => FormatLanguageCount.OnlyTarget;
+
+ ///
+ public LanguageSupport LanguageSupport => LanguageSupport.OnlyTarget;
+
+ ///
+ public ICollection TranslationUnits { get; } = new List();
///
public void Read(Stream stream, FormatReadOptions? options = null)
@@ -30,7 +40,7 @@ public async Task ReadAsync(Stream stream, FormatReadOptions? options = null)
return;
}
- Guard.IsNotNullOrWhiteSpace(Header.TargetLanguage);
+ Guard.IsNotNullOrWhiteSpace(Header.TargetLanguage.Value);
using StreamReader reader = new(stream);
using LineReader lineReader = new(reader);
@@ -41,27 +51,27 @@ public async Task ReadAsync(Stream stream, FormatReadOptions? options = null)
// TODO: Can this be made Protected in a abstract class to avoid duplicate code?
private async Task ConfigureOptionsAsync(FormatReadOptions? options)
{
- if (string.IsNullOrWhiteSpace(options?.TargetLanguage))
+ if (string.IsNullOrWhiteSpace(options?.TargetLanguage.Value))
{
Guard.IsNotNull(options?.FormatOptionsCallback);
FormatStringOption targetLanguageOption = new("Target language", true);
FormatOptions formatOptions = new()
{
- Options = new FormatOption[]
- {
+ Options =
+ [
targetLanguageOption
- }
+ ]
};
await options.FormatOptionsCallback.Invoke(formatOptions); // Invoke callback
if (formatOptions.IsCanceled) return false;
- Header.TargetLanguage = targetLanguageOption.Value;
+ Header.TargetLanguage = Language.Parse(targetLanguageOption.Value);
}
else
{
- Header.TargetLanguage = options.TargetLanguage;
+ Header.TargetLanguage = (Language)options.TargetLanguage!;
}
return true;
@@ -72,13 +82,11 @@ private async Task ReadTranslations(LineReader reader)
await reader.SkipEmptyLinesAsync();
while (await reader.HasMoreLinesAsync())
{
- var translation = ParseLine(await reader.ReadLineAsync(), reader.LineNumber);
- var unit = new DefaultTranslationUnit(translation.Id) { translation };
- Add(unit);
+ TranslationUnits.Add(ParseLine(await reader.ReadLineAsync(), reader.LineNumber));
}
}
- private ITranslation ParseLine(string? line, int lineNumber)
+ private ITranslationUnit ParseLine(string? line, int lineNumber)
{
Guard.IsNotNullOrWhiteSpace(line);
@@ -89,8 +97,14 @@ private ITranslation ParseLine(string? line, int lineNumber)
var id = match.Groups["key"].Value;
var value = match.Groups["value"].Value;
-
- return new DefaultTranslationString(id, value, Header.TargetLanguage);
+ var translation = new DefaultTranslationString(id, value, Header.TargetLanguage);
+ return new DefaultTranslationUnit(id)
+ {
+ Translations =
+ {
+ translation
+ }
+ };
}
///
@@ -107,27 +121,17 @@ public async Task WriteAsync(Stream stream)
{
await using StreamWriter writer = new(stream, Encoding.Latin1);
- foreach (var translation in this.SelectMany(translationUnit => translationUnit))
+ foreach (var translationUnit in TranslationUnits)
{
- if (translation is AbstractTranslationString translationString)
+ foreach (var translation in translationUnit.Translations)
{
- await writer.WriteLineAsync($"{translationString.Id}={translationString.Value}");
+ await writer.WriteLineAsync($"{translationUnit.Id}={translation.Value}");
}
}
await writer.FlushAsync();
}
- ///
- public Func BuildFormatProvider()
- {
- return builder => builder.SetId("javaproperties")
- .SetSupportedFileExtensions(new[] { ".properties" })
- .SetFormatType()
- .SetFormatBuilder()
- .Create();
- }
-
[GeneratedRegex("(?.*?)=(?.*)")]
private static partial Regex MyRegex();
}
\ No newline at end of file
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesBuilder.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormatBuilder.cs
similarity index 68%
rename from Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesBuilder.cs
rename to Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormatBuilder.cs
index 2e3a1b3..cdcfb6a 100644
--- a/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesBuilder.cs
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormatBuilder.cs
@@ -1,18 +1,22 @@
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using CommunityToolkit.Diagnostics;
namespace Ashampoo.Translation.Systems.Formats.JavaProperties;
-public class JavaPropertiesBuilder : IFormatBuilderWithTarget
+///
+/// Builder for the .
+///
+public class JavaPropertiesFormatBuilder : IFormatBuilderWithTarget
{
- private string _targetLanguage = string.Empty;
+ private Language _targetLanguage = Language.Empty;
private readonly Dictionary _translations = new();
///
- public IFormat Build()
+ public JavaPropertiesFormat Build()
{
- Guard.IsNotNullOrWhiteSpace(_targetLanguage);
+ Guard.IsNotNullOrWhiteSpace(_targetLanguage.Value);
JavaPropertiesFormat format = new()
{
@@ -26,8 +30,8 @@ public IFormat Build()
{
DefaultTranslationUnit unit = new(translation.Key);
DefaultTranslationString translationString = new(translation.Key, translation.Value, _targetLanguage);
- unit.Add(translationString);
- format.Add(unit);
+ unit.Translations.Add(translationString);
+ format.TranslationUnits.Add(unit);
}
return format;
@@ -40,7 +44,7 @@ public void Add(string id, string target)
}
///
- public void SetTargetLanguage(string language)
+ public void SetTargetLanguage(Language language)
{
_targetLanguage = language;
}
@@ -52,7 +56,7 @@ public void SetTargetLanguage(string language)
///
public void SetHeaderInformation(IFormatHeader header)
{
- throw new NotSupportedException("Header information's are not supported by the JavaProperties format");
+ // Do nothing, JavaPropertiesFormat does not support header information
}
///
@@ -63,6 +67,6 @@ public void SetHeaderInformation(IFormatHeader header)
///
public void AddHeaderInformation(string key, string value)
{
- throw new NotSupportedException("Header information's are not supported by the JavaProperties format");
+ // Do nothing, JavaPropertiesFormat does not support header information
}
}
\ No newline at end of file
diff --git a/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormatProvider.cs b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormatProvider.cs
new file mode 100644
index 0000000..dc5e260
--- /dev/null
+++ b/Ashampoo.Translation.Systems.Formats.JavaProperties/JavaPropertiesFormatProvider.cs
@@ -0,0 +1,25 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+
+namespace Ashampoo.Translation.Systems.Formats.JavaProperties;
+
+///
+/// The format provider for the Java properties format.
+///
+public sealed class JavaPropertiesFormatProvider : IFormatProvider
+{
+ ///
+ public string Id { get; } = "javaProperties";
+ ///
+ public JavaPropertiesFormat Create() => new();
+
+ ///
+ public bool SupportsFileName(string fileName)
+ {
+ return SupportedFileExtensions.Any(ext => fileName.EndsWith(ext, StringComparison.OrdinalIgnoreCase));
+ }
+
+ ///
+ public string[] SupportedFileExtensions { get; } = [".properties"];
+ ///
+ public IFormatBuilder GetFormatBuilder() => new JavaPropertiesFormatBuilder();
+}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Ashampoo.Translation.Systems.Components.csproj b/src/Ashampoo.Translation.Systems.Components/src/Ashampoo.Translation.Systems.Components.csproj
deleted file mode 100644
index 96bb625..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Ashampoo.Translation.Systems.Components.csproj
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
- net7.0
- enable
- enable
- Ashampoo.Translation.Systems.Components
- tjorvenK
- ashampoo
- Package containg basic blazor components and services for creating a converter.
- LICENSE
- localization;blazor;razor;ASP.Net,c#,dotnet
- true
- true
- true
- true
- snupkg
- ash-logo-icon-big-128x.png
- v
- true
- prerelease
- README.md
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Components/DisplayFormat.razor b/src/Ashampoo.Translation.Systems.Components/src/Components/DisplayFormat.razor
deleted file mode 100644
index 29b5ba4..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Components/DisplayFormat.razor
+++ /dev/null
@@ -1,66 +0,0 @@
-@using Ashampoo.Translation.Systems.Formats.Abstractions
-@using Ashampoo.Translation.Systems.Formats.Abstractions.Translation
-@using MudBlazor
-
-@* ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract *@
-@if (Format is not null)
-{
-
-
-
-
-
-
-
-
- ID
-
- @if (!string.IsNullOrWhiteSpace(sourceLanguage))
- {
-
-
- Soure: @Format.Header.SourceLanguage
-
-
- }
-
-
- Target: @Format.Header.TargetLanguage
-
-
-
-
-
-
-
- @if (!string.IsNullOrWhiteSpace(sourceLanguage))
- {
-
-
-
- }
-
-
-
-
-
- @context.Id
- @if (Format.LanguageCount != FormatLanguageCount.OnlyTarget)
- {
-
-
-
- }
-
-
-
-
-
-
-
-
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Components/DisplayFormat.razor.cs b/src/Ashampoo.Translation.Systems.Components/src/Components/DisplayFormat.razor.cs
deleted file mode 100644
index 342c83e..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Components/DisplayFormat.razor.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using Ashampoo.Translation.Systems.Components.Services;
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-using Microsoft.AspNetCore.Components;
-
-namespace Ashampoo.Translation.Systems.Components.Components;
-
-///
-/// Component for displaying translations of a format in a table.
-///
-public partial class DisplayFormat : ComponentBase
-{
- ///
- /// The format to display, passed to the component as a parameter.
- ///
- [Parameter] public IFormat Format { get; set; } = default!;
-
- [Inject] private IFormatService FormatService { get; init; } = default!;
-
- private string sourceLanguage = default!;
- private string targetLanguage = default!;
-
- private string searchString = "";
- private bool filterForEmptyTranslations;
- private ITranslationUnit selectedTranslation = default!;
- private (string, string) backupTranslations = ("", "");
-
- ///
- /// Get the source and target languages from the instance in .
- ///
- ///
- /// A representing an asynchronous operation.
- public override async Task SetParametersAsync(ParameterView parameters)
- {
- if (parameters.TryGetValue(nameof(Format), out var format))
- {
- if (format is not null)
- {
- if (format.Header.SourceLanguage is null && format.LanguageCount != FormatLanguageCount.OnlyTarget)
- throw new ArgumentNullException(nameof(format.Header.SourceLanguage));
-
- sourceLanguage = format.Header.SourceLanguage ?? "";
- targetLanguage = format.Header.TargetLanguage;
- }
- }
-
- await base.SetParametersAsync(parameters);
- }
-
- ///
- /// Save the original translations before editing.
- ///
- ///
- private void BackupUnit(object element)
- {
- if (element is not ITranslationUnit unit) throw new ArgumentException("Expected ITranslationUnit.");
-
- var source = (unit.TryGet(sourceLanguage) as ITranslationString)?.Value ?? "";
- var target = (unit[targetLanguage] as ITranslationString)?.Value ?? "";
-
- backupTranslations = (source, target);
- }
-
- ///
- /// Reset the current changes.
- ///
- ///
- private void ResetTranslations(object element)
- {
- if (element is ITranslationUnit translationUnit)
- {
- if (!string.IsNullOrWhiteSpace(sourceLanguage))
- translationUnit.AsTranslationString(sourceLanguage).Value = backupTranslations.Item1;
-
- translationUnit.AsTranslationString(targetLanguage).Value = backupTranslations.Item2;
- }
- else
- throw new ArgumentException("Expected ITranslationUnit.");
- }
-
- ///
- /// Change the languages of translations.
- ///
- ///
- ///
- private async Task SwitchLanguage(string oldLanguage)
- {
- await FormatService.SwitchLanguageAsync(Format, oldLanguage);
-
- sourceLanguage = Format.Header.SourceLanguage ?? "";
- targetLanguage = Format.Header.TargetLanguage;
- }
-
- ///
- /// Required filter function from MudBlazor
- ///
- ///
- /// if the or one of the translations contains the search string;
- /// otherwise
- ///
- private bool FilterFunc1(ITranslationUnit unit) => FilterFunc(unit, searchString);
-
- ///
- /// Filter function for table.
- ///
- ///
- /// The translation unit to filter.
- ///
- ///
- /// The search string.
- ///
- ///
- /// if the or one of the translations contains ;
- /// otherwise .
- ///
- private bool FilterFunc(ITranslationUnit unit, string search)
- {
- if (filterForEmptyTranslations)
- return string.IsNullOrWhiteSpace((unit.TryGet(targetLanguage) as ITranslationString)?.Value);
-
- if (unit.Id.Contains(search)) return true;
- if ((unit.TryGet(sourceLanguage) as ITranslationString)?.Value.Contains(search) ?? false) return true;
- return (unit.TryGet(targetLanguage) as ITranslationString)?.Value.Contains(search) ?? false;
- }
-}
-
-///
-/// Provides extension methods for .
-///
-public static class TranslationExtensions
-{
- ///
- /// Get the translation string for the given language.
- ///
- ///
- /// The translation unit.
- ///
- ///
- /// The language to get the translation string for.
- ///
- ///
- /// The translation string for the given language.
- ///
- public static ITranslationString AsTranslationString(this ITranslationUnit unit, string language)
- {
- var translation = unit[language];
- return (ITranslationString)translation;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Components/ExportFormat.razor b/src/Ashampoo.Translation.Systems.Components/src/Components/ExportFormat.razor
deleted file mode 100644
index 4f7f31b..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Components/ExportFormat.razor
+++ /dev/null
@@ -1,6 +0,0 @@
-@using MudBlazor
-
-
- Export File
-
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Components/ExportFormat.razor.cs b/src/Ashampoo.Translation.Systems.Components/src/Components/ExportFormat.razor.cs
deleted file mode 100644
index 60ba613..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Components/ExportFormat.razor.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using Ashampoo.Translation.Systems.Components.Services;
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using Microsoft.AspNetCore.Components;
-
-namespace Ashampoo.Translation.Systems.Components.Components;
-
-///
-/// Component for exporting a format to a file.
-///
-public partial class ExportFormat : ComponentBase
-{
- ///
- /// The format to export, passed in from the parent component as a parameter.
- ///
- [Parameter] public IFormat? Format { get; set; }
-
- ///
- /// The name of the file to export to.
- ///
- [Parameter] public string FileName { get; set; } = "";
-
- [Inject] private IFormatService FormatService { get; init; } = default!;
-
- [Inject] private IFormatFactory FormatFactory { get; init; } = default!;
-
- [Inject] private IFileService FileService { get; init; } = default!;
-
- ///
- /// Export the to a file and save it to the computer.
- ///
- /// A representing an asynchronous operation.
- private async Task SaveFile()
- {
- if (Format is null) return;
-
- var newFormatId = await FormatService.GetFormatIdAsync(); // Get a new format id.
- if (newFormatId == null) return;
-
- // Convert the format into the new format
- var convertedFormat = await FormatService.ConvertToAsync(Format, newFormatId, FormatOptionsCallback);
- if (convertedFormat.Count == 0) return;
-
- // Get the format provider for the new format
- var formatProvider = FormatFactory.GetFormatProvider(convertedFormat);
-
- var ms = new MemoryStream();
- convertedFormat.Write(ms); // Write the converted format to the memory stream for async purposes.
- ms.Position = 0;
-
- var fileExtension = formatProvider.SupportedFileExtensions; // Get the file extension for the new format.
-
- await FileService.SaveFile(ms, FileName, fileExtension); // Save the file to the computer.
- }
-
- ///
- /// Callback to configure the options of the .
- ///
- ///
- ///
- private async Task FormatOptionsCallback(FormatOptions options)
- {
- await FormatService.ConfigureFormatOptionsAsync(options);
-
- return !options.IsCanceled;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Components/UploadFormat.razor b/src/Ashampoo.Translation.Systems.Components/src/Components/UploadFormat.razor
deleted file mode 100644
index bc5d3ae..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Components/UploadFormat.razor
+++ /dev/null
@@ -1,19 +0,0 @@
-@using Microsoft.AspNetCore.Components.Forms
-@using MudBlazor
-
-
- @if (processing)
- {
-
- Processing
- }
- else
- {
- @ChildContent
- }
-
-@if (uploadFailed)
-{
- @uploadFailedExceptionMessage
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Components/UploadFormat.razor.cs b/src/Ashampoo.Translation.Systems.Components/src/Components/UploadFormat.razor.cs
deleted file mode 100644
index c90c466..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Components/UploadFormat.razor.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using Ashampoo.Translation.Systems.Components.Services;
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using Microsoft.AspNetCore.Components;
-using Microsoft.AspNetCore.Components.Forms;
-using MudBlazor;
-
-namespace Ashampoo.Translation.Systems.Components.Components;
-
-///
-/// A component for uploading a file and creating a format object from it.
-///
-public partial class UploadFormat : ComponentBase
-{
- ///
- /// Callback when the file is uploaded, and the format is created.
- ///
- [Parameter] public EventCallback<(IFormat?, string)> OnFormatUploaded { get; init; }
-
- ///
- /// Child content to display in the upload button.
- ///
- [Parameter] public RenderFragment? ChildContent { get; set; }
-
- ///
- /// Bool indicating if uploading is disabled.
- ///
- [Parameter] public bool Disabled { get; set; } = false;
-
- ///
- /// The color of the upload button.
- ///
- [Parameter] public Color Color { get; set; } = Color.Primary;
-
- ///
- /// The variant of the upload button.
- ///
- [Parameter] public Variant Variant { get; set; } = Variant.Filled;
-
- ///
- /// Css class for the upload button.
- ///
- [Parameter] public string Class { get; set; } = "";
-
- ///
- /// The start icon of the upload button.
- ///
- [Parameter] public string StartIcon { get; set; } = Icons.Filled.CloudUpload;
-
- ///
- /// Callback to configure format options.
- ///
- [Parameter] public FormatOptionsCallback? FormatOptionsCallback { get; set; }
- ///
- /// Options for reading the format.
- ///
- [Parameter] public FormatReadOptions? FormatReadOptions { get; set; }
-
- [Inject] private IFormatService FormatService { get; init; } = default!;
-
- [Inject] private IFormatFactory FormatFactory { get; init; } = default!;
-
- private bool processing = false;
- private bool uploadFailed = false;
- private string uploadFailedExceptionMessage = "";
- private IFormat? format = null;
-
- private string id = Guid.NewGuid().ToString();
-
- private string fileName = "";
-
- ///
- /// Upload a file from the computer and create an .
- ///
- ///
- /// A representing an asynchronous operation.
- private async Task UploadFileAsync(InputFileChangeEventArgs e)
- {
- processing = true; // Disable the upload button.
- Disabled = true;
- uploadFailed = false;
- try
- {
- // Read the file, allow for a maximum of 1MB.
- var stream = e.File.OpenReadStream(1024000L);
- var ms = new MemoryStream();
- await stream.CopyToAsync(ms); // Copy the stream to a memory stream for async reading.
- ms.Position = 0;
-
- // Create the format.
- if(FormatReadOptions is not null)
- format = await FormatService.ReadFromStreamAsync(ms, e.File.Name, FormatReadOptions);
- else
- format = await FormatService.ReadFromStreamAsync(ms, e.File.Name, FormatOptionsCallback ?? OptionsCallback);
-
- // Get the file name
- fileName = Path.GetFileNameWithoutExtension(e.File.Name);
- }
- catch (Exception exception) // TODO: Handle?
- {
- uploadFailedExceptionMessage = exception.Message; // Display the exception message.
- uploadFailed = true; // Set the upload failed flag.
- format = null; // Clear the format.
- fileName = ""; // Clear the file name.
- }
-
- await OnFormatUploaded.InvokeAsync((format, fileName)); // Invoke the callback.
- processing = false; // Enable the upload button.
- Disabled = false;
- }
-
- ///
- /// Callback to configure the options for the .
- ///
- ///
- /// A representing an asynchronous operation.
- private async Task OptionsCallback(FormatOptions options)
- {
- await FormatService.ConfigureFormatOptionsAsync(options);
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/DependencyInjection.cs b/src/Ashampoo.Translation.Systems.Components/src/DependencyInjection.cs
deleted file mode 100644
index f4bbded..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/DependencyInjection.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Ashampoo.Translation.Systems.Components.Services;
-using MediatR;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Ashampoo.Translation.Systems.Components;
-
-
-internal record Dummy;
-
-///
-/// Extension methods for the
-///
-public static class DependencyInjection
-{
- ///
- /// Adds all required services from the component library to the .
- ///
- ///
- ///
- public static IServiceCollection AddBaseComponents(this IServiceCollection services)
- {
- services.AddScoped();
- services.AddScoped();
- services.AddMediatR(typeof(Dummy).Assembly);
-
- return services;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Dialogs/ConfigureFormatOptionsDialog.razor b/src/Ashampoo.Translation.Systems.Components/src/Dialogs/ConfigureFormatOptionsDialog.razor
deleted file mode 100644
index dbc7b01..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Dialogs/ConfigureFormatOptionsDialog.razor
+++ /dev/null
@@ -1,29 +0,0 @@
-@using Ashampoo.Translation.Systems.Formats.Abstractions
-@using MudBlazor
-
-
-
-
- @foreach (var formatOption in FormatOptions.Options)
- {
- if (formatOption is FormatStringOption stringOption)
- {
-
-
- }
-
- else if (formatOption is FormatFilterOption filterOption)
- {
-
- }
- }
-
-
-
-
- Cancel
- Ok
-
-
-
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Dialogs/ConfigureFormatOptionsDialog.razor.cs b/src/Ashampoo.Translation.Systems.Components/src/Dialogs/ConfigureFormatOptionsDialog.razor.cs
deleted file mode 100644
index 821295b..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Dialogs/ConfigureFormatOptionsDialog.razor.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using Microsoft.AspNetCore.Components;
-using MudBlazor;
-
-namespace Ashampoo.Translation.Systems.Components.Dialogs;
-
-///
-/// A dialog that allows the user to configure options for a format.
-///
-public partial class ConfigureFormatOptionsDialog : ComponentBase
-{
- [CascadingParameter] private MudDialogInstance MudDialog { get; set; } = default!;
-
-
- ///
- /// The format options to configure.
- ///
- [Parameter]
- public FormatOptions FormatOptions { get; init; } = default!;
-
- private MudForm? form;
-
- private void Submit()
- {
- form?.Validate();
- if (!form?.IsValid ?? true) return;
- MudDialog.Close(DialogResult.Ok(true));
- }
-
- private void Cancel()
- {
- form?.ResetValidation();
- MudDialog.Cancel();
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Dialogs/SelectFormatDialog.razor b/src/Ashampoo.Translation.Systems.Components/src/Dialogs/SelectFormatDialog.razor
deleted file mode 100644
index 76cef49..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Dialogs/SelectFormatDialog.razor
+++ /dev/null
@@ -1,18 +0,0 @@
-@using Ashampoo.Translation.Systems.Formats.Abstractions
-@using MudBlazor
-@inject IFormatFactory FormatFactory
-
-
-
-
- @foreach (var provider in FormatFactory.GetFormatProviders())
- {
- @provider.Id
- }
-
-
-
- Cancel
- Ok
-
-
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Dialogs/SelectFormatDialog.razor.cs b/src/Ashampoo.Translation.Systems.Components/src/Dialogs/SelectFormatDialog.razor.cs
deleted file mode 100644
index 82098da..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Dialogs/SelectFormatDialog.razor.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Microsoft.AspNetCore.Components;
-using MudBlazor;
-
-namespace Ashampoo.Translation.Systems.Components.Dialogs;
-
-///
-/// A dialog that allows the user to select a format.
-///
-public partial class SelectFormatDialog : ComponentBase
-{
- [CascadingParameter] private MudDialogInstance MudDialog { get; set; } = default!;
-
- private string? formatId;
- private bool error;
-
- private void Submit()
- {
- if (string.IsNullOrWhiteSpace(formatId))
- {
- error = true;
- return;
- }
- error = false;
- MudDialog.Close(DialogResult.Ok(formatId));
- }
-
-
- private void Cancel() => MudDialog.Cancel();
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/FormatUploadedNotificationHandler.cs b/src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/FormatUploadedNotificationHandler.cs
deleted file mode 100644
index 95bedef..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/FormatUploadedNotificationHandler.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using Ashampoo.Translation.Systems.Components.Notifications;
-using MediatR;
-using MudBlazor;
-
-namespace Ashampoo.Translation.Systems.Components.NotificationHandlers;
-
-///
-/// Handler for the .
-///
-public class FormatUploadedNotificationHandler : INotificationHandler
-{
- private readonly ISnackbar snackbar;
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The snackbar.
- ///
- public FormatUploadedNotificationHandler(ISnackbar snackbar)
- {
- this.snackbar = snackbar;
- }
-
- ///
- public Task Handle(FormatUploadedNotification notification, CancellationToken cancellationToken)
- {
- snackbar.Add("Uploaded Format.", Severity.Success);
-
- return Task.CompletedTask;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/ImportedTranslationsNotificationHandler.cs b/src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/ImportedTranslationsNotificationHandler.cs
deleted file mode 100644
index 08840ad..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/ImportedTranslationsNotificationHandler.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Ashampoo.Translation.Systems.Components.Notifications;
-using MediatR;
-using MudBlazor;
-
-namespace Ashampoo.Translation.Systems.Components.NotificationHandlers;
-
-///
-/// Handler for the .
-///
-public class ImportedTranslationsNotificationHandler : INotificationHandler
-{
- private readonly ISnackbar snackbar;
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The snackbar.
- ///
- public ImportedTranslationsNotificationHandler(ISnackbar snackbar)
- {
- this.snackbar = snackbar;
- }
-
- ///
- public Task Handle(ImportedTranslationsNotification notification, CancellationToken cancellationToken)
- {
- switch (notification.Count)
- {
- case 0:
- snackbar.Add("No new translations were imported.", Severity.Warning);
- break;
- case 1:
- snackbar.Add("1 new translations was imported.", Severity.Success);
- break;
- default:
- snackbar.Add($"{notification.Count} new translations were imported.", Severity.Success);
- break;
- }
-
- return Task.CompletedTask;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/SimpleWarningNotificationHandler.cs b/src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/SimpleWarningNotificationHandler.cs
deleted file mode 100644
index bf90c58..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/NotificationHandlers/SimpleWarningNotificationHandler.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using Ashampoo.Translation.Systems.Components.Notifications;
-using MediatR;
-using MudBlazor;
-
-namespace Ashampoo.Translation.Systems.Components.NotificationHandlers;
-
-///
-/// Handler for the .
-///
-public class SimpleWarningNotificationHandler : INotificationHandler
-{
- private readonly ISnackbar snackbar;
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The snackbar.
- ///
- public SimpleWarningNotificationHandler(ISnackbar snackbar)
- {
- this.snackbar = snackbar;
- }
-
- ///
- public Task Handle(SimpleWarning notification, CancellationToken cancellationToken)
- {
- snackbar.Add(notification.Message, Severity.Warning);
-
- return Task.CompletedTask;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Notifications/FormatUploadedNotification.cs b/src/Ashampoo.Translation.Systems.Components/src/Notifications/FormatUploadedNotification.cs
deleted file mode 100644
index 163c01b..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Notifications/FormatUploadedNotification.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using MediatR;
-
-namespace Ashampoo.Translation.Systems.Components.Notifications;
-
-///
-/// Notification for when a format was uploaded.
-///
-public record FormatUploadedNotification : INotification;
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Notifications/ImportedTranslationsNotification.cs b/src/Ashampoo.Translation.Systems.Components/src/Notifications/ImportedTranslationsNotification.cs
deleted file mode 100644
index a297441..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Notifications/ImportedTranslationsNotification.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using MediatR;
-
-namespace Ashampoo.Translation.Systems.Components.Notifications;
-
-///
-/// Notification for when a format was imported successfully.
-///
-///
-/// The number of translations imported.
-///
-public record ImportedTranslationsNotification(int Count) : INotification;
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Notifications/SimpleWarning.cs b/src/Ashampoo.Translation.Systems.Components/src/Notifications/SimpleWarning.cs
deleted file mode 100644
index 82ae274..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Notifications/SimpleWarning.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using MediatR;
-
-namespace Ashampoo.Translation.Systems.Components.Notifications;
-
-///
-/// Notification to display a simple warning message.
-///
-///
-/// The message to display.
-///
-public record SimpleWarning(string Message) : INotification;
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Pages/Converter.razor b/src/Ashampoo.Translation.Systems.Components/src/Pages/Converter.razor
deleted file mode 100644
index 95cf7d1..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Pages/Converter.razor
+++ /dev/null
@@ -1,31 +0,0 @@
-@page "/converter"
-@using Ashampoo.Translation.Systems.Components.Components
-@using Microsoft.AspNetCore.Components.Web
-@using MudBlazor
-
-
-Converter
-
-
-
-
- Upload File
-
-
-
-
- Import file
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Pages/Converter.razor.cs b/src/Ashampoo.Translation.Systems.Components/src/Pages/Converter.razor.cs
deleted file mode 100644
index 2e027e4..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Pages/Converter.razor.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using Ashampoo.Translation.Systems.Components.Notifications;
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using MediatR;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Components;
-
-namespace Ashampoo.Translation.Systems.Components.Pages;
-
-///
-/// A page that allows the user to upload, display, import and export formats.
-///
-[AllowAnonymous]
-public partial class Converter : ComponentBase
-{
- [Inject] private IMediator Mediator { get; init; } = default!;
-
- private IFormat? format;
- private string fileName = "";
-
- ///
- /// Callback to be invoked when the user uploaded a format.
- ///
- ///
- private async Task FormatUploaded((IFormat? format, string fileName) args)
- {
- format = args.format;
- fileName = args.fileName;
-
- await Mediator.Publish(new FormatUploadedNotification());
- }
-
- ///
- /// Callback to be invoked when the user imported a format.
- ///
- ///
- private async Task FormatImported((IFormat? format, string fileName) args)
- {
- if (format is null || args.format is null) return;
-
- var imported = format.ImportFrom(args.format);
-
- await Mediator.Publish(new ImportedTranslationsNotification(imported.Count));
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/README.md b/src/Ashampoo.Translation.Systems.Components/src/README.md
deleted file mode 100644
index c058f66..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# Ashampoo.Translation.Systems.Components
-Razor class library containing basic components and services for creating a converter with blazor.
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Services/FormatService.cs b/src/Ashampoo.Translation.Systems.Components/src/Services/FormatService.cs
deleted file mode 100644
index 1e85d74..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Services/FormatService.cs
+++ /dev/null
@@ -1,150 +0,0 @@
-using Ashampoo.Translation.Systems.Components.Dialogs;
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using MudBlazor;
-
-namespace Ashampoo.Translation.Systems.Components.Services;
-
-///
-public class FormatService : IFormatService
-{
- private readonly IFormatFactory formatFactory;
-
- private readonly IDialogService dialogService;
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The dialog service.
- ///
- ///
- /// The format factory.
- ///
- public FormatService
- (
- IDialogService dialogService,
- IFormatFactory formatFactory
- )
- {
- this.dialogService = dialogService;
- this.formatFactory = formatFactory;
- }
-
-
- ///
- public IFormat ConvertTo(IFormat format, string convertFormatId,
- FormatOptionsCallback? formatOptionsCallback = null, AssignOptions? options = null)
- {
- return ConvertToAsync(format, convertFormatId, formatOptionsCallback, options).Result;
- }
-
- ///
- public async Task ReadFromStreamAsync(Stream stream, string fileName,
- FormatOptionsCallback? formatOptionsCallback = null)
- {
- var format = formatFactory.TryCreateFormatByFileName(fileName) ??
- throw new InvalidOperationException("File type not supported.");
-
- var targetLanguage = LanguageParser.TryParseLanguageId(fileName);
- FormatReadOptions formatReadOptions = new()
- {
- TargetLanguage = targetLanguage,
- FormatOptionsCallback = formatOptionsCallback
- };
-
- await format.ReadAsync(stream, formatReadOptions);
-
- return formatReadOptions.IsCancelled ? null : format;
- }
-
- ///
- public async Task ReadFromStreamAsync(Stream stream, string fileName, FormatReadOptions readOptions)
- {
- var format = formatFactory.TryCreateFormatByFileName(fileName) ??
- throw new InvalidOperationException("File type not supported.");
-
- await format.ReadAsync(stream, readOptions);
-
- return readOptions.IsCancelled ? null : format;
-
-
- }
-
- ///
- public async Task ConfigureFormatOptionsAsync(FormatOptions options, string title = "Configure format options")
- {
- var dialogOptions = new DialogOptions { CloseOnEscapeKey = true };
-
-
- var parameter = new DialogParameters { { "FormatOptions", options } };
-
- var dialogReference = dialogService.Show(title, parameter, dialogOptions);
- var result = await dialogReference.Result;
- options.IsCanceled = result is null || result.Cancelled;
- }
-
- ///
- public async Task ConvertToAsync(IFormat format, string convertFormatId,
- FormatOptionsCallback? formatOptionsCallback = null, AssignOptions? options = null)
- {
- var convertFormatType = formatFactory.GetFormatProvider(convertFormatId).FormatType;
-
- options ??= new AssignOptions
- {
- FormatOptionsCallback = formatOptionsCallback
- };
-
- var convertedFormat = await format.ConvertToAsync(convertFormatType, formatFactory, options);
-
- return convertedFormat;
- }
-
- ///
- public async Task SwitchLanguageAsync(IFormat format, string oldLanguage, string? newLanguage = null)
- {
- if (!oldLanguage.Equals(format.Header.SourceLanguage) && !oldLanguage.Equals(format.Header.TargetLanguage))
- throw new ArgumentException($"Did not find {oldLanguage} in the given format.");
-
- if (newLanguage is null)
- {
- FormatStringOption language = new("New language", true);
- FormatOptions options = new()
- {
- Options = new FormatOption[]
- {
- language
- }
- };
-
- await ConfigureFormatOptionsAsync(options, "Select new language");
-
- if (options.IsCanceled) return;
-
- newLanguage = language.Value;
- }
-
-
- var isTargetLanguage = oldLanguage.Equals(format.Header.TargetLanguage);
-
- foreach (var translationUnit in format)
- {
- var translation = translationUnit.TryGet(oldLanguage);
- if (translation is null) continue;
-
- translation.Language = newLanguage;
- }
-
- if (isTargetLanguage) format.Header.TargetLanguage = newLanguage;
- else
- format.Header.SourceLanguage = newLanguage;
- }
-
- ///
- public async Task GetFormatIdAsync()
- {
- var dialogReference = dialogService.Show("Select a Format");
- var result = await dialogReference.Result;
-
- return result.Cancelled ? null : result.Data.ToString();
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Services/IFileService.cs b/src/Ashampoo.Translation.Systems.Components/src/Services/IFileService.cs
deleted file mode 100644
index 703a2cc..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Services/IFileService.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-
-namespace Ashampoo.Translation.Systems.Components.Services;
-
-///
-/// A service for handling translation files.
-///
-public interface IFileService
-{
- ///
- /// Write the stream to a File.
- ///
- ///
- /// The stream to write the file to.
- ///
- ///
- /// The name of the file.
- ///
- ///
- /// The extensions of the file.
- ///
- ///
- Task SaveFile(Stream stream, string fileName, string[]? fileExtension = null);
-
- ///
- /// Save multiple formats in a zip file.
- ///
- ///
- /// The formats to save.
- ///
- ///
- /// The name of the file to save.
- ///
- ///
- /// The file extensions for the format files.
- ///
- ///
- Task SaveFormatsAsync(IEnumerable formats, string fileName, string[]? fileExtensions = null);
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Services/IFormatService.cs b/src/Ashampoo.Translation.Systems.Components/src/Services/IFormatService.cs
deleted file mode 100644
index 868a829..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Services/IFormatService.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-
-namespace Ashampoo.Translation.Systems.Components.Services;
-
-///
-/// A service for handling formats.
-///
-public interface IFormatService
-{
- ///
- /// Create a new instance of from the given stream.
- ///
- ///
- /// The stream to read the format from.
- ///
- ///
- /// The name of the file the stream is from.
- ///
- ///
- /// The options to use when creating the format.
- ///
- /// A new instance of IFormat
- /// ///
- /// Thrown if the file type is not supported.
- ///
- Task ReadFromStreamAsync(Stream stream, string fileName, FormatOptionsCallback? options = null);
-
- ///
- /// Create a new instance of from the given stream.
- ///
- ///
- /// The stream to read the format from.
- ///
- ///
- /// The name of the file the stream is from.
- ///
- ///
- /// The options to use when reading the format.
- ///
- ///
- /// A new instance of IFormat, or null if the format could not be read.
- ///
- ///
- /// Thrown if the file type is not supported.
- ///
- Task ReadFromStreamAsync(Stream stream, string fileName, FormatReadOptions readOptions);
-
- ///
- /// Convert the given instance of IFormat into the format specified by id.
- ///
- ///
- /// The format to convert.
- ///
- ///
- /// The id of the format to convert to.
- ///
- ///
- /// The callback to use when the options are insufficient to convert the format.
- ///
- ///
- /// The options to use when converting the format.
- ///
- /// A new instance of IFormat
- IFormat ConvertTo(IFormat format, string convertFormatId, FormatOptionsCallback? formatOptionsCallback = null,
- AssignOptions? options = null);
-
- ///
- /// Convert the given instance of IFormat into the format specified by id.
- ///
- ///
- /// The format to convert.
- ///
- ///
- /// The id of the format to convert to.
- ///
- ///
- /// The callback to use when the options are insufficient to convert the format.
- ///
- ///
- /// The options to use when converting the format.
- ///
- /// A new instance of IFormat
- Task ConvertToAsync(IFormat format, string convertFormatId,
- FormatOptionsCallback? formatOptionsCallback = null, AssignOptions? options = null);
-
- ///
- /// Configures the options specified in the CallbackOptions
- ///
- ///
- /// The options to configure.
- ///
- ///
- /// The title of the options dialog.
- ///
- /// A tuple, with Item1 = sourceLanguage, Item2 = targetLanguage
- public Task ConfigureFormatOptionsAsync(FormatOptions options, string title = "Configure Format Options");
-
- ///
- /// Displays a dialog to select an .
- ///
- ///
- /// A string containing the id of the selected format.
- ///
- Task GetFormatIdAsync();
-
- ///
- /// Switches the language of a format with a new one.
- ///
- ///
- /// The format to switch the language of.
- ///
- ///
- /// The old language to switch.
- ///
- ///
- /// The new language to switch to.
- ///
- ///
- public Task SwitchLanguageAsync(IFormat format, string oldLanguage, string? newLanguage = null);
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/Services/WebFileService.cs b/src/Ashampoo.Translation.Systems.Components/src/Services/WebFileService.cs
deleted file mode 100644
index ba7201e..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/Services/WebFileService.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System.IO.Compression;
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using Microsoft.JSInterop;
-
-namespace Ashampoo.Translation.Systems.Components.Services;
-
-///
-/// A service for handling translation files for web applications.
-///
-public class WebFileService : IFileService
-{
- private readonly IJSRuntime js;
-
- private IJSObjectReference? module;
-
- ///
- /// Creates a new instance of the class.
- ///
- ///
- /// The instance to use for interacting with the JavaScript environment.
- ///
- public WebFileService(IJSRuntime js)
- {
- this.js = js;
- }
-
- ///
- /// Load the js module for the file service when needed.
- ///
- private async Task LoadModule()
- {
- module = await js.InvokeAsync("import",
- "./_content/Ashampoo.Translation.Systems.Components/Scripts/WebFileService.js");
- }
-
- ///
- public async Task SaveFile(Stream stream, string fileName, string[]? fileExtension = null)
- {
- if (module is null) await LoadModule();
-
- using var streamRef = new DotNetStreamReference(stream: stream); // Create a reference to the stream for js.
-
- await js.InvokeVoidAsync("saveFile", streamRef, fileName, fileExtension);
- }
-
- ///
- public async Task SaveFormatsAsync(IEnumerable formats, string fileName, string[]? fileExtensions = null)
- {
- // Create a zip archive in a memory stream.
- await using var ms = new MemoryStream();
- using var archive = new ZipArchive(ms, ZipArchiveMode.Create, true);
-
- foreach (var format in formats)
- {
- // Create a new entry in the archive.
- var formatFile =
- archive.CreateEntry($"{fileName}-{format.Header.TargetLanguage}{fileExtensions?[0] ?? "txt"}");
- await using var entryStream = formatFile.Open(); // Open the entry stream.
-
- await using var tempMemoryStream = new MemoryStream();
- format.Write(tempMemoryStream); // Write the format to the memory stream for async purposes.
- tempMemoryStream.Seek(0, SeekOrigin.Begin);
- await tempMemoryStream.CopyToAsync(entryStream); // Copy the memory stream to the entry stream.
- }
-
- archive.Dispose(); // Dispose the archive.
-
- ms.Seek(0, SeekOrigin.Begin);
- await SaveFile(ms, fileName, new[] { ".zip" }); // Save the archive.
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/wwwroot/Scripts/WebFileService.js b/src/Ashampoo.Translation.Systems.Components/src/wwwroot/Scripts/WebFileService.js
deleted file mode 100644
index b5a2cb7..0000000
--- a/src/Ashampoo.Translation.Systems.Components/src/wwwroot/Scripts/WebFileService.js
+++ /dev/null
@@ -1,46 +0,0 @@
-window.saveFile = async (contentStreamReference, fileName, fileExtension) => {
- if (!'showSaveFilePicker' in window) return await downloadFileFromStream(fileName, contentStreamReference);
-
- const arrayBuffer = await contentStreamReference.arrayBuffer();
- const blob = new Blob([arrayBuffer]);
-
- const options = {
- suggestedName: fileName,
- types: [{
- description: '',
- accept: {'text/plain': fileExtension},
- }],
- };
-
- try {
-
- const fileHandle = await window.showSaveFilePicker(options);
- const writableStream = await fileHandle.createWritable();
- await writableStream.write(blob);
- await writableStream.close();
- } catch (e) {
- if (e.name === "AbortError") {
-
- } else {
- throw e;
- }
- }
-}
-
-async function downloadFileFromStream(fileName, contentStreamReference) {
- const arrayBuffer = await contentStreamReference.arrayBuffer();
- const blob = new Blob([arrayBuffer]);
- const url = URL.createObjectURL(blob);
-
- triggerFileDownload(fileName, url);
-
- URL.revokeObjectURL(url);
-}
-
-function triggerFileDownload(fileName, url) {
- const anchorElement = document.createElement('a');
- anchorElement.href = url;
- anchorElement.download = fileName ?? '';
- anchorElement.click();
- anchorElement.remove();
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Components/src/wwwroot/background.png b/src/Ashampoo.Translation.Systems.Components/src/wwwroot/background.png
deleted file mode 100644
index e15a3bde6e2bdb380df6a0b46d7ed00bdeb0aaa8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 378
zcmeAS@N?(olHy`uVBq!ia0vp^x**KK1SGdsl%54rjKx9jP7LeL$-D$|SkfJR9T^xl
z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33
zJwr2>%=KS^ie7oTIEF;HpS|GCbyPusHSqiXaCu3qf)82(9Gq&mZq2{Kq}M*X&MWtJ
zSi1Jo7ZzfImg%g=t(qo=wsSR2lZoP(Rj#3wacN=q0?Br(rXzgZEGK2$ID{|A=5S{xJEuzSH>!M+7wSY6hB<=-E^*n0W7
S8wY^CX7F_Nb6Mw<&;$S{dxtsz
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/AbstractFormatHeader.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/AbstractFormatHeader.cs
index ce3569a..5882f08 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/AbstractFormatHeader.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/AbstractFormatHeader.cs
@@ -1,13 +1,18 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
/// Abstract base class for the
///
-public abstract class AbstractFormatHeader : Dictionary, IFormatHeader
+public abstract class AbstractFormatHeader : IFormatHeader
{
///
- public abstract string TargetLanguage { get; set; }
+ public abstract Language TargetLanguage { get; set; }
+
+ ///
+ public abstract Language? SourceLanguage { get; set; }
///
- public abstract string? SourceLanguage { get; set; }
+ public abstract Dictionary AdditionalHeaders { get; set; }
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Ashampoo.Translation.Systems.Formats.Abstractions.csproj b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Ashampoo.Translation.Systems.Formats.Abstractions.csproj
index 54a111b..964bf8d 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Ashampoo.Translation.Systems.Formats.Abstractions.csproj
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Ashampoo.Translation.Systems.Formats.Abstractions.csproj
@@ -1,24 +1,10 @@
- net7.0
+ net8.0
enable
enable
- Ashampoo.Translation.Systems.Formats.Abstractions
- tjorvenK
- ashampoo
- Abstraction package for interfaces, default and base implementations
- LICENSE
- localization;translation;po;gengo
true
- true
- true
- true
- snupkg
- ash-logo-icon-big-128x.png
- v
- true
- prerelease.0
@@ -27,16 +13,17 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
-
-
-
+
+
+
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/AssignOptions.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/AssignOptions.cs
deleted file mode 100644
index 8ef81d8..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/AssignOptions.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions;
-
-///
-/// This class is used to store options when converting an implementation of into another implementation:
-///
-public class AssignOptions
-{
- ///
- /// A that is used to filter out translations while converting formats.
- ///
- public ITranslationFilter? Filter { get; set; }
-
- ///
- /// Specifies what language should be used for the source language while converting between formats.
- ///
- public string? SourceLanguage { get; set; }
-
- ///
- /// Specifies what language should be used for the target language while converting between formats.
- ///
- public string? TargetLanguage { get; set; }
-
- ///
- /// This callback is used by the converting format, when options are insufficient.
- ///
- public FormatOptionsCallback? FormatOptionsCallback { get; init; }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatFactory.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatFactory.cs
index 47fe2f1..70068b9 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatFactory.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatFactory.cs
@@ -5,7 +5,7 @@ namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
public class DefaultFormatFactory : IFormatFactory
{
- private readonly Dictionary formatProviders;
+ private readonly Dictionary> _formatProviders;
///
/// Initializes a new instance of the class.
@@ -13,66 +13,50 @@ public class DefaultFormatFactory : IFormatFactory
///
/// The format providers to use.
///
- public DefaultFormatFactory(IEnumerable formatProviders)
+ public DefaultFormatFactory(IEnumerable> formatProviders)
{
- this.formatProviders = formatProviders.ToDictionary(formatProvider => formatProvider.Id.ToLower(),
+ _formatProviders = formatProviders.ToDictionary(formatProvider => formatProvider.Id.ToLower(),
formatProvider => formatProvider);
}
///
public IFormat CreateFormat(string formatId)
{
- return formatProviders[formatId.ToLower()].Create();
+ return _formatProviders[formatId.ToLower()].Create();
}
///
- public IFormatProvider GetFormatProvider(IFormat format)
+ public IFormatProvider GetFormatProvider(T format) where T : class, IFormat
{
return TryGetFormatProvider(format) ?? throw new Exception("Format provider not found"); // TODO: throw specific exception
}
///
- public IFormatProvider GetFormatProvider(string formatId)
+ public IFormatProvider GetFormatProvider(string formatId)
{
return TryGetFormatProvider(formatId) ?? throw new Exception("Format provider not found"); // TODO: throw specific exception
}
///
- public IEnumerable GetFormatProviders() => formatProviders.Values;
+ public IEnumerable> GetFormatProviders() => _formatProviders.Values;
///
public IFormat? TryCreateFormatByFileName(string fileName)
{
// Test every format provider to see if it can handle the file name. Return the first one that can.
- return formatProviders.Values.FirstOrDefault(provider => provider.SupportsFileName(fileName))?.Create();
+ return _formatProviders.Values.FirstOrDefault(provider => provider.SupportsFileName(fileName))?.Create();
}
///
- public IFormatProvider? TryGetFormatProvider(IFormat format)
+ public IFormatProvider? TryGetFormatProvider(T format) where T : class, IFormat
{
- return formatProviders.Values.FirstOrDefault(provider => provider.FormatType == format.GetType());
+ return _formatProviders.Values.OfType>().FirstOrDefault();
}
///
- public IFormatProvider? TryGetFormatProvider(string formatId)
+ public IFormatProvider? TryGetFormatProvider(string formatId)
{
- return formatProviders.Values.FirstOrDefault(provider =>
+ return _formatProviders.Values.FirstOrDefault(provider =>
string.Equals(provider.Id, formatId, StringComparison.CurrentCultureIgnoreCase));
}
-
-
- ///
- public IFormatProvider GetFormatProvider(Type formatType)
- {
- if (!typeof(IFormat).IsAssignableFrom(formatType))
- throw new ArgumentException($"Expected Type of IFormat, got: {formatType} instead.");
- return TryGetFormatProvider(formatType) ?? throw new Exception( "Format provider not found"); // TODO: throw specific exception
- }
-
-
- ///
- public IFormatProvider? TryGetFormatProvider(Type formatType)
- {
- return formatProviders.Values.FirstOrDefault(provider => provider.FormatType == formatType);
- }
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatHeader.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatHeader.cs
index 84f4da5..0088bf1 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatHeader.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatHeader.cs
@@ -1,3 +1,5 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
@@ -6,8 +8,11 @@ namespace Ashampoo.Translation.Systems.Formats.Abstractions;
public class DefaultFormatHeader : AbstractFormatHeader
{
///
- public override string TargetLanguage { get; set; } = "";
+ public override Language TargetLanguage { get; set; } = Language.Empty;
+
+ ///
+ public override Language? SourceLanguage { get; set; }
///
- public override string? SourceLanguage { get; set; }
+ public override Dictionary AdditionalHeaders { get; set; } = [];
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatProvider.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatProvider.cs
deleted file mode 100644
index 885794b..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DefaultFormatProvider.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-namespace Ashampoo.Translation.Systems.Formats.Abstractions;
-
-///
-/// Default implementation of the interface .
-///
-internal class DefaultFormatProvider : IFormatProvider
-{
- public DefaultFormatProvider
- (
- string id,
- string[] supportedFileExtensions,
- Type formatType,
- Type formatBuilderType
- )
- {
- Id = id;
- SupportedFileExtensions = supportedFileExtensions;
- FormatType = formatType;
- FormatBuilderType = formatBuilderType;
- }
-
- public string Id { get; }
-
- public string[] SupportedFileExtensions { get; }
-
- public Type FormatType { get; }
- public Type FormatBuilderType { get; }
-
- public IFormat Create()
- {
- // create new instance from type
- return Activator.CreateInstance(FormatType) as IFormat ??
- throw new InvalidOperationException("Could not create instance of format.");
- }
-
- public IFormatBuilder GetFormatBuilder()
- {
- // create new instance from type
- return Activator.CreateInstance(FormatBuilderType) as IFormatBuilder ??
- throw new InvalidOperationException("Could not create instance of format builder.");
- }
-
- public bool SupportsFileName(string fileName)
- {
- return SupportedFileExtensions.Any(ext => fileName.EndsWith(ext, StringComparison.OrdinalIgnoreCase));
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DependencyInjection.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DependencyInjection.cs
deleted file mode 100644
index 223b08d..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/DependencyInjection.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions;
-
-///
-/// Static class that contains extension methods for .
-///
-public static class DependencyInjection
-{
- ///
- /// Register the in the .
- ///
- ///
- /// The to add the to.
- ///
- ///
- /// The so that additional calls can be chained.
- ///
- public static IServiceCollection AddFormatFactory(this IServiceCollection services)
- {
- services.AddSingleton();
-
- return services;
- }
-
- ///
- /// Options class for configuring the service registration of .
- ///
- public class FormatFactoryOptions
- {
- ///
- /// A list of strings representing
- /// file paths to plugins that will be loaded.
- ///
- public List PluginPaths { get; } = new();
- }
-
- ///
- /// Extension method that registers the in the .
- ///
- ///
- /// The to add the to.
- ///
- ///
- /// The options to configure the .
- ///
- ///
- /// The so that additional calls can be chained.
- ///
- public static IServiceCollection AddFormatFactory(this IServiceCollection services,
- Action configuration)
- {
- FormatFactoryOptions options = new();
- configuration(options);
-
- services.AddSingleton(sp =>
- {
- var loggerFactory = sp.GetRequiredService();
- var logger = loggerFactory.CreateLogger();
-
- var loader = new FormatProviderLoader(logger);
- foreach (var path in options.PluginPaths)
- {
- loader.LoadPlugins(path);
- }
-
- return new DefaultFormatFactory(loader.FormatProviders);
- });
- return services;
- }
-
- ///
- /// Registers all necessary services and the format implementations.
- ///
- ///
- /// The to register the services with.
- ///
- ///
- /// The for chaining.
- ///
- ///
- /// Thrown if something went wrong during the registration.
- ///
- public static IServiceCollection RegisterFormats(this IServiceCollection services)
- {
- services.AddFormatFactory(configuration =>
- {
- var path = Path.GetDirectoryName(typeof(DependencyInjection).Assembly.Location);
-
- if (string.IsNullOrWhiteSpace(path))
- throw new ArgumentNullException(nameof(path), "Assembly location could not be found.");
-
- configuration.PluginPaths.Add(path);
- });
- return services;
- }
-
- ///
- /// Register the given with the service collection.
- ///
- ///
- /// The to add the to.
- ///
- ///
- /// The type of to register.
- ///
- ///
- /// The so that additional calls can be chained.
- ///
- public static IServiceCollection RegisterFormat(this IServiceCollection services) where T : IFormat
- {
- var format = Activator.CreateInstance();
- services.AddFormatProvider(format.BuildFormatProvider());
-
- return services;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatExtensions.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatExtensions.cs
deleted file mode 100644
index eb0f4c9..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatExtensions.cs
+++ /dev/null
@@ -1,239 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-using CommunityToolkit.Diagnostics;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions;
-
-///
-/// Provides extension methods for the interface.
-///
-public static class FormatExtensions
-{
- ///
- /// Validates if the generic is implemented by the format and returns it or throws an exception otherwise.
- ///
- ///
- ///
- ///
- ///
- public static T GetRequired(this IFormat format)
- {
- if (format is T result) return result;
- throw new UnsupportedFormatException(format,
- $"Interface '{typeof(T).FullName}' not supported by format '{format.GetType().FullName}'");
- }
-
- ///
- /// Imports translations from another format into the calling one
- ///
- ///
- ///
- ///
- public static IList ImportFrom(this IFormat format, ITranslationUnits formatToImport)
- {
- List imported = new();
- foreach (var translationUnit in formatToImport)
- {
- foreach (var translation in translationUnit)
- {
- var id = translationUnit.Id;
- var language = translation.Language;
- var value = (translation as ITranslationString)?.Value;
- if (value is null) throw new Exception("Expected translation string");
-
- if (format[id]?.TryGet(language) is not ITranslationString translationString) continue;
- if (translationString.Value.Equals(value)) continue;
-
- translationString.Value = value;
- imported.Add(translation);
- }
- }
-
- return imported;
- }
-
- ///
- /// Convert current instance of IFormat into the IFormat specified by the type.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// A new instance of .
- public static IFormat ConvertTo(this IFormat format, Type type, IFormatFactory formatFactory,
- AssignOptions options)
- {
- return ConvertToAsync(format, type, formatFactory, options).Result;
- }
-
- ///
- /// Convert current instance of IFormat into the IFormat specified by the type.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// A representing an asynchronous operation.
- public static async Task ConvertToAsync(this IFormat format, Type type, IFormatFactory formatFactory,
- AssignOptions options)
- {
- var formatBuilder = formatFactory.GetFormatProvider(type).GetFormatBuilder();
- var formatToConvertTo = formatBuilder switch
- {
- IFormatBuilderWithTarget targetBuilder => await BuildWithTarget(format, targetBuilder, options),
- IFormatBuilderWithSourceAndTarget targetAndSourceBuilder => await BuildWithSourceAndTarget(format,
- targetAndSourceBuilder, options),
- _ => throw new NotImplementedException()
- };
-
- return formatToConvertTo;
- }
-
- ///
- /// Convert current instance of IFormat into the IFormat specified by the type.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// A new instance of IFormat.
- public static T ConvertTo(this IFormat format, IFormatFactory formatFactory,
- AssignOptions options) where T : IFormat
- {
- var converted = ConvertToAsync(format, typeof(T), formatFactory, options).Result;
- if (converted is not T result) throw new ArgumentException("Expected format of type " + typeof(T).FullName);
-
- return result;
- }
-
- ///
- /// Convert current instance of IFormat into the IFormat specified by the type.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// A representing an asynchronous operation.
- public static async Task ConvertToAsync(this IFormat format, IFormatFactory formatFactory,
- AssignOptions options) where T : IFormat
- {
- var converted = await ConvertToAsync(format, typeof(T), formatFactory, options);
- if (converted is not T result) throw new ArgumentException("Expected format of type " + typeof(T).FullName);
-
- return result;
- }
-
- private static async Task BuildWithTarget
- (
- IFormat format,
- IFormatBuilderWithTarget formatBuilder,
- AssignOptions options
- )
- {
- if (!await ConfigureOptions(false, options)) throw new InvalidOperationException();
-
- var targetLanguage = options.TargetLanguage;
- Guard.IsNotNullOrWhiteSpace(targetLanguage, nameof(targetLanguage));
- formatBuilder.SetTargetLanguage(targetLanguage);
-
- var filter = options.Filter ?? new DefaultTranslationFilter();
-
- foreach (var unit in format)
- {
- if (!filter.IsValid(unit)) continue;
-
- var id = unit.Id;
- var translationString = unit.TryGet(targetLanguage) as ITranslationString;
-
- formatBuilder.Add(id, translationString?.Value ?? string.Empty);
- }
-
- return formatBuilder.Build();
- }
-
- private static async Task BuildWithSourceAndTarget
- (
- IFormat format,
- IFormatBuilderWithSourceAndTarget formatBuilder,
- AssignOptions options
- )
- {
- if (!await ConfigureOptions(true, options)) throw new InvalidOperationException();
- var filter = options.Filter ?? new DefaultTranslationFilter();
-
- var targetLanguage = options.TargetLanguage;
- var sourceLanguage = options.SourceLanguage;
-
- Guard.IsNotNullOrWhiteSpace(targetLanguage, nameof(targetLanguage));
- Guard.IsNotNullOrWhiteSpace(sourceLanguage, nameof(sourceLanguage));
-
- formatBuilder.SetTargetLanguage(targetLanguage);
- formatBuilder.SetSourceLanguage(sourceLanguage);
- formatBuilder.SetHeaderInformation(format.Header);
-
- foreach (var unit in format)
- {
- if (!filter.IsValid(unit)) continue;
-
- var id = unit.Id;
- var targetTranslationString = unit.TryGet(targetLanguage) as ITranslationString;
- var sourceTranslationString = unit.TryGet(sourceLanguage) as ITranslationString;
-
- formatBuilder.Add(id, sourceTranslationString?.Value ?? string.Empty,
- targetTranslationString?.Value ?? string.Empty);
- }
-
- return formatBuilder.Build();
- }
-
- private static async Task ConfigureOptions(bool sourceAndTarget, AssignOptions? assignOptions)
- {
- var setTargetLanguage = string.IsNullOrWhiteSpace(assignOptions?.TargetLanguage);
- var setSourceLanguage = string.IsNullOrWhiteSpace(assignOptions?.SourceLanguage) && sourceAndTarget;
- var setFilter = assignOptions?.Filter is null;
-
- if (!setTargetLanguage && !setSourceLanguage && !setFilter) return true;
-
- if (assignOptions?.FormatOptionsCallback is null)
- throw new InvalidOperationException("Callback for Format options required.");
-
- FormatStringOption targetLanguageOption = new("Target language", true);
- FormatStringOption sourceLanguageOption = new("Source language", true);
- FormatFilterOption onlyUntranslated = new("Only untranslated");
-
- List optionList = new();
-
- if (setSourceLanguage) optionList.Add(sourceLanguageOption);
- if (setTargetLanguage) optionList.Add(targetLanguageOption);
- if (setFilter) optionList.Add(onlyUntranslated);
-
- var formatOptions = new FormatOptions
- {
- Options = optionList.ToArray()
- };
-
- await assignOptions.FormatOptionsCallback.Invoke(formatOptions);
- if (formatOptions.IsCanceled) return false;
-
- if (setTargetLanguage) assignOptions.TargetLanguage = targetLanguageOption.Value;
- if (setSourceLanguage) assignOptions.SourceLanguage = sourceLanguageOption.Value;
- if (setFilter)
- assignOptions.Filter = onlyUntranslated.SetFilter
- ? new IsEmptyTranslationFilter(targetLanguageOption.Value)
- : assignOptions.Filter;
-
- return true;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatOptions.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatOptions.cs
index c253d76..2c565e3 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatOptions.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatOptions.cs
@@ -28,26 +28,6 @@ public record FormatStringOption(string Name, bool Required = false) : FormatOpt
public string Value { get; set; } = "";
}
-///
-/// Format option for a boolean value.
-/// Indicates whether a
-///
-/// should be applied.
-///
-///
-///
-///
-///
-///
-///
-public record FormatFilterOption(string Name, bool Required = false) : FormatOption(Name, Required)
-{
- ///
- /// The value of the option.
- ///
- public bool SetFilter { get; set; } = Required;
-}
-
///
/// Configuration object for a containing a list of FormatOptions.
///
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderBuilder.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderBuilder.cs
deleted file mode 100644
index 7225cc0..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderBuilder.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-namespace Ashampoo.Translation.Systems.Formats.Abstractions;
-
-///
-/// Builder to create an .
-///
-public class FormatProviderBuilder
-{
- private string formatProviderId = "";
- private string[] supportedFileExtensions = Array.Empty();
- private Type formatType = default!;
- private Type formatBuilderType = default!;
-
- ///
- /// Sets the format provider id.
- ///
- ///
- /// The id to set.
- ///
- ///
- /// The current instance of to allow chaining.
- ///
- public FormatProviderBuilder SetId(string id)
- {
- formatProviderId = id;
- return this;
- }
-
- ///
- /// Sets the supported file extensions.
- ///
- ///
- /// The file extensions to set.
- ///
- ///
- /// The current instance of to allow chaining.
- ///
- public FormatProviderBuilder SetSupportedFileExtensions(string[] fileExtensions)
- {
- this.supportedFileExtensions = fileExtensions;
- return this;
- }
-
- ///
- /// Sets the format type.
- ///
- ///
- /// The type of the .
- ///
- ///
- /// The current instance of to allow chaining.
- ///
- public FormatProviderBuilder SetFormatType() where T : IFormat
- {
- formatType = typeof(T);
- return this;
- }
-
- ///
- /// Sets the format builder type.
- ///
- ///
- /// The type of the .
- ///
- ///
- /// The current instance of to allow chaining.
- ///
- public FormatProviderBuilder SetFormatBuilder() where T : IFormatBuilder
- {
- formatBuilderType = typeof(T);
- return this;
- }
-
- ///
- /// Builds the .
- ///
- ///
- /// The .
- ///
- ///
- /// Thrown if the format provider id is not set, the format type is not set,
- /// or the supported file extensions are not set.
- ///
- public IFormatProvider Create()
- {
- if (string.IsNullOrEmpty(formatProviderId)) throw new InvalidOperationException("formatProviderId must be set");
- if (supportedFileExtensions is null || supportedFileExtensions.Length == 0)
- throw new InvalidOperationException("supportedFileExtensions must be set");
- if (formatType is null) throw new InvalidOperationException("formatType must be set");
-
- return new DefaultFormatProvider(formatProviderId, supportedFileExtensions, formatType, formatBuilderType);
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderExtensions.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderExtensions.cs
deleted file mode 100644
index c9ff4a0..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderExtensions.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions;
-
-///
-/// Static class that contains extension methods for .
-///
-public static class FormatProviderExtensions
-{
- ///
- /// Adds the format provider to the service collection.
- ///
- ///
- /// The service collection.
- ///
- ///
- /// Function that creates the format provider.
- ///
- ///
- public static IServiceCollection AddFormatProvider(this IServiceCollection services,
- Func formatProviderFactory)
- {
- services.AddSingleton(_ =>
- {
- var builder = new FormatProviderBuilder();
- return formatProviderFactory(builder);
- });
- return services;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderLoader.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderLoader.cs
deleted file mode 100644
index d23812d..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatProviderLoader.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System.Reflection;
-using System.Runtime.Loader;
-using Microsoft.Extensions.Logging;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions;
-
-internal class FormatProviderLoader
-{
- private readonly ILogger logger;
- public List FormatProviders { get; } = new();
-
- public FormatProviderLoader(ILogger logger)
- {
- this.logger = logger;
- }
-
- public void LoadPlugins(string path)
- {
- // Load all the assemblies in the given path
- var fileNames =
- Directory.GetFiles(path, "Ashampoo.Translation.Systems.Formats.*.dll", SearchOption.TopDirectoryOnly)
- .Select(p => Path.GetFileNameWithoutExtension(p)).ToArray();
-
- foreach (var filename in fileNames)
- {
- var assembly = LoadAssembly(filename);
- var provider = CreateFormatProvider(assembly);
- FormatProviders.AddRange(provider);
- }
- }
-
- private Assembly LoadAssembly(string fileName)
- {
- logger.LogInformation("loading assembly {FileName}", fileName);
-
- var assemblyName = new AssemblyName(fileName);
- return AssemblyLoadContext.Default.LoadFromAssemblyName(assemblyName);
- }
-
- private IEnumerable CreateFormatProvider(Assembly assembly)
- {
- var builder = new FormatProviderBuilder();
- foreach (var type in assembly.GetTypes())
- {
- if (typeof(IFormat).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
- {
- if (Activator.CreateInstance(type) is not IFormat format) continue;
- yield return format.BuildFormatProvider()(builder);
- }
- }
- }
-}
-
-internal class PluginLoadContext : AssemblyLoadContext
-{
- private readonly AssemblyDependencyResolver resolver;
-
- public PluginLoadContext(string pluginPath)
- {
- resolver = new AssemblyDependencyResolver(pluginPath);
- }
-
- protected override Assembly? Load(AssemblyName assemblyName)
- {
- var assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
-
- return assemblyPath != null ? LoadFromAssemblyPath(assemblyPath) : null;
- }
-
- protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
- {
- var libraryPath = resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
-
- return libraryPath != null ? LoadUnmanagedDllFromPath(libraryPath) : IntPtr.Zero;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatReadOptions.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatReadOptions.cs
index 20f7b0d..2928b06 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatReadOptions.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatReadOptions.cs
@@ -1,3 +1,5 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
@@ -8,11 +10,11 @@ public class FormatReadOptions
///
/// The target language of the format.
///
- public string? TargetLanguage { get; init; }
+ public Language TargetLanguage { get; init; } = Language.Empty;
///
/// The source language of the format.
///
- public string? SourceLanguage { get; init; }
+ public Language? SourceLanguage { get; init; }
///
/// Indicates whether the read has been cancelled.
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/GlobalUsings.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/GlobalUsings.cs
index 053b13e..8d2d750 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/GlobalUsings.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/GlobalUsings.cs
@@ -4,4 +4,3 @@
global using System.IO;
global using System.Threading.Tasks;
global using System.Text.RegularExpressions;
-global using System.Text;
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormat.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormat.cs
index 1c0b974..00f0331 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormat.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormat.cs
@@ -1,13 +1,14 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
+using System.Diagnostics.CodeAnalysis;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
/// Interface for a translation format.
///
-public interface IFormat : ITranslationUnits
+public interface IFormat
{
-
///
/// Reads the format from the given stream.
///
@@ -41,15 +42,11 @@ void Read(Stream stream, FormatReadOptions? options = null) // TODO: require opt
void Write(Stream stream);
///
- /// A function to build a for this format.
+ /// Writes the format to the given stream asynchronously.
///
- ///
- /// A function that takes a and returns a new .
- ///
- ///
- /// If the format does not support building a .
- ///
- Func BuildFormatProvider();
+ ///
+ ///
+ Task WriteAsync(Stream stream);
///
/// The containing the header information for this format.
@@ -59,5 +56,65 @@ void Read(Stream stream, FormatReadOptions? options = null) // TODO: require opt
///
/// Information about how many languages the format can handle.
///
- FormatLanguageCount LanguageCount { get; }
+ LanguageSupport LanguageSupport { get; }
+
+ ///
+ /// Gets the collection of translation units associated with the format.
+ ///
+ ICollection TranslationUnits { get; }
+}
+
+///
+/// Provides extension methods for collections of translation units.
+///
+public static class TranslationUnitCollectionExtensions
+{
+ ///
+ /// Tries to get a translation unit from the collection by its ID.
+ ///
+ /// The collection of translation units.
+ /// The ID of the translation unit to get.
+ /// When this method returns, contains the translation unit with the specified ID, if found; otherwise, null.
+ /// true if a translation unit with the specified ID is found; otherwise, false.
+ public static bool TryGetTranslationUnit(this ICollection translationUnits, string id,
+ [NotNullWhen(true)] out ITranslationUnit? translationUnit)
+ {
+ var foundTranslationUnit = translationUnits.FirstOrDefault(t => t.Id == id);
+ if (foundTranslationUnit is null)
+ {
+ translationUnit = null;
+ return false;
+ }
+
+ translationUnit = foundTranslationUnit;
+ return true;
+ }
+
+ ///
+ /// Gets a translation unit from the collection by its ID.
+ ///
+ /// The collection of translation units.
+ /// The ID of the translation unit to get.
+ /// The translation unit with the specified ID.
+ /// No translation unit with the specified ID is found.
+ public static ITranslationUnit GetTranslationUnit(this ICollection translationUnits, string id) =>
+ translationUnits.First(t => t.Id == id);
+
+ ///
+ /// Adds a new translation to the collection or updates an existing one.
+ ///
+ /// The collection of translations.
+ /// The language of the translation to add or update.
+ /// The new translation.
+ public static void AddOrUpdateTranslationUnit(this ICollection translations, Language language, ITranslation value)
+ {
+ var existingTranslation = translations.FirstOrDefault(t => t.Language == language);
+
+ if (existingTranslation is not null)
+ {
+ translations.Remove(existingTranslation);
+ }
+
+ translations.Add(value);
+ }
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilder.cs
index 021b7ae..f575c11 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilder.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilder.cs
@@ -1,9 +1,11 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
/// Interface for a builder for a .
///
-public interface IFormatBuilder
+public interface IFormatBuilder where T : class, IFormat
{
///
/// Builds the instance of .
@@ -11,9 +13,8 @@ public interface IFormatBuilder
///
/// The instance of .
///
- IFormat Build();
+ T Build();
-
///
/// Set the header information. All information will be added to the header and will overwrite
/// existing information. All previous information will be removed.
@@ -33,4 +34,12 @@ public interface IFormatBuilder
/// The value of the information.
///
void AddHeaderInformation(string key, string value);
+
+ ///
+ /// Set the target language.
+ ///
+ ///
+ /// The language to set.
+ ///
+ void SetTargetLanguage(Language language);
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderWithSourceAndTarget.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderWithSourceAndTarget.cs
index c4319a3..aef66ed 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderWithSourceAndTarget.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderWithSourceAndTarget.cs
@@ -1,9 +1,12 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
-/// Interface for a for formats that support two languages.
+/// Interface for a for formats that support two languages.
///
-public interface IFormatBuilderWithSourceAndTarget : IFormatBuilder
+/// is of type .
+public interface IFormatBuilderWithSourceAndTarget : IFormatBuilder where T : class, IFormat
{
///
/// Add a translation for the source and target language.
@@ -18,18 +21,12 @@ public interface IFormatBuilderWithSourceAndTarget : IFormatBuilder
/// The target translation.
///
void Add(string id, string source, string target);
+
///
/// Set the source language.
///
///
/// The language to set.
///
- void SetSourceLanguage(string language);
- ///
- /// Set the target language.
- ///
- ///
- /// The language to set.
- ///
- void SetTargetLanguage(string language);
+ void SetSourceLanguage(Language language);
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderWithTarget.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderWithTarget.cs
index d928a8d..b31099a 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderWithTarget.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatBuilderWithTarget.cs
@@ -1,9 +1,9 @@
namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
-/// Interface for a for formats that only support one language.
+/// Interface for a for formats that only support one language.
///
-public interface IFormatBuilderWithTarget : IFormatBuilder
+public interface IFormatBuilderWithTarget : IFormatBuilder where T : class, IFormat
{
///
/// Add a translation for the target language.
@@ -15,11 +15,4 @@ public interface IFormatBuilderWithTarget : IFormatBuilder
/// The translation.
///
void Add(string id, string target);
- ///
- /// Set the target language.
- ///
- ///
- /// The language to set.
- ///
- void SetTargetLanguage(string language);
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatFactory.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatFactory.cs
index de5afa7..55b5b45 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatFactory.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatFactory.cs
@@ -11,7 +11,7 @@ public interface IFormatFactory
///
/// A collection of all available format providers.
///
- IEnumerable GetFormatProviders();
+ IEnumerable> GetFormatProviders();
///
/// Creates a new instance of by the given .
@@ -45,7 +45,7 @@ public interface IFormatFactory
///
/// Thrown if the format provider for the specified format is not found.
///
- IFormatProvider GetFormatProvider(IFormat format);
+ IFormatProvider GetFormatProvider(T format) where T : class, IFormat;
///
/// Try to get the for the specified .
@@ -56,7 +56,7 @@ public interface IFormatFactory
///
/// The for the specified , or null if not found.
///
- IFormatProvider? TryGetFormatProvider(IFormat format);
+ IFormatProvider? TryGetFormatProvider(T format) where T : class, IFormat ;
///
/// Gets the format provider for a format with the specified id.
@@ -68,7 +68,7 @@ public interface IFormatFactory
///
/// Thrown if the format provider for the specified format is not found.
///
- IFormatProvider GetFormatProvider(string formatId);
+ IFormatProvider GetFormatProvider(string formatId);
///
/// Try to get the for the specified format id.
@@ -79,33 +79,5 @@ public interface IFormatFactory
///
/// The for the specified format id, or null if not found.
///
- IFormatProvider? TryGetFormatProvider(string formatId);
-
- ///
- /// Get the for the specified .
- ///
- ///
- /// The format type to get the format provider for.
- ///
- ///
- /// The for the specified .
- ///
- ///
- /// Thrown if the specified is not a supported format.
- ///
- ///
- /// Thrown if the format provider for the specified format is not found.
- ///
- IFormatProvider GetFormatProvider(Type formatType);
-
- ///
- /// Try to get the for the specified .
- ///
- ///
- /// The format type to get the format provider for.
- ///
- ///
- /// The for the specified , or null if not found.
- ///
- IFormatProvider? TryGetFormatProvider(Type formatType);
+ IFormatProvider? TryGetFormatProvider(string formatId);
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatHeader.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatHeader.cs
index 0a2df48..d8c7ed7 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatHeader.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatHeader.cs
@@ -1,16 +1,24 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
/// Interface for a header containing information about an .
///
-public interface IFormatHeader : IDictionary
+public interface IFormatHeader
{
///
/// The target language of the .
///
- string TargetLanguage { get; set; }
+ Language TargetLanguage { get; set; }
+
///
/// The source language of the .
///
- string? SourceLanguage { get; set; }
+ Language? SourceLanguage { get; set; }
+
+ ///
+ /// Gets or sets the additional headers associated with the format.
+ ///
+ Dictionary AdditionalHeaders { get; set; }
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatProvider.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatProvider.cs
index 6f74635..57722d6 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatProvider.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IFormatProvider.cs
@@ -4,7 +4,7 @@ namespace Ashampoo.Translation.Systems.Formats.Abstractions;
/// Interface for a provider for a specific .
/// Contains additional information about the format.
///
-public interface IFormatProvider
+public interface IFormatProvider where T : class, IFormat
{
///
/// Returns the id of the format provider.
@@ -15,7 +15,7 @@ public interface IFormatProvider
/// Creates a new instance of the format.
///
///
- IFormat Create();
+ T Create();
///
/// Returns true if the format provider supports the given file name, otherwise false.
@@ -29,20 +29,9 @@ public interface IFormatProvider
///
string[] SupportedFileExtensions { get; }
- ///
- /// Returns the type of the format implementation.
- ///
- Type FormatType { get; }
-
- ///
- /// Returns the type of the format builder implementation.
- ///
- ///
- Type FormatBuilderType { get; }
-
///
/// Creates a new instance of the format builder.
///
///
- IFormatBuilder GetFormatBuilder();
+ IFormatBuilder GetFormatBuilder();
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IO/LineReader.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IO/LineReader.cs
index e9dccac..09a414d 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IO/LineReader.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/IO/LineReader.cs
@@ -6,8 +6,8 @@ namespace Ashampoo.Translation.Systems.Formats.Abstractions.IO;
///
public class LineReader : IDisposable
{
- private readonly TextReader textReader;
- private string? lastLine;
+ private readonly TextReader _textReader;
+ private string? _lastLine;
///
/// The number of the line that is currently being read.
@@ -23,7 +23,7 @@ public class LineReader : IDisposable
public LineReader(TextReader reader)
{
// This should create a Thread-Safe StreamReader.
- textReader = TextReader.Synchronized(reader);
+ _textReader = TextReader.Synchronized(reader);
}
///
@@ -44,14 +44,14 @@ public LineReader(TextReader reader)
///
public async Task ReadLineAsync()
{
- if (lastLine is null)
+ if (_lastLine is null)
{
LineNumber++;
- return await textReader.ReadLineAsync();
+ return await _textReader.ReadLineAsync();
}
- var line = lastLine;
- lastLine = null;
+ var line = _lastLine;
+ _lastLine = null;
return line;
}
@@ -75,12 +75,12 @@ public LineReader(TextReader reader)
///
public async Task PeekLineAsync()
{
- if (lastLine is not null) return lastLine;
+ if (_lastLine is not null) return _lastLine;
LineNumber++;
- lastLine = await textReader.ReadLineAsync();
+ _lastLine = await _textReader.ReadLineAsync();
- return lastLine;
+ return _lastLine;
}
///
@@ -112,7 +112,7 @@ EndOfStream won't work with all async. stream implementations.
///
public void Dispose()
{
- textReader.Dispose();
+ _textReader.Dispose();
GC.SuppressFinalize(this);
}
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatLanguageCount.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/LanguageSupport.cs
similarity index 93%
rename from src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatLanguageCount.cs
rename to src/Ashampoo.Translation.Systems.Formats.Abstractions/src/LanguageSupport.cs
index ef7b94d..571e657 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/FormatLanguageCount.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/LanguageSupport.cs
@@ -3,16 +3,18 @@ namespace Ashampoo.Translation.Systems.Formats.Abstractions;
///
/// Enum to differentiate between different translation format types.
///
-public enum FormatLanguageCount
+public enum LanguageSupport
{
///
/// Indicates that the format only has support for one language.
///
OnlyTarget,
+
///
/// Indicates that the format has support for two languages.
///
SourceAndTarget,
+
///
/// Indicates that the format has support for multiple languages.
///
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Models/Language.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Models/Language.cs
new file mode 100644
index 0000000..8548974
--- /dev/null
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Models/Language.cs
@@ -0,0 +1,35 @@
+using StronglyTypedIds;
+
+namespace Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
+///
+/// Represents a strongly typed identifier for a language.
+///
+[StronglyTypedId(Template.String)]
+public readonly partial struct Language;
+
+///
+/// Provides extension methods for the Language struct.
+///
+public static class LanguageExtensions
+{
+ ///
+ /// Checks if the Language instance is null or its value is whitespace.
+ ///
+ /// The Language instance to check.
+ /// True if the Language instance is null or its value is whitespace, otherwise false.
+ public static bool IsNullOrWhitespace(this Language? language)
+ {
+ return string.IsNullOrWhiteSpace(language?.Value);
+ }
+
+ ///
+ /// Checks if the value of the Language instance is whitespace.
+ ///
+ /// The Language instance to check.
+ /// True if the value of the Language instance is whitespace, otherwise false.
+ public static bool IsNullOrWhitespace(this Language language)
+ {
+ return string.IsNullOrWhiteSpace(language.Value);
+ }
+}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationString.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationString.cs
index 5e2b450..1f9ebd8 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationString.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationString.cs
@@ -1,24 +1,25 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
///
-/// Abstract base class for the interface.
+/// Abstract base class for the interface.
///
-public abstract class AbstractTranslationString : ITranslationString
+public abstract class AbstractTranslationString : ITranslation
{
///
- public virtual string Value { get; set; } = "";
-
- ///
- public virtual string Id { get; set; } = "";
+ public string Value { get; set; }
- ///
- public virtual string? Comment { get; set; }
+ ///
+ /// Gets or sets the ID of the translation string.
+ ///
+ protected string Id { get; set; }
///
- public virtual bool IsEmpty => string.IsNullOrWhiteSpace(Value);
+ public string? Comment { get; set; }
///
- public virtual string Language { get; set; } = "";
+ public Language Language { get; set; }
///
/// Base constructor for the class.
@@ -35,7 +36,7 @@ public abstract class AbstractTranslationString : ITranslationString
///
/// The comment of the translation string.
///
- protected AbstractTranslationString(string id, string value, string language, string? comment = null)
+ protected AbstractTranslationString(string id, string value, Language language, string? comment = null)
{
Id = id;
Value = value;
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationUnit.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationUnit.cs
index 49845e7..6c13a0a 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationUnit.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationUnit.cs
@@ -3,9 +3,8 @@ namespace Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
///
/// Abstract base class for the interface.
///
-public abstract class AbstractTranslationUnit : HashSet, ITranslationUnit
+public abstract class AbstractTranslationUnit : ITranslationUnit
{
-
///
/// Base constructor for the class.
///
@@ -13,61 +12,13 @@ public abstract class AbstractTranslationUnit : HashSet, ITranslat
/// The id of the translation unit.
///
protected AbstractTranslationUnit(string id)
- : base(new TranslationUnitEqualityComparer())
{
Id = id;
}
///
public string Id { get; init; }
-
- ///
- /// Get or set the translation for the given language.
- ///
- /// The language of the you are trying to get or set.
- /// The you are trying to set.
- /// The is null or whitespace or the ist null.
- /// The could not be added.
- public ITranslation this[string language]
- {
- get { return this.First(x => x.Language == language); }
- set
- {
- if (string.IsNullOrWhiteSpace(language))
- throw new ArgumentNullException($"AbstractTranslationUnit: {nameof(language)} can not be null.");
- if (value is null)
- throw new ArgumentNullException($"AbstractTranslationUnit: {nameof(value)} can not be null.");
- if (Add(value)) return;
-
- RemoveWhere(x => x.Language == language);
- if (!Add(value))
- throw new InvalidOperationException(
- $"AbstractTranslationUnit: not able to add translation {value.Id}.");
- }
- }
-
- ///
- /// Try to get the translation for the given language.
- ///
- /// The language of the you are trying to get.
- /// The for the given language, or null if it could not be found.
- public ITranslation? TryGet(string language)
- {
- return this.FirstOrDefault(x => x.Language == language);
- }
-}
-
-internal class TranslationUnitEqualityComparer : IEqualityComparer
-{
- public bool Equals(ITranslation? x, ITranslation? y)
- {
- if (x is null || y is null) return false;
- return x.Language == y.Language; // Translation is unique by language.
- }
-
- public int GetHashCode(ITranslation obj)
- {
- return HashCode.Combine(obj.Language);
- }
+ ///
+ public ICollection Translations { get; } = new HashSet();
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationUnits.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationUnits.cs
deleted file mode 100644
index 296c8ff..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/AbstractTranslationUnits.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-
-///
-/// Abstract base class for the interface.
-///
-public abstract class AbstractTranslationUnits : HashSet
-{
- ///
- /// Base constructor for the class.
- ///
- ///
- /// The collection of objects
- /// to add to the object.
- ///
- protected AbstractTranslationUnits(IEnumerable collection)
- : base(collection, new TranslationUnitsEqualityComparer())
- {
- }
-
- ///
- /// Base constructor for the class.
- ///
- protected AbstractTranslationUnits()
- : base(new TranslationUnitsEqualityComparer())
- {
- }
-
- ///
- /// The id of the object.
- ///
- public virtual string Id { get; init; } = "";
-
- ///
- /// Get or set the translation unit for the given id.
- ///
- /// The id of the
- /// The you are trying to set.
- /// The id is null or whitespace, or the value is null.
- /// The could not be added.
- public ITranslationUnit? this[string id]
- {
- //TODO: dont return null if not found, make try get instead
- get { return this.FirstOrDefault(x => x.Id == id); }
- set
- {
- if (string.IsNullOrWhiteSpace(id))
- throw new ArgumentNullException($"AbstractTranslationUnits: {nameof(id)} can not be null.");
- if (value is null)
- throw new ArgumentNullException($"AbstractTranslationUnits: {nameof(value)} can not be null");
-
- if (Add(value)) return;
- RemoveWhere(x => x.Id == id);
- if (!Add(value))
- throw new InvalidOperationException(
- $"AbstractTranslationUnits: not able to add translation unit {value.Id}.");
- }
- }
-}
-
-internal class TranslationUnitsEqualityComparer : IEqualityComparer
-{
- public bool Equals(ITranslationUnit? x, ITranslationUnit? y)
- {
- if (x is null || y is null) return false;
- return x.Id == y.Id; // TranslationUnit is unique by id
- }
-
- public int GetHashCode(ITranslationUnit obj)
- {
- return HashCode.Combine(obj.Id);
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/DefaultTranslationString.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/DefaultTranslationString.cs
index f4f8dda..bf31016 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/DefaultTranslationString.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/DefaultTranslationString.cs
@@ -1,7 +1,9 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
///
-/// Default implementation of the interface.
+/// Default implementation of the interface.
///
public class DefaultTranslationString : AbstractTranslationString
{
@@ -20,7 +22,7 @@ public class DefaultTranslationString : AbstractTranslationString
///
/// The comment of the translation string.
///
- public DefaultTranslationString(string id, string value, string language, string? comment = null) : base(id, value,
+ public DefaultTranslationString(string id, string value, Language language, string? comment = null) : base(id, value,
language, comment)
{
}
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslation.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslation.cs
index ecd3054..ad44257 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslation.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslation.cs
@@ -1,3 +1,5 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
///
@@ -6,22 +8,17 @@ namespace Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
public interface ITranslation
{
///
- /// Unique id of the translation.
+ /// The value of the translation.
///
- string Id { get; }
+ string Value { get; set; }
///
/// Optional comment for this translation.
///
string? Comment { get; set; }
- ///
- /// Indicates whether this translation is empty.
- ///
- bool IsEmpty { get; }
-
///
/// The language of the translation.
///
- string Language { get; set; }
+ Language Language { get; set; }
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslationString.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslationString.cs
deleted file mode 100644
index 5ef4b91..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslationString.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-
-///
-/// Interface for translations that only have a single value.
-///
-public interface ITranslationString : ITranslation
-{
- ///
- /// The value of the translation.
- ///
- string Value { get; set; }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslationUnit.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslationUnit.cs
index dcf38e2..340450e 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslationUnit.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/Translation/ITranslationUnit.cs
@@ -1,52 +1,75 @@
+using System.Diagnostics.CodeAnalysis;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
///
/// Interface for a container that contains .
///
-public interface ITranslationUnit : IEnumerable
+public interface ITranslationUnit
{
///
/// The id of the translation unit.
///
string Id { get; }
+
///
- /// The count of translations in the translation unit.
- ///
- int Count { get; }
-
- ///
- /// Gets the with the specified language.
+ /// Gets the collection of translations associated with the translation unit.
///
- ///
- /// The language of the translation.
- ///
- ITranslation this[string language] { get; set; }
- ///
- /// Try to get the with the specified language.
- ///
- ///
- /// The language of the translation.
- ///
- ///
- /// The with the specified language.
- ///
- ITranslation? TryGet(string language);
+ ICollection Translations { get; }
}
///
-/// Interface for a container that contains .
+/// Provides extension methods for collections of translations.
///
-public interface ITranslationUnits : IEnumerable
+public static class TranslationCollectionExtensions
{
///
- /// The count of translation units.
+ /// Tries to get a translation from the collection by its language.
///
- int Count { get; }
+ /// The collection of translations.
+ /// The language of the translation to get.
+ /// When this method returns, contains the translation with the specified language, if found; otherwise, null.
+ /// true if a translation with the specified language is found; otherwise, false.
+ public static bool TryGetTranslation(this ICollection translations, Language language,
+ [NotNullWhen(true)] out ITranslation? translation)
+ {
+ var foundTranslation = translations.FirstOrDefault(t => t.Language == language);
+ if (foundTranslation is null)
+ {
+ translation = null;
+ return false;
+ }
+
+ translation = foundTranslation;
+ return true;
+ }
+
+ ///
+ /// Gets a translation from the collection by its language.
+ ///
+ /// The collection of translations.
+ /// The language of the translation to get.
+ /// The translation with the specified language.
+ /// No translation with the specified language is found.
+ public static ITranslation GetTranslation(this ICollection translations, Language language) =>
+ translations.First(t => t.Language == language);
+
///
- /// Gets the with the specified id.
+ /// Adds a new translation to the collection or updates an existing one.
///
- ///
- /// The id of the .
- ///
- ITranslationUnit? this[string id] { get; set; }
+ /// The collection of translations.
+ /// The language of the translation to add or update.
+ /// The new translation.
+ public static void AddOrUpdateTranslation(this ICollection translations, Language language, ITranslation value)
+ {
+ var existingTranslation = translations.FirstOrDefault(t => t.Language == language);
+
+ if (existingTranslation is not null)
+ {
+ translations.Remove(existingTranslation);
+ }
+
+ translations.Add(value);
+ }
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/AndTranslationFilter.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/AndTranslationFilter.cs
deleted file mode 100644
index 0820004..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/AndTranslationFilter.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-
-///
-/// Implementation of , representing an And-combination of filters.
-///
-public class AndTranslationFilter : ITranslationFilter
-{
- private readonly List filters = new();
-
- ///
- /// Initializes a new instance of the class, with the given filters.
- ///
- ///
- public AndTranslationFilter(params ITranslationFilter[] filters)
- {
- this.filters.AddRange(filters);
- }
-
- ///
- /// Determine whether the given is accepted by every filter in the list.
- ///
- ///
- /// The to test.
- ///
- ///
- /// True if the is accepted by every filter in the list, false otherwise.
- ///
- public bool IsValid(ITranslationUnit translationUnit)
- {
- return filters.All(filter => filter.IsValid(translationUnit));
- }
-
- ///
- public override string ToString()
- {
- return "AND";
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/DefaultTranslationFilter.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/DefaultTranslationFilter.cs
deleted file mode 100644
index 343ccb4..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/DefaultTranslationFilter.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-
-///
-/// An implementation of the interface that is valid for every .
-///
-public class DefaultTranslationFilter : ITranslationFilter
-{
- ///
- public string? Language { get; init; }
-
-
- ///
- /// Check if the is valid for the filter.
- ///
- ///
- /// The to check.
- ///
- ///
- /// , this filter is valid for every .
- ///
- public bool IsValid(ITranslationUnit translationUnit) => true;
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/ITranslationFilter.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/ITranslationFilter.cs
deleted file mode 100644
index c7c03b2..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/ITranslationFilter.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-
-///
-/// Interface for translation filters.
-///
-public interface ITranslationFilter
-{
- ///
- /// Check if the is valid under the filter.
- ///
- /// The that is to be validated.
- /// A indicating whether the is valid under the filter.
- bool IsValid(ITranslationUnit translationUnit);
-
- ///
- /// The optional language for the filter, not all filters require a language.
- ///
- ///
- /// Thrown if the filter does not support a language.
- ///
- string? Language
- {
- get => throw new InvalidOperationException($"{GetType()} has no language support.");
- init => throw new InvalidOperationException($"{GetType()} has no language support.");
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/IsEmptyTranslationFilter.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/IsEmptyTranslationFilter.cs
deleted file mode 100644
index 7f3e261..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/IsEmptyTranslationFilter.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-
-///
-/// An implementation of that filters indicates
-/// whether a translation is empty for the specified language.
-///
-public class IsEmptyTranslationFilter : ITranslationFilter
-{
- ///
- public string? Language { get; init; }
-
- ///
- /// Initializes a new instance of the class, with the specified language.
- /// If no language is specified, the filter will match all languages.
- ///
- ///
- public IsEmptyTranslationFilter(string? language = null)
- {
- Language = language;
- }
-
-
- ///
- /// Determines whether the specified translation is empty for the specified language, or for all languages.
- ///
- ///
- /// The translation unit to test.
- ///
- ///
- /// if the translation is empty for the specified language, or for all languages;
- /// otherwise, .
- ///
- public bool IsValid(ITranslationUnit translationUnit)
- {
- if (Language is not null) return translationUnit.TryGet(Language)?.IsEmpty ?? true;
-
- return translationUnit.Any(translation => translation.IsEmpty);
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/MatchIdTranslationFilter.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/MatchIdTranslationFilter.cs
deleted file mode 100644
index ed0e5a5..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/MatchIdTranslationFilter.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-
-///
-/// An implementation of the interface, that checks, if the id of the matches a specified value.
-/// The Filter can be created to match "Contains", "StartsWith" or "EndsWith" for the id.
-///
-public class MatchIdTranslationFilter : ITranslationFilter
-{
- private readonly Regex regex;
-
- private MatchIdTranslationFilter(Regex regex)
- {
- this.regex = regex;
- }
-
- ///
- /// Creates a new that checks, if the id of an contains the input.
- ///
- /// The string to match against the id.
- /// A new instance of .
- public static MatchIdTranslationFilter Contains(string input)
- {
- return new MatchIdTranslationFilter(new Regex(input));
- }
-
- ///
- /// Creates a new that checks, if the id of an starts with the input.
- ///
- /// The string to match against the id.
- /// A new instance of .
- public static MatchIdTranslationFilter StartsWith(string input)
- {
- return new MatchIdTranslationFilter(new Regex($"^{input}"));
- }
-
- ///
- /// Creates a new that checks, if the id of an ends with the input.
- ///
- /// The string to match against the id.
- /// A new instance of .
- public static MatchIdTranslationFilter EndsWith(string input)
- {
- return new MatchIdTranslationFilter(new Regex($"{input}$"));
- }
-
- ///
- /// Checks, if the id of an matches the input.
- ///
- ///
- /// The to check against the input.
- ///
- ///
- /// True, if the id of the matches the input.
- ///
- public bool IsValid(ITranslationUnit translationUnit)
- {
- return regex.IsMatch(translationUnit.Id);
- }
-
- ///
- public override string ToString()
- {
- return $"Id = {regex}";
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/MatchValueTranslationFilter.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/MatchValueTranslationFilter.cs
deleted file mode 100644
index 6f0a26e..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/MatchValueTranslationFilter.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-
-///
-/// An implementation of the interface, that checks, if the value of an matches a specified value.
-/// The Filter can be created to match "Contains", "StartsWith" or "EndsWith" for the id.
-///
-public class MatchValueTranslationFilter : ITranslationFilter
-{
- private readonly Regex regex;
-
- ///
- public string? Language { get; init; }
-
- private MatchValueTranslationFilter(Regex regex, string? language = null)
- {
- this.regex = regex;
- Language = language;
- }
-
- ///
- /// Creates a new that checks, if the value of the for the given language contains the input.
- ///
- /// The string to match against the value.
- /// The language of the translation that will be checked.
- /// A new instance of .
- public static MatchValueTranslationFilter Contains(string input, string? language = null)
- {
- return new MatchValueTranslationFilter(new Regex(input), language);
- }
-
- ///
- /// Creates a new that checks, if the value of the for the given language starts with the input.
- ///
- /// The string to match against the value.
- /// The language of the translation that will be checked.
- /// A new instance of .
- public static MatchValueTranslationFilter StartsWith(string input, string? language = null)
- {
- return new MatchValueTranslationFilter(new Regex($"^{input}"), language);
- }
-
- ///
- /// Creates a new that checks, if the value of the for the given language ends with the input.
- ///
- /// The string to match against the value.
- /// The language of the translation that will be checked.
- /// A new instance of .
- public static MatchValueTranslationFilter EndsWith(string input, string? language = null)
- {
- return new MatchValueTranslationFilter(new Regex($"{input}$"), language);
- }
-
- ///
- /// Checks, if the value of the for the given language matches the input.
- ///
- ///
- /// The containing the that will be checked.
- ///
- ///
- /// True, if the value of the for the given language matches the input.
- ///
- public bool IsValid(ITranslationUnit translationUnit)
- {
- if (Language is not null)
- {
- var translation = translationUnit.TryGet(Language);
- var translationString = translation as ITranslationString;
-
- return translationString is not null && regex.IsMatch(translationString.Value);
- }
-
- foreach (var translation in translationUnit)
- {
- if (translation is ITranslationString translationString && regex.IsMatch(translationString.Value))
- return true;
- }
-
- return false;
- }
-
- ///
- public override string ToString()
- {
- return $"Value = {regex}";
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/OrTranslationFilter.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/OrTranslationFilter.cs
deleted file mode 100644
index 2dbaf5e..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilter/OrTranslationFilter.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-
-///
-/// Implementation of the interface, representing an OR-combination of other filters.
-///
-public class OrTranslationFilter : ITranslationFilter
-{
- private readonly List filters = new();
-
- ///
- /// Initializes a new instance of the class, with the given filters.
- ///
- ///
- public OrTranslationFilter(params ITranslationFilter[] filters)
- {
- this.filters.AddRange(filters);
- }
-
- ///
- /// Determines whether any of the filters in this filter matches the given .
- ///
- ///
- /// The to check.
- ///
- ///
- /// if any of the filters in this filter matches the given ;
- /// otherwise, .
- ///
- public bool IsValid(ITranslationUnit translationUnit)
- {
- return filters.Any(filter => filter.IsValid(translationUnit));
- }
-
- ///
- public override string ToString()
- {
- return "OR";
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Exceptions/ParserExceptions.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Exceptions/ParserExceptions.cs
deleted file mode 100644
index 2877f4a..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Exceptions/ParserExceptions.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser.Exceptions;
-
-///
-/// Exception that is thrown when a filter string is invalid.
-///
-public class ParserException : Exception
-{
- ///
- public ParserException(string message) : base(message)
- {
- }
-}
-
-///
-public class UnexpectedTokenException : ParserException
-{
- ///
- /// The token that was unexpected.
- ///
- public Token Token { get; init; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The token that was unexpected.
- ///
- public UnexpectedTokenException(Token token) : base(
- $"Unexpected token: '{token.Type}' at position: '{token.Position}'.")
- {
- Token = token;
- }
-}
-
-///
-public class ExpectedTokenException : ParserException
-{
- ///
- /// The token that was expected.
- ///
- public TokenType Type { get; init; }
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The token type that was expected.
- ///
- public ExpectedTokenException(TokenType type) : base($"Expected token: '{type}'.")
- {
- Type = type;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The token type that was expected.
- ///
- ///
- /// The position where the token was expected.
- ///
- public ExpectedTokenException(TokenType type, int position) : base(
- $"Expected token: '{type}' at position: '{position}'.")
- {
- Type = type;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Extensions/EnumeratorTokenExtensions.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Extensions/EnumeratorTokenExtensions.cs
deleted file mode 100644
index af36818..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Extensions/EnumeratorTokenExtensions.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser.Exceptions;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser.Extensions;
-
-///
-/// Static class, providing extension methods for parsing a from a string.
-///
-public static class EnumeratorTokenExtensions
-{
- ///
- /// Ensures, that the next is of the specified type.
- ///
- /// An containing tokens.
- /// The required type of the next token.
- /// There was no text token in the .
- /// The next token was not of the specified type.
- public static void Required(this IEnumerator token, TokenType type)
- {
- if (!token.MoveNext()) throw new ExpectedTokenException(type);
- if (token.Current.Type != type) throw new UnexpectedTokenException(token.Current);
- }
-
- ///
- /// Checks, if the current token in the is of the specified type.
- ///
- ///
- ///
- /// if the current token is of the specified type, otherwise .
- public static bool IsType(this IEnumerator token, TokenType type)
- {
- return token.Current.Type == type;
- }
-
- ///
- /// Get the value of the current token.
- ///
- /// The containing the tokens.
- ///
- public static string GetValue(this IEnumerator token)
- {
- return token.Current.Value;
- }
-
- ///
- /// Compares the value of the current token with the specified value.
- ///
- ///
- /// The containing the tokens.
- ///
- ///
- /// The value to compare with.
- ///
- ///
- /// if the value of the current token is equal to the specified value, otherwise .
- ///
- public static bool IsValue(this IEnumerator token, string value)
- {
- return string.Equals(token.Current.Value, value, StringComparison.CurrentCultureIgnoreCase);
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Lexer.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Lexer.cs
deleted file mode 100644
index 6b8b1cb..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Lexer.cs
+++ /dev/null
@@ -1,156 +0,0 @@
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser;
-
-///
-/// This class is used to parse a filter string and return a list of tokens.
-///
-public class Lexer : IDisposable
-{
- private readonly List tokens = new();
- private int position;
- private readonly TextReader reader;
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The input string to tokenize.
- ///
- public Lexer(string input)
- {
- reader = new StringReader(input);
- }
-
- ///
- /// Tokenizes the input string.
- ///
- /// A list of tokens.
- public List Tokenize()
- {
- ParseToken();
- return tokens;
- }
-
- //private bool hasMove();
- //private char nextChar(); -> position++
- // char peakChar('');
-
- ///
- /// Parses all the tokens in the input string.
- ///
- ///
- private void ParseToken()
- {
- while (HasMore())
- {
- var c = PeekChar();
- if (char.IsWhiteSpace(c))
- {
- NextChar();
- continue;
- }
-
- if (char.IsLetter(c))
- ParseName();
- else if (c == '"')
- ParseString();
- else if (c == '(')
- {
- tokens.Add(new Token { Type = TokenType.OpenBracket, Value = c.ToString(), Position = position });
- NextChar();
- }
- else if (c == ')')
- {
- tokens.Add(new Token { Type = TokenType.CloseBracket, Value = c.ToString(), Position = position });
- NextChar();
- }
- else if (c == ',')
- {
- tokens.Add(new Token { Type = TokenType.Comma, Value = c.ToString(), Position = position });
- NextChar();
- }
- else
- throw new Exception($"Unexpected character '{c}'");
- }
- }
-
- ///
- /// Parses a name token.
- ///
- private void ParseName()
- {
- StringBuilder builder = new();
- var start = position;
- char c;
- do
- {
- c = NextChar();
- builder.Append(c);
- c = PeekChar();
- } while (char.IsLetterOrDigit(c) || c == '.'); // read until we hit a dot or digit
-
- if (builder.ToString().ToLower().Equals("and")) // create AND token
- {
- tokens.Add(new Token { Type = TokenType.And, Value = "AND", Position = start });
- return;
- }
-
- if (builder.ToString().ToLower().Equals("or")) // create OR token
- {
- tokens.Add(new Token { Type = TokenType.Or, Value = "OR", Position = start });
- return;
- }
-
- tokens.Add(new Token { Type = TokenType.Name, Value = builder.ToString(), Position = start }); // create name token
- }
-
- ///
- /// Parses a string token.
- ///
- private void ParseString()
- {
- StringBuilder builder = new();
- var start = position;
- var c = NextChar(); //skip leading '"'
-
- while ((c = PeekChar()) != '"') // read until we hit a '"'
- {
- builder.Append(c);
- NextChar();
- }
-
- NextChar(); //skip trailing '"'
-
- tokens.Add(new Token { Type = TokenType.String, Value = builder.ToString(), Position = start }); // create string token
- }
-
- private bool HasMore()
- {
- return reader.Peek() != -1;
- }
-
- private char NextChar()
- {
- position++;
- return (char)reader.Read();
- }
-
- private char PeekChar()
- {
- return (char)reader.Peek();
- }
-
- ///
- public void Dispose()
- {
- reader.Dispose();
- GC.SuppressFinalize(this);
- }
-
- ///
- /// Destructor.
- ///
- ~Lexer()
- {
- Dispose();
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Parser.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Parser.cs
deleted file mode 100644
index 743ec32..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Parser.cs
+++ /dev/null
@@ -1,295 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser.Exceptions;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser.Extensions;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser;
-
-///
-/// Parses a filter string into a filter object.
-///
-public class Parser
-{
- ///
- /// The filter that was parsed from the input string.
- ///
- public ITranslationFilter Filter { get; private set; }
-
- ///
- /// Create a new parser.
- ///
- /// The filter string
- ///
- /// Thrown if the input string is invalid.
- ///
- public Parser(string input)
- {
- var tokens = new Lexer(input).Tokenize();
- Filter = ParseExpressions(tokens.GetEnumerator(), 0) ?? throw new Exception(); //TODO: throw correct exception
- }
-
- private enum Match
- {
- Value,
- Id
- }
-
- ///
- /// Parse the tokens into a filter object.
- ///
- ///
- /// The tokens to parse.
- ///
- ///
- /// The index of the current bracket.
- ///
- ///
- /// The filter object.
- ///
- ///
- /// Thrown if the tokens are invalid.
- ///
- ///
- /// Thrown if the filter string is invalid.
- ///
- ///
- /// Thrown if the filter string is invalid.
- ///
- private ITranslationFilter ParseExpressions(IEnumerator token, int bracketIndex)
- {
- if (!token.MoveNext())
- throw new ParserException("Expected name or open bracket.");
-
- ITranslationFilter ParseNameOrBracket()
- {
- if (token.IsType(TokenType.Name))
- return ParseName(token);
-
- if (token.IsType(TokenType.OpenBracket))
- return ParseExpressions(token, bracketIndex + 1);
-
- throw new UnexpectedTokenException(token.Current);
- }
-
- var left = ParseNameOrBracket();
-
- var currentPosition = token.Current.Position;
- if (!token.MoveNext())
- {
- if (bracketIndex > 0)
- throw new ExpectedTokenException(TokenType.CloseBracket, currentPosition);
- return left;
- }
-
- if (token.IsType(TokenType.And))
- {
- var right = ParseExpressions(token, bracketIndex);
- return new AndTranslationFilter(left, right);
- }
-
- if (token.IsType(TokenType.Or))
- {
- var right = ParseExpressions(token, bracketIndex);
- return new OrTranslationFilter(left, right);
- }
-
- if (token.IsType(TokenType.CloseBracket))
- {
- if (bracketIndex > 0)
- return left;
-
- throw new UnexpectedTokenException(token.Current);
- }
-
- throw new UnexpectedTokenException(token.Current);
- }
-
- ///
- /// Parse a name token.
- ///
- ///
- /// The tokens to parse.
- ///
- ///
- ///
- private ITranslationFilter ParseName(IEnumerator token)
- {
- if (token.IsValue("StartsWith") || token.IsValue("Value.StartsWith"))
- return ParseStartsWith(token, Match.Value);
- if (token.IsValue("EndsWith") || token.IsValue("Value.EndsWith")) return ParseEndsWith(token, Match.Value);
- if (token.IsValue("Contains") || token.IsValue("Value.Contains")) return ParseContains(token, Match.Value);
- if (token.IsValue("Id.StartsWith")) return ParseStartsWith(token, Match.Id);
- if (token.IsValue("Id.EndsWith")) return ParseEndsWith(token, Match.Id);
- if (token.IsValue("Id.Contains")) return ParseContains(token, Match.Id);
- if (token.IsValue("IsEmpty")) return ParseIsEmpty(token);
- throw new UnexpectedTokenException(token.Current);
- }
-
- ///
- /// Parse a name token, that corresponds to a contains filter.
- ///
- ///
- ///
- ///
- ///
- ///
- private ITranslationFilter ParseContains(IEnumerator token, Match match)
- {
- // Input string should look like this:
- // (Id. || Value.)Contains("{value}" || Contains("{value}", "{language}") when match = Match.Value
-
- token.Required(TokenType.OpenBracket);
- token.Required(TokenType.String);
-
- var value = token.GetValue();
-
- string? language = null;
-
- try
- {
- token.Required(TokenType.CloseBracket);
- }
- catch (Exception e)
- {
- //TODO: implement try catch correctly
- if (e is not UnexpectedTokenException) throw new Exception(e.Message);
-
- if (token.Current.Type != TokenType.Comma || match != Match.Value)
- throw new UnexpectedTokenException(token.Current);
-
- token.Required(TokenType.String);
- language = token.GetValue();
- token.Required(TokenType.CloseBracket);
- }
-
- ITranslationFilter filter = match switch
- {
- Match.Value => MatchValueTranslationFilter.Contains(value, language),
- Match.Id => MatchIdTranslationFilter.Contains(value),
- _ => throw new Exception($"Unexpected match type: {match}")
- };
-
- return filter;
- }
-
- ///
- /// Parse a name token, that corresponds to a starts with filter.
- ///
- ///
- ///
- ///
- ///
- ///
- private ITranslationFilter ParseStartsWith(IEnumerator token, Match match)
- {
- // Input string should look like this:
- // (Id. || Value.)Contains("{value}" || Contains("{value}", "{language}") when match = Match.Value
-
- token.Required(TokenType.OpenBracket);
- token.Required(TokenType.String);
-
- var value = token.GetValue();
- string? language = null;
-
- try
- {
- token.Required(TokenType.CloseBracket);
- }
- catch (Exception e)
- {
- if (e is not UnexpectedTokenException) throw new Exception(e.Message);
-
- if (token.Current.Type != TokenType.Comma || match != Match.Value)
- throw new UnexpectedTokenException(token.Current);
-
- token.Required(TokenType.String);
- language = token.GetValue();
- token.Required(TokenType.CloseBracket);
- }
-
- ITranslationFilter filter = match switch
- {
- Match.Value => MatchValueTranslationFilter.StartsWith(value, language),
- Match.Id => MatchIdTranslationFilter.StartsWith(value),
- _ => throw new Exception($"Unexpected match type: {match}")
- };
-
- return filter;
- }
-
- ///
- /// Parse a name token, that corresponds to an ends with filter.
- ///
- ///
- ///
- ///
- ///
- ///
- private ITranslationFilter ParseEndsWith(IEnumerator token, Match match)
- {
- // Input string should look like this:
- // (Id. || Value.)Contains("{value}" || Contains("{value}", "{language}") when match = Match.Value
-
- token.Required(TokenType.OpenBracket);
- token.Required(TokenType.String);
-
- var value = token.GetValue();
-
- string? language = null;
-
- try
- {
- token.Required(TokenType.CloseBracket);
- }
- catch (Exception e)
- {
- if (e is not UnexpectedTokenException) throw new Exception(e.Message);
-
- if (token.Current.Type != TokenType.Comma || match != Match.Value)
- throw new UnexpectedTokenException(token.Current);
-
- token.Required(TokenType.String);
- language = token.GetValue();
- token.Required(TokenType.CloseBracket);
- }
-
- ITranslationFilter filter = match switch
- {
- Match.Value => MatchValueTranslationFilter.EndsWith(value, language),
- Match.Id => MatchIdTranslationFilter.EndsWith(value),
- _ => throw new Exception($"Unexpected match type: {match}")
- };
-
- return filter;
- }
-
- ///
- /// Parse a name token, that corresponds to an is empty filter.
- ///
- ///
- ///
- ///
- ///
- private ITranslationFilter ParseIsEmpty(IEnumerator token)
- {
- // Input string should look like this:
- // isEmpty()
- token.Required(TokenType.OpenBracket);
-
- string? language = null;
-
- try
- {
- token.Required(TokenType.CloseBracket);
- }
- catch (Exception e)
- {
- if (e is not UnexpectedTokenException) throw new Exception(e.Message);
- if (token.Current.Type != TokenType.String) throw new UnexpectedTokenException(token.Current);
-
- language = token.GetValue();
- token.Required(TokenType.CloseBracket);
- }
-
- return new IsEmptyTranslationFilter(language);
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Token.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Token.cs
deleted file mode 100644
index d0a2215..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/src/TranslationFilterParser/Token.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser;
-
-///
-/// Enum representing the type of a .
-///
-public enum TokenType
-{
- ///
- /// The token represents the name of a filter.
- ///
- Name,
- ///
- /// The token represents a string literal.
- ///
- String,
- ///
- /// The token represents an And-Filter
- ///
- And,
- ///
- /// The token represents an Or-Filter
- ///
- Or,
- ///
- /// The token represents an open bracket.
- ///
- OpenBracket,
- ///
- /// The token represents a close bracket.
- ///
- CloseBracket,
- ///
- /// The token represents a comma.
- ///
- Comma
-}
-
-///
-/// Represents a token in a filter expression.
-///
-public class Token
-{
- ///
- /// The type of the token.
- ///
- public TokenType Type { get; init; }
- ///
- /// The value of the token.
- ///
- public string Value { get; init; } = string.Empty;
- ///
- /// The position of the token in the filter expression.
- ///
- public int Position { get; init; }
-
- ///
- public override string ToString()
- {
- return @$"{Type} = ""{Value}""";
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Ashampoo.Translation.Systems.Formats.Abstractions.Tests.csproj b/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Ashampoo.Translation.Systems.Formats.Abstractions.Tests.csproj
deleted file mode 100644
index 2c46e11..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Ashampoo.Translation.Systems.Formats.Abstractions.Tests.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
- net7.0
- enable
- false
-
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/LanguageParserTests.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/LanguageParserTests.cs
deleted file mode 100644
index d6e542c..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/LanguageParserTests.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using Xunit;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.Tests;
-
-public class LanguageParserTests
-{
- [Fact]
- public void ParseLanguageCountryTest()
- {
- const string filePath = "Snap14-en-us.json";
- var language = LanguageParser.TryParseLanguageId(filePath);
- Assert.Equal("en-US", language);
- }
-
- [Fact]
- public void ParseLanguageScriptTagCountryTest()
- {
- const string filePath = "Snap14-zh-Hant-TW.json";
- var language = LanguageParser.TryParseLanguageId(filePath);
- Assert.Equal("zh-Hant-TW", language);
- }
-
-
- [Fact]
- public void ValidLanguageCodeTest()
- {
- const string languageCodeChinese = "zh-Hant-TW";
- const string languageCodeEnglish = "en-US";
- var chineseIsValid = LanguageParser.IsValidLanguageCode(languageCodeChinese);
- var englishIsValid = LanguageParser.IsValidLanguageCode(languageCodeEnglish);
- Assert.True(chineseIsValid);
- Assert.True(englishIsValid);
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Lexer.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Lexer.cs
deleted file mode 100644
index 14de1c7..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Lexer.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser;
-using Xunit;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.Tests
-{
- public class LexerTest
- {
- [Fact]
- public void TokenizeX()
- {
- Lexer lexer = new(@"(aadasd");
- var tokens = lexer.Tokenize();
- }
-
- [Fact]
- public void Tokenize()
- {
- Lexer lexer = new(@"StartsWith(""Hello World"")");
- var tokens = lexer.Tokenize();
- }
-
- [Fact]
- public void Tokenize13()
- {
- Lexer lexer = new(@"(StartsWith(""Hello World""))");
- var tokens = lexer.Tokenize();
- }
-
- [Fact]
- public void Tokenize2()
- {
- Lexer lexer = new(@"StartsWith(""Hello World"") AND EndsWith(""sfsdfdsf"")");
- var tokens = lexer.Tokenize();
- }
-
- [Fact]
- public void TokenizeWithLanguage1()
- {
- Lexer lexer = new(@"StartsWith(""Hello World"", ""en-US"")");
- var tokens = lexer.Tokenize();
- }
-
- [Fact]
- public void TokenizeWithLanguage2()
- {
- Lexer lexer = new(@"StartsWith(""Hello World"") AND EndsWith(""sfsdfdsf"", ""en-US"")");
- var tokens = lexer.Tokenize();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/MatchValueFilter.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/MatchValueFilter.cs
deleted file mode 100644
index 7a30c67..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/MatchValueFilter.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-using Ashampoo.Translation.Systems.TestBase;
-using Xunit;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.Tests;
-
-public class MatchValueTranslationFilterTest
-{
- [Fact]
- public void StartsWith()
- {
- var unit = MockFormatWithTranslationUnits.CreateMockFormatWithTranslationUnits("en-US", "hello.id",
- "Hello World");
- Assert.NotNull(unit["hello.id"]);
- Assert.True(MatchValueTranslationFilter.StartsWith("Hello").IsValid(unit["hello.id"]!));
- Assert.False(MatchValueTranslationFilter.StartsWith("World").IsValid(unit["hello.id"]!));
-
- Assert.True(MatchValueTranslationFilter.StartsWith("Hello", "en-US").IsValid(unit["hello.id"]!));
- Assert.False(MatchValueTranslationFilter.StartsWith("World", "en-US").IsValid(unit["hello.id"]!));
-
- Assert.False(MatchValueTranslationFilter.StartsWith("Hello", "de-DE").IsValid(unit["hello.id"]!));
- Assert.False(MatchValueTranslationFilter.StartsWith("World", "de-DE").IsValid(unit["hello.id"]!));
- }
-
- [Fact]
- public void EndsWith()
- {
- var unit = MockFormatWithTranslationUnits.CreateMockFormatWithTranslationUnits("en-US", "hello.id",
- "Hello World");
- Assert.NotNull(unit["hello.id"]);
- Assert.True(MatchValueTranslationFilter.EndsWith("World").IsValid(unit["hello.id"]!));
- Assert.False(MatchValueTranslationFilter.EndsWith("Hello").IsValid(unit["hello.id"]!));
-
- Assert.True(MatchValueTranslationFilter.EndsWith("World", "en-US").IsValid(unit["hello.id"]!));
- Assert.False(MatchValueTranslationFilter.EndsWith("Hello", "en-US").IsValid(unit["hello.id"]!));
-
- Assert.False(MatchValueTranslationFilter.EndsWith("World", "de-DE").IsValid(unit["hello.id"]!));
- Assert.False(MatchValueTranslationFilter.EndsWith("Hello", "de-DE").IsValid(unit["hello.id"]!));
- }
-
- [Fact]
- public void Contains()
- {
- var unit = MockFormatWithTranslationUnits.CreateMockFormatWithTranslationUnits("en-US", "hello.id",
- "Hello World");
- Assert.NotNull(unit["hello.id"]);
- Assert.True(MatchValueTranslationFilter.Contains("lo Wo").IsValid(unit["hello.id"]!));
- Assert.False(MatchValueTranslationFilter.Contains("lolo").IsValid(unit["hello.id"]!));
-
- Assert.True(MatchValueTranslationFilter.Contains("lo Wo", "en-US").IsValid(unit["hello.id"]!));
- Assert.False(MatchValueTranslationFilter.Contains("lolo", "en-US").IsValid(unit["hello.id"]!));
-
- Assert.False(MatchValueTranslationFilter.Contains("lo Wo", "de-DE").IsValid(unit["hello.id"]!));
- Assert.False(MatchValueTranslationFilter.Contains("lolo", "de-DE").IsValid(unit["hello.id"]!));
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Parser.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Parser.cs
deleted file mode 100644
index 6bdc817..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Parser.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using System;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilterParser.Exceptions;
-using Xunit;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.Tests;
-
-public class ParserTest
-{
- [Fact]
- public void ParseStartsWith()
- {
- var parser = new Parser(@"StartsWith(""Hello"")");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseIsEmpty()
- {
- var parser = new Parser(@"isEmpty()");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseAnd()
- {
- var parser = new Parser(@"StartsWith(""Hello"") AND EndsWith(""World"")");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseMultipleAnd()
- {
- var parser = new Parser(@"StartsWith(""Hello"") AND EndsWith(""World"") AND StartsWith(""Ashampoo"")");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseOr()
- {
- var parser = new Parser(@"StartsWith(""Hello"") OR EndsWith(""World"")");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseMultipleOr()
- {
- var parser = new Parser(@"StartsWith(""Hello"") OR EndsWith(""World"") OR StartsWith(""Ashampoo"")");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseAndOr()
- {
- var parser = new Parser(@"StartsWith(""Hello"") AND EndsWith(""World"") OR StartsWith(""Ashampoo"")");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseSimpleBracket()
- {
- var parser = new Parser(@"(StartsWith(""Hello""))");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseComplexBracket()
- {
- var parser = new Parser(@"StartsWith(""Hello"") AND ( EndsWith(""World"") OR EndsWith(""Universe"") ) ");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseComplexBracket2()
- {
- var parser =
- new Parser(
- @"(StartsWith(""Hello"") OR StartsWith(""Hi"")) AND ( EndsWith(""World"") OR EndsWith(""Universe"") ) ");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseComplexBracket3()
- {
- var parser =
- new Parser(
- @"(StartsWith(""Hello"") OR StartsWith(""Hi"")) AND (Contains(""Ever"") OR Contains(""Never"")) AND ( EndsWith(""World"") OR EndsWith(""Universe"") ) ");
- var filter = parser.Filter;
- }
-
- [Fact]
- public void ParseInvalidBracketExpression()
- {
- Exception thrownException = Assert.Throws(() => new Parser(@"()"));
- Assert.Equal("Unexpected token: 'CloseBracket' at position: '1'.", thrownException.Message);
-
- thrownException = Assert.Throws(() => new Parser(@"("));
- Assert.Equal("Expected name or open bracket.", thrownException.Message);
-
- thrownException = Assert.Throws(() => new Parser(@")"));
- Assert.Equal("Unexpected token: 'CloseBracket' at position: '0'.", thrownException.Message);
-
- thrownException = Assert.Throws(() => new Parser(@"(StartsWith(""Hello"")"));
- Assert.Equal("Expected token: 'CloseBracket' at position: '19'.", thrownException.Message);
-
- thrownException = Assert.Throws(() => new Parser(@"StartsWith(""Hello""))"));
- Assert.Equal("Unexpected token: 'CloseBracket' at position: '19'.", thrownException.Message);
- }
-
- [Fact]
- public void ParseWithLanguage1()
- {
- var parser = new Parser(@"StartsWith(""Hello"", ""en-US"")");
- var filter = parser.Filter;
- Assert.IsAssignableFrom(filter);
- Assert.Equal("en-US", filter.Language);
-
- parser = new Parser(@"EndsWith(""Hello"", ""en-US"")");
- filter = parser.Filter;
- Assert.IsAssignableFrom(filter);
- Assert.Equal("en-US", filter.Language);
-
- parser = new Parser(@"Contains(""Hello"", ""en-US"")");
- filter = parser.Filter;
- Assert.IsAssignableFrom(filter);
- Assert.Equal("en-US", filter.Language);
-
- parser = new Parser(@"IsEmpty(""en-US"")");
- filter = parser.Filter;
- Assert.IsAssignableFrom(filter);
- Assert.Equal("en-US", filter.Language);
- }
-
- [Fact]
- public void ParseWWithLanguage2()
- {
- Assert.Throws(() => new Parser(@"Id.StartsWith(""Hello"", ""en-US"")"));
- Assert.Throws(() => new Parser(@"Id.EndsWith(""Hello"", ""en-US"")"));
- Assert.Throws(() => new Parser(@"Id.Contains(""Hello"", ""en-US"")"));
- Assert.Throws(() => new Parser(@"IsEmpty(""Hello"", ""en-US"")"));
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Startup.cs b/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Startup.cs
deleted file mode 100644
index fac54aa..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Abstractions/tests/Startup.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Ashampoo.Translation.Systems.Formats.Abstractions.Tests;
-
-public class Startup
-{
- public void ConfigureServices(IServiceCollection services)
- {
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormat.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormat.cs
index 6b9f28e..06e5609 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormat.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormat.cs
@@ -1,14 +1,13 @@
using Ashampoo.Translation.Systems.Formats.Abstractions;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using Ashampoo.Translation.Systems.Formats.AshLang.Chunk;
-using IFormatProvider = Ashampoo.Translation.Systems.Formats.Abstractions.IFormatProvider;
namespace Ashampoo.Translation.Systems.Formats.AshLang;
///
/// Provides a format for the AshLang format.
///
-public class AshLangFormat : AbstractTranslationUnits, IFormat
+public class AshLangFormat : IFormat
{
///
/// The chunks of the ashlang file.
@@ -16,24 +15,21 @@ public class AshLangFormat : AbstractTranslationUnits, IFormat
public IChunk[] Chunks { get; private set; }
///
- public FormatLanguageCount LanguageCount => FormatLanguageCount.SourceAndTarget;
+ public LanguageSupport LanguageSupport => LanguageSupport.SourceAndTarget;
+ ///
+ public ICollection TranslationUnits { get; } = new List();
///
- public Func BuildFormatProvider()
+ public Task WriteAsync(Stream stream)
{
- return builder => builder.SetId("ashlang")
- .SetSupportedFileExtensions(new[] { ".ashlang" })
- .SetFormatType()
- .SetFormatBuilder()
- .Create();
+ throw new NotImplementedException();
}
///
public IFormatHeader Header { get; private set; }
-
///
/// Initializes a new instance of the class.
///
@@ -46,14 +42,14 @@ public AshLangFormat()
var appIdChunk = new AppIdChunk();
var versionChunk = new VersionChunk();
var translationChunk = new TranslationChunk();
- Chunks = new IChunk[]
- {
+ Chunks =
+ [
ashLangFormatHeader.LanguageChunk,
appIdChunk,
ashLangFormatHeader.XDataChunk,
versionChunk,
translationChunk
- };
+ ];
}
///
@@ -83,10 +79,13 @@ public Task ReadAsync(Stream stream, FormatReadOptions? options = null)
{
var translationUnit = new DefaultTranslationUnit(translation.Id)
{
- new SourceTranslationString(sourceLanguage, translation),
- new TargetTranslationString(Header.TargetLanguage, translation)
+ Translations =
+ {
+ new SourceTranslationString(sourceLanguage, translation),
+ new TargetTranslationString(Header.TargetLanguage, translation)
+ }
};
- Add(translationUnit);
+ TranslationUnits.Add(translationUnit);
}
Chunks = reader.Chunks;
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatBuilder.cs
index a31ed72..59add0d 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatBuilder.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatBuilder.cs
@@ -1,4 +1,5 @@
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using Ashampoo.Translation.Systems.Formats.AshLang.Chunk;
using CommunityToolkit.Diagnostics;
@@ -8,46 +9,49 @@ namespace Ashampoo.Translation.Systems.Formats.AshLang;
///
/// Builder for
///
-public class AshLangFormatBuilder : IFormatBuilderWithSourceAndTarget
+public class AshLangFormatBuilder : IFormatBuilderWithSourceAndTarget
{
- private const string SourceLanguage = "en-US"; // AshLang source is always in English
- private string? targetLanguage;
- private readonly Dictionary translations = new();
- private Dictionary information = new();
+ private static readonly Language SourceLanguage = new("en-US"); // AshLang source is always in English
+ private Language _targetLanguage = Language.Empty;
+ private readonly Dictionary _translations = new();
+ private Dictionary _information = new();
///
- public IFormat Build()
+ public AshLangFormat Build()
{
- Guard.IsNotNullOrWhiteSpace(targetLanguage, nameof(targetLanguage));
+ Guard.IsNotNullOrWhiteSpace(_targetLanguage.Value, nameof(_targetLanguage));
var ashLang = new AshLangFormat
{
Header =
{
- TargetLanguage = targetLanguage
+ TargetLanguage = _targetLanguage
}
};
var appIdChunk = ashLang.Chunks.OfType().FirstOrDefault();
- if ( appIdChunk is not null && information.TryGetValue("Name", out var name))
+ if (appIdChunk is not null && _information.TryGetValue("Name", out var name))
{
appIdChunk.Name = name;
- information.Remove("Name");
+ _information.Remove("Name");
}
var translationChunk = (TranslationChunk)ashLang.Chunks.Last();
- foreach (var (id, (source, target)) in translations)
+ foreach (var (id, (source, target)) in _translations)
{
var translation = new TranslationChunk.Translation(0, id, target, source, "");
translationChunk.Translations.Add(translation);
var translationUnit = new DefaultTranslationUnit(id)
{
- new SourceTranslationString(SourceLanguage, translation),
- new TargetTranslationString(targetLanguage, translation)
+ Translations =
+ {
+ new SourceTranslationString(SourceLanguage, translation),
+ new TargetTranslationString(_targetLanguage, translation)
+ }
};
- ashLang.Add(translationUnit);
+ ashLang.TranslationUnits.Add(translationUnit);
}
@@ -57,30 +61,30 @@ public IFormat Build()
///
public void Add(string id, string source, string target)
{
- translations.Add(id, (source, target));
+ _translations.Add(id, (source, target));
}
///
- public void SetSourceLanguage(string language)
+ public void SetSourceLanguage(Language language)
{
// AshLang source is always in English
}
///
- public void SetTargetLanguage(string language)
+ public void SetTargetLanguage(Language language)
{
- targetLanguage = language;
+ _targetLanguage = language;
}
///
public void SetHeaderInformation(IFormatHeader header)
{
- information = new Dictionary(header);
+ _information = new Dictionary(header.AdditionalHeaders);
}
///
public void AddHeaderInformation(string key, string value)
{
- information.Add(key, value);
+ _information.Add(key, value);
}
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatHeader.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatHeader.cs
index f9d96b5..1c20b12 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatHeader.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatHeader.cs
@@ -1,6 +1,6 @@
-using System.Collections;
using System.Diagnostics.CodeAnalysis;
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.AshLang.Chunk;
namespace Ashampoo.Translation.Systems.Formats.AshLang;
@@ -36,35 +36,27 @@ public AshLangFormatHeader(LanguageChunk? languageChunk = null, XDataChunk? xDat
public LanguageChunk LanguageChunk { get; }
///
- public string this[string key]
- {
- get => XDataChunk[key];
- set => XDataChunk[key] = value;
- }
-
- ///
- public string TargetLanguage
+ public Language TargetLanguage
{
get => LanguageChunk.LanguageId;
- set
- {
- if (value is null) throw new ArgumentNullException(nameof(TargetLanguage));
-
+ set =>
// TODO: set Language and Country
LanguageChunk.LanguageId = value;
- }
}
///
- public string? SourceLanguage
+ public Language? SourceLanguage
{
- get => "en-US";
+ get => new("en-US");
set
{
// Do nothing. SourceLanguage is always "en-US"
}
}
+ ///
+ public Dictionary AdditionalHeaders { get; set; } = new();
+
///
public ICollection Keys => XDataChunk.Keys;
@@ -136,9 +128,4 @@ public bool TryGetValue(string key, [MaybeNullWhen(false)] out string value)
{
throw new NotImplementedException();
}
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- return XDataChunk.GetEnumerator();
- }
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatProvider.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatProvider.cs
new file mode 100644
index 0000000..2632ff5
--- /dev/null
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/AshLangFormatProvider.cs
@@ -0,0 +1,27 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+
+namespace Ashampoo.Translation.Systems.Formats.AshLang;
+
+///
+/// for the AshLang format.
+///
+public sealed class AshLangFormatProvider : IFormatProvider
+{
+ ///
+ public string Id { get; } = "ashlang";
+
+ ///
+ public AshLangFormat Create() => new();
+
+ ///
+ public bool SupportsFileName(string fileName)
+ {
+ return SupportedFileExtensions.Any(ext => fileName.EndsWith(ext, StringComparison.OrdinalIgnoreCase));
+ }
+
+ ///
+ public string[] SupportedFileExtensions => [".ashlang"];
+
+ ///
+ public IFormatBuilder GetFormatBuilder() => new AshLangFormatBuilder();
+}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Ashampoo.Translation.Systems.Formats.AshLang.csproj b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Ashampoo.Translation.Systems.Formats.AshLang.csproj
index fd4ca3d..0a26875 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Ashampoo.Translation.Systems.Formats.AshLang.csproj
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Ashampoo.Translation.Systems.Formats.AshLang.csproj
@@ -1,25 +1,10 @@
- net7.0
+ net8.0
enable
enable
- Ashampoo.Translation.Systems.Formats.AshLang
- tjorvenK
- ashampoo
- Package containing the implementation for the AshLang format.
- LICENSE
- localization;translation;ashlang
true
- true
- true
- true
- snupkg
- ash-logo-icon-big-128x.png
- v
- true
- prerelease.0
- README.md
@@ -33,7 +18,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Chunk/ChunkWriter.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Chunk/ChunkWriter.cs
index 8d58002..ef93bc7 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Chunk/ChunkWriter.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Chunk/ChunkWriter.cs
@@ -7,8 +7,8 @@ namespace Ashampoo.Translation.Systems.Formats.AshLang.Chunk;
///
public class ChunkWriter
{
- private readonly BinaryWriter writer;
- private readonly IChunk[] chunks;
+ private readonly BinaryWriter _writer;
+ private readonly IChunk[] _chunks;
///
/// Initializes a new instance of the ChunkWriter class.
@@ -17,8 +17,8 @@ public class ChunkWriter
///
public ChunkWriter(Stream stream, IChunk[] chunks)
{
- this.chunks = chunks;
- writer = new BinaryWriter(stream);
+ _chunks = chunks;
+ _writer = new BinaryWriter(stream);
}
///
@@ -27,12 +27,12 @@ public ChunkWriter(Stream stream, IChunk[] chunks)
public void Write()
{
// Header
- writer.WriteUTF8String("URESFILE");
+ _writer.WriteUTF8String("URESFILE");
// Write all chunks.
- WriteDataAndUpdateSize(writer, () =>
+ WriteDataAndUpdateSize(_writer, () =>
{
- foreach (var chunk in chunks)
+ foreach (var chunk in _chunks)
{
// TODO: Should this be optional?
if (chunk.IsEmpty) continue;
@@ -40,7 +40,7 @@ public void Write()
}
});
- writer.Flush();
+ _writer.Flush();
}
///
@@ -49,9 +49,9 @@ public void Write()
///
private void WriteChunk(IChunk chunk)
{
- writer.WriteUTF8String(chunk.Id);
+ _writer.WriteUTF8String(chunk.Id);
- WriteDataAndUpdateSize(writer, () => { chunk.Write(writer); });
+ WriteDataAndUpdateSize(_writer, () => { chunk.Write(_writer); });
}
///
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Chunk/LanguageChunk.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Chunk/LanguageChunk.cs
index 6a6b160..8ce8017 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Chunk/LanguageChunk.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/Chunk/LanguageChunk.cs
@@ -5,6 +5,8 @@ [ChunkString][ChunkString ][ChunkString ]
[LanguageId ][Language Name][Country Name]
*/
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
+
namespace Ashampoo.Translation.Systems.Formats.AshLang.Chunk;
///
@@ -17,10 +19,11 @@ public class LanguageChunk : IChunk
///
public const string Id = "lang";
string IChunk.Id => Id;
+
///
/// The language id.
///
- public string LanguageId { get; set; } = "";
+ public Language LanguageId { get; set; } = Abstractions.Models.Language.Empty;
///
/// The language name.
///
@@ -31,13 +34,13 @@ public class LanguageChunk : IChunk
public string Country { get; set; } = "";
///
- public bool IsEmpty => string.IsNullOrEmpty(LanguageId) && string.IsNullOrEmpty(Language) &&
+ public bool IsEmpty => string.IsNullOrEmpty(LanguageId.ToString()) && string.IsNullOrEmpty(Language) &&
string.IsNullOrEmpty(Country);
///
public void Read(BinaryReader reader)
{
- LanguageId = ChunkString.Read(reader);
+ LanguageId = new Language(ChunkString.Read(reader));
Language = ChunkString.Read(reader);
Country = ChunkString.Read(reader);
}
@@ -45,7 +48,7 @@ public void Read(BinaryReader reader)
///
public void Write(BinaryWriter writer)
{
- ChunkString.Write(writer, LanguageId);
+ ChunkString.Write(writer, LanguageId.ToString());
ChunkString.Write(writer, Language);
ChunkString.Write(writer, Country);
}
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/DependencyInjectionExtension.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/DependencyInjectionExtension.cs
new file mode 100644
index 0000000..4623a5b
--- /dev/null
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/DependencyInjectionExtension.cs
@@ -0,0 +1,31 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Ashampoo.Translation.Systems.Formats.AshLang;
+
+///
+/// Static class that contains extension methods for .
+///
+public static class DependencyInjection
+{
+ ///
+ /// Registers all necessary services for the AshLang format.
+ ///
+ ///
+ /// The to register the services with.
+ ///
+ ///
+ /// The for chaining.
+ ///
+ ///
+ /// Thrown if something went wrong during the registration.
+ ///
+ public static IServiceCollection AddAshLangFormatFeatures(this IServiceCollection services)
+ {
+ services.AddSingleton()
+ .AddSingleton>(sp => sp.GetRequiredService())
+ .AddSingleton>(sp => sp.GetRequiredService());
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/SourceTranslationString.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/SourceTranslationString.cs
index e50269a..822f9bd 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/SourceTranslationString.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/SourceTranslationString.cs
@@ -1,16 +1,17 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using Ashampoo.Translation.Systems.Formats.AshLang.Chunk;
namespace Ashampoo.Translation.Systems.Formats.AshLang;
///
-/// Implementation of the interface, representing a source translation string for the AshLang format.
+/// Implementation of the interface, representing a source translation string for the AshLang format.
///
-public class SourceTranslationString : ITranslationString
+public class SourceTranslationString : ITranslation
{
- private readonly TranslationChunk.Translation translation;
+ private readonly TranslationChunk.Translation _translation;
- private readonly string language;
+ private readonly Language _language;
///
/// Initializes a new instance of the class.
@@ -21,39 +22,36 @@ public class SourceTranslationString : ITranslationString
///
/// The
///
- public SourceTranslationString(string language, TranslationChunk.Translation translation)
+ public SourceTranslationString(Language language, TranslationChunk.Translation translation)
{
- this.language = language;
- this.translation = translation;
+ _language = language;
+ _translation = translation;
}
///
public string Value
{
- get => translation.Fallback;
+ get => _translation.Fallback;
set
{
// Do nothing - Sources in AshLang are readonly.
}
}
- ///
- public string Id => translation.Id;
-
///
public string? Comment
{
- get => translation.Comment;
- set => translation.Comment = value ?? "";
+ get => _translation.Comment;
+ set => _translation.Comment = value ?? "";
}
///
public bool IsEmpty => string.IsNullOrWhiteSpace(Value);
///
- public string Language
+ public Language Language
{
- get => language;
+ get => _language;
set
{
//Do nothing, source language is always en-us
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/TargetTranslationString.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/TargetTranslationString.cs
index e348f87..ad625f5 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/src/TargetTranslationString.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/src/TargetTranslationString.cs
@@ -1,14 +1,15 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using Ashampoo.Translation.Systems.Formats.AshLang.Chunk;
namespace Ashampoo.Translation.Systems.Formats.AshLang;
///
-/// Implementation of the interface, representing a target translation string for the AshLang format.
+/// Implementation of the interface, representing a target translation string for the AshLang format.
///
-public class TargetTranslationString : ITranslationString
+public class TargetTranslationString : ITranslation
{
- private readonly TranslationChunk.Translation translation;
+ private readonly TranslationChunk.Translation _translation;
///
/// Initializes a new instance of the class.
@@ -19,32 +20,26 @@ public class TargetTranslationString : ITranslationString
///
/// The of the translation string.
///
- public TargetTranslationString(string language, TranslationChunk.Translation translation)
+ public TargetTranslationString(Language language, TranslationChunk.Translation translation)
{
Language = language;
- this.translation = translation;
+ _translation = translation;
}
///
public string Value
{
- get => translation.Value;
- set => translation.Value = value;
+ get => _translation.Value;
+ set => _translation.Value = value;
}
- ///
- public string Id => translation.Id;
-
///
public string? Comment
{
- get => translation.Comment;
- set => translation.Comment = value ?? "";
+ get => _translation.Comment;
+ set => _translation.Comment = value ?? "";
}
///
- public bool IsEmpty => string.IsNullOrWhiteSpace(Value);
-
- ///
- public string Language { get; set; }
+ public Language Language { get; set; }
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/AshLangFormatTest.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/AshLangFormatTest.cs
index 997c461..d0305fc 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/AshLangFormatTest.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/AshLangFormatTest.cs
@@ -1,40 +1,30 @@
using System;
using System.IO;
-using System.Threading.Tasks;
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
using Ashampoo.Translation.Systems.TestBase;
+using FluentAssertions;
using Xunit;
namespace Ashampoo.Translation.Systems.Formats.AshLang.Tests;
public class AshLangFormatTest : FormatTestBase
{
- private readonly IFormatFactory formatFactory;
-
- public AshLangFormatTest(IFormatFactory formatFactory)
- {
- this.formatFactory = formatFactory;
- }
[Fact]
public void NewFormat()
{
var format = CreateFormat();
- Assert.Empty(format);
+ format.TranslationUnits.Should().BeEmpty();
// source sets are 'en-US' per default.
- Assert.Equal("en-US", format.Header.SourceLanguage);
-
- format.Header.TargetLanguage = "es-ES";
- Assert.Equal("es-ES", format.Header.TargetLanguage);
-
- // set Language null must throw.
- var thrownException = Assert.Throws(() => format.Header.TargetLanguage = null!);
- Assert.Equal("Value cannot be null. (Parameter 'TargetLanguage')", thrownException.Message);
-
+ format.Header.SourceLanguage.Should().Be(new Language("en-US"));
+
+ format.Header.TargetLanguage = new Language("es-ES");
+ format.Header.TargetLanguage.Should().Be(new Language("es-ES"));
+
// author is nullable and can be changed.
//Assert.Null(format.Header.Author);
//format.Header.Author = "Ashampoo";
@@ -43,64 +33,24 @@ public void NewFormat()
//Assert.Null(format.Header.Author);
}
- [Fact]
- public void ImportSuccessTest()
- {
- var format = CreateAndReadFromFile("normalized_peru-de-DE.ashLang");
-
- const string id = "peru.CFileSystemManager.MoveFolderFailed.CreateFolder";
- const string value = "Import Test";
- var imported = format.ImportMockTranslationWithUnits("de-DE", id);
-
- var translationUnit = format[id];
- Assert.NotNull(translationUnit);
- Assert.Equal(value, (translationUnit["de-DE"] as ITranslationString)?.Value);
- Assert.Equal(1, imported.Count);
- }
-
- [Fact]
- public void NoMatchImportTest()
- {
- var format = CreateAndReadFromFile("normalized_peru-de-DE.ashLang");
-
- const string id = "Not matching Import-Id";
- var imported = format.ImportMockTranslationWithUnits("de-DE", id);
-
- Assert.Equal(0, imported.Count);
- }
-
- [Fact]
- public void ImportEqualTranslationTest()
- {
- var format = CreateAndReadFromFile("normalized_peru-de-DE.ashLang");
-
- const string id = "peru.CFileSystemManager.MoveFolderFailed.CreateFolder";
- const string value =
- "Fehler beim Verschieben von Ordner '%SRC%' nach '%DEST%''. Konnte Ordner nicht erstellen.";
- var imported = format.ImportMockTranslationWithUnits("de-DE", id, value);
-
- Assert.Equal(0, imported.Count);
- }
-
[Fact]
public void ReadFromFile()
{
var format = CreateAndReadFromFile("normalized_peru-de-DE.ashLang");
- Assert.Equal(67, format.Count);
- Assert.Equal("en-US", format.Header.SourceLanguage);
- //Assert.Null(format.Header.Author);
- Assert.Equal("de-DE", format.Header.TargetLanguage);
- //Assert.Equal("Ashampoo", format.Header.Author);
+ format.TranslationUnits.Count.Should().Be(67);
+ format.Header.SourceLanguage.Should().Be(new Language("en-US"));
+ format.Header.TargetLanguage.Should().Be(new Language("de-DE"));
const string id = "peru.CSystem.MoveFileFailed";
- var translationUnit = format[id];
- Assert.NotNull(translationUnit);
- Assert.Equal(id, translationUnit.Id);
- Assert.Equal("Error moving file '%SRC%' to '%DEST%'.", (translationUnit["en-US"] as ITranslationString)?.Value);
- Assert.Equal("Fehler beim Verschieben der Datei '%SRC%' nach '%DEST%'.",
- (translationUnit["de-DE"] as ITranslationString)?.Value);
+ var translationUnit = format.TranslationUnits.GetTranslationUnit(id);
+ translationUnit.Should().NotBeNull();
+ translationUnit.Id.Should().Be(id);
+ translationUnit.Translations.GetTranslation(new Language("en-US")).Value.Should()
+ .Be("Error moving file '%SRC%' to '%DEST%'.");
+ translationUnit.Translations.GetTranslation(new Language("de-DE")).Value.Should()
+ .Be("Fehler beim Verschieben der Datei '%SRC%' nach '%DEST%'.");
}
[Fact]
@@ -118,69 +68,43 @@ public void ReadAndWrite()
//ms.MustBeEqualTo(fs);
}
- [Fact]
- public async Task ConvertTest()
- {
- var mockFormat =
- MockFormatWithTranslationUnits.CreateMockFormatWithTranslationUnits("en-US", "Convert ID", "Convert Test");
-
- var assignOptions = new AssignOptions
- {
- SourceLanguage = "en-US",
- TargetLanguage = "de-DE",
- Filter = new DefaultTranslationFilter()
- };
-
- var ashLang = await mockFormat.ConvertToAsync(formatFactory, assignOptions);
-
- Assert.Equal("en-US", ashLang.Header.SourceLanguage);
- Assert.Equal("de-DE", ashLang.Header.TargetLanguage);
-
- Assert.Single(ashLang);
- Assert.Equal(2, ashLang["Convert ID"]?.Count);
- Assert.Equal("Convert Test", (ashLang["Convert ID"]?["en-US"] as ITranslationString)?.Value);
- Assert.Null(ashLang["Convert Test"]?.TryGet("de-DE"));
- }
-
[Fact]
public void FormatBuilderTest()
{
- var builder =
- (IFormatBuilderWithSourceAndTarget)formatFactory.GetFormatProvider(typeof(AshLangFormat))
- .GetFormatBuilder();
+ var builder = new AshLangFormatBuilder();
- builder.SetTargetLanguage("de-DE");
+ builder.SetTargetLanguage(new Language("de-DE"));
builder.Add("Test ID 1", "Test Source 1", "Test Ziel 1");
builder.Add("Test ID 2", "Test Source 2", string.Empty);
builder.Add("Test ID 3", string.Empty, "Test Ziel 2");
var ashLang = builder.Build();
- Assert.Equal("en-US", ashLang.Header.SourceLanguage);
- Assert.Equal("de-DE", ashLang.Header.TargetLanguage);
-
- Assert.Equal(3, ashLang.Count);
- Assert.Equal("Test Source 1", (ashLang["Test ID 1"]?["en-US"] as ITranslationString)?.Value);
- Assert.Equal("Test Ziel 1", (ashLang["Test ID 1"]?["de-DE"] as ITranslationString)?.Value);
-
- Assert.Equal("Test Source 2", (ashLang["Test ID 2"]?["en-US"] as ITranslationString)?.Value);
- Assert.Equal(string.Empty, (ashLang["Test ID 2"]?.TryGet("de-DE") as ITranslationString)?.Value);
-
- Assert.Equal(string.Empty, (ashLang["Test ID 3"]?.TryGet("en-US") as ITranslationString)?.Value);
- Assert.Equal("Test Ziel 2", (ashLang["Test ID 3"]?["de-DE"] as ITranslationString)?.Value);
-
-
- builder = (IFormatBuilderWithSourceAndTarget)formatFactory.GetFormatProvider(typeof(AshLangFormat))
- .GetFormatBuilder();
- builder.SetSourceLanguage("de-DE");
-
- Assert.Throws(() => builder.Build());
-
- builder.SetTargetLanguage("de-DE");
-
+ ashLang.Header.SourceLanguage.Should().Be(new Language("en-US"));
+ ashLang.Header.TargetLanguage.Should().Be(new Language("de-DE"));
+ ashLang.TranslationUnits.Count.Should().Be(3);
+ ashLang.TranslationUnits.GetTranslationUnit("Test ID 1").Translations.GetTranslation(new Language("en-US")).Value.Should()
+ .Be("Test Source 1");
+ ashLang.TranslationUnits.GetTranslationUnit("Test ID 1").Translations.GetTranslation(new Language("de-DE")).Value.Should()
+ .Be("Test Ziel 1");
+ ashLang.TranslationUnits.GetTranslationUnit("Test ID 2").Translations.GetTranslation(new Language("en-US")).Value.Should()
+ .Be("Test Source 2");
+ ashLang.TranslationUnits.GetTranslationUnit("Test ID 2").Translations.GetTranslation(new Language("de-DE")).Value.Should()
+ .BeEmpty();
+ ashLang.TranslationUnits.GetTranslationUnit("Test ID 3").Translations.GetTranslation(new Language("en-US")).Value.Should()
+ .BeEmpty();
+ ashLang.TranslationUnits.GetTranslationUnit("Test ID 3").Translations.GetTranslation(new Language("de-DE")).Value.Should()
+ .Be("Test Ziel 2");
+
+ builder = new AshLangFormatBuilder();
+ builder.SetSourceLanguage(new Language("de-DE"));
+
+ builder.Invoking(x => x.Build()).Should().Throw();
+ builder.SetTargetLanguage(new Language("de-DE"));
+
var format = builder.Build();
- Assert.Equal("en-US", format.Header.SourceLanguage);
- Assert.Equal("de-DE", format.Header.TargetLanguage);
+ format.Header.SourceLanguage.Should().Be(new Language("en-US"));
+ format.Header.TargetLanguage.Should().Be(new Language("de-DE"));
}
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/Ashampoo.Translation.Systems.Formats.AshLang.Tests.csproj b/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/Ashampoo.Translation.Systems.Formats.AshLang.Tests.csproj
index c4d2430..2cc726d 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/Ashampoo.Translation.Systems.Formats.AshLang.Tests.csproj
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/Ashampoo.Translation.Systems.Formats.AshLang.Tests.csproj
@@ -1,19 +1,19 @@
- net7.0
+ net8.0
enable
false
-
-
-
-
-
+
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/ChunkReaderTest.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/ChunkReaderTest.cs
index 7fa8cdf..8a056f6 100644
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/ChunkReaderTest.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/ChunkReaderTest.cs
@@ -1,6 +1,9 @@
using System.IO;
+using System.Linq;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.AshLang.Chunk;
using Ashampoo.Translation.Systems.TestBase;
+using FluentAssertions;
using Xunit;
namespace Ashampoo.Translation.Systems.Formats.AshLang.Tests;
@@ -18,9 +21,11 @@ public void LanguageChunk()
{
var chunkReader = new ChunkReader(GetNewStream());
var chunk = chunkReader.TryGetOrNull(Chunk.LanguageChunk.Id);
- Assert.Equal("de-DE", chunk?.LanguageId);
- Assert.Equal("Deutsch", chunk?.Language);
- Assert.Equal("Deutschland", chunk?.Country);
+
+ chunk.Should().NotBeNull();
+ chunk!.LanguageId.Should().Be(new Language("de-DE"));
+ chunk.Language.Should().Be("Deutsch");
+ chunk.Country.Should().Be("Deutschland");
}
[Fact]
@@ -29,7 +34,7 @@ public void CommentChunk()
var chunkReader = new ChunkReader(GetNewStream());
var chunk = chunkReader.TryGetOrNull(Chunk.CommentChunk.Id);
- Assert.Null(chunk);
+ chunk.Should().BeNull();
}
[Fact]
@@ -38,8 +43,9 @@ public void AppIdChunk()
var chunkReader = new ChunkReader(GetNewStream());
var chunk = chunkReader.TryGetOrNull(Chunk.AppIdChunk.Id);
- Assert.Equal("peru", chunk?.Name);
- Assert.Equal("1.0.0.204", chunk?.Version);
+ chunk.Should().NotBeNull();
+ chunk!.Name.Should().Be("peru");
+ chunk.Version.Should().Be("1.0.0.204");
}
[Fact]
@@ -48,7 +54,8 @@ public void VersionChunk()
var chunkReader = new ChunkReader(GetNewStream());
var chunk = chunkReader.TryGetOrNull(Chunk.VersionChunk.Id);
- Assert.Equal(AshLangVersion.AshLangFormatV2, chunk?.Version);
+ chunk.Should().NotBeNull();
+ chunk!.Version.Should().Be(AshLangVersion.AshLangFormatV2);
}
[Fact]
@@ -57,15 +64,12 @@ public void XDataChunk()
var chunkReader = new ChunkReader(GetNewStream());
var chunk = chunkReader.TryGetOrNull(Chunk.XDataChunk.Id);
- Assert.Equal(4, chunk?.Count);
- if (chunk is not null && chunk.ContainsKey("ASH_LANG_AUTHOR"))
- Assert.Equal("Ashampoo", chunk["ASH_LANG_AUTHOR"]);
- if (chunk is not null && chunk.ContainsKey("ASH_LANG_CREATION_TOOL"))
- Assert.Equal("Ashampoo Translation Studio Advanced", chunk["ASH_LANG_CREATION_TOOL"]);
- if (chunk is not null && chunk.ContainsKey("ASH_LANG_CREATION_TOOL_VERSION"))
- Assert.Equal("1.8.20.1", chunk["ASH_LANG_CREATION_TOOL_VERSION"]);
- if (chunk is not null && chunk.ContainsKey("ASH_LANG_MAIL"))
- Assert.Equal("translations@ashampoo.com", chunk["ASH_LANG_MAIL"]);
+ chunk.Should().NotBeNull();
+ chunk!.Count.Should().Be(4);
+ chunk["ASH_LANG_AUTHOR"].Should().Be("Ashampoo");
+ chunk["ASH_LANG_CREATION_TOOL"].Should().Be("Ashampoo Translation Studio Advanced");
+ chunk["ASH_LANG_CREATION_TOOL_VERSION"].Should().Be("1.8.20.1");
+ chunk["ASH_LANG_MAIL"].Should().Be("translations@ashampoo.com");
}
[Fact]
@@ -74,13 +78,16 @@ public void TranslationChunk()
var chunkReader = new ChunkReader(GetNewStream());
var chunk = chunkReader.TryGetOrNull(Chunk.TranslationChunk.Id);
- Assert.Equal(67, chunk?.Translations.Count);
- var translation = chunk?.Translations[3];
- Assert.Equal("", translation?.Comment);
- Assert.Equal("Music files", translation?.Fallback);
- Assert.Equal((uint)0, translation?.Flags);
- Assert.Equal("peru.filesystem.smart.Music", translation?.Id);
- Assert.Equal("peru.filesystem.smart.Music", translation?.Key);
- Assert.Equal("Musikdateien", translation?.Value);
+ chunk.Should().NotBeNull();
+ chunk!.Translations.Count.Should().Be(67);
+
+ var translation = chunk.Translations[3];
+ translation.Should().NotBeNull();
+ translation.Comment.Should().BeEmpty();
+ translation.Fallback.Should().Be("Music files");
+ translation.Flags.Should().Be(0);
+ translation.Id.Should().Be("peru.filesystem.smart.Music");
+ translation.Key.Should().Be("peru.filesystem.smart.Music");
+ translation.Value.Should().Be("Musikdateien");
}
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/PluginLoaderTest.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/PluginLoaderTest.cs
deleted file mode 100644
index 5684817..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/PluginLoaderTest.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using Xunit;
-
-namespace Ashampoo.Translation.Systems.Formats.AshLang.Tests;
-
-public class PluginLoaderTest
-{
- private readonly IFormatFactory formatFactory;
-
- public PluginLoaderTest(IFormatFactory formatFactory)
- {
- this.formatFactory = formatFactory;
- }
-
- [Fact]
- public void LoadTest()
- {
- Assert.NotEmpty(formatFactory.GetFormatProviders());
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/Startup.cs b/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/Startup.cs
deleted file mode 100644
index 7c24f3c..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.AshLang/tests/Startup.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System;
-using System.IO;
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-
-namespace Ashampoo.Translation.Systems.Formats.AshLang.Tests;
-
-public class Startup
-{
- public void ConfigureServices(IServiceCollection services)
- {
- // services.AddFormatFactory().AddFormatProvider(builder =>
- // {
- // return builder.SetId("ashlang")
- // .SetSupportedFileExtensions(new[] { ".ashlang" })
- // .SetFormatType()
- // .SetFormatBuilder()
- // .Create();
- // });
-
- services.AddLogging(b => b.AddConsole());
-
- services.AddFormatFactory().RegisterFormat();;
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Gengo/src/Ashampoo.Translation.Systems.Formats.Gengo.csproj b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/Ashampoo.Translation.Systems.Formats.Gengo.csproj
index f377f5b..3bddf2c 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Gengo/src/Ashampoo.Translation.Systems.Formats.Gengo.csproj
+++ b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/Ashampoo.Translation.Systems.Formats.Gengo.csproj
@@ -1,25 +1,10 @@
- net7.0
+ net8.0
enable
enable
- Ashampoo.Translation.Systems.Formats.Gengo
- tjorvenK
- ashampoo
- Package containing the implementation for the Gengo format.
- LICENSE
- localization;translation;gengo
true
- true
- true
- true
- snupkg
- ash-logo-icon-big-128x.png
- v
- true
- prerelease.0
- README.md
@@ -28,7 +13,7 @@
-
+
@@ -38,7 +23,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Ashampoo.Translation.Systems.Formats.Gengo/src/DependencyInjectionExtension.cs b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/DependencyInjectionExtension.cs
new file mode 100644
index 0000000..1f2c1af
--- /dev/null
+++ b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/DependencyInjectionExtension.cs
@@ -0,0 +1,31 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Ashampoo.Translation.Systems.Formats.Gengo;
+
+///
+/// Static class that contains extension methods for .
+///
+public static class DependencyInjection
+{
+ ///
+ /// Registers all necessary services for the Gengo format.
+ ///
+ ///
+ /// The to register the services with.
+ ///
+ ///
+ /// The for chaining.
+ ///
+ ///
+ /// Thrown if something went wrong during the registration.
+ ///
+ public static IServiceCollection AddGengoFormatFeatures(this IServiceCollection services)
+ {
+ services.AddSingleton()
+ .AddSingleton>(sp => sp.GetRequiredService())
+ .AddSingleton>(sp => sp.GetRequiredService());
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormat.cs b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormat.cs
index 32e52fb..502b886 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormat.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormat.cs
@@ -1,10 +1,10 @@
using System.Text.RegularExpressions;
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using Microsoft.Toolkit.Diagnostics;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
-using IFormatProvider = Ashampoo.Translation.Systems.Formats.Abstractions.IFormatProvider;
namespace Ashampoo.Translation.Systems.Formats.Gengo;
@@ -12,21 +12,23 @@ namespace Ashampoo.Translation.Systems.Formats.Gengo;
///
/// Implementation of the interface for the Gengo translation format.
///
-public class GengoFormat : AbstractTranslationUnits, IFormat
+public partial class GengoFormat : IFormat
{
///
public IFormatHeader Header { get; init; } = new DefaultFormatHeader();
///
- public FormatLanguageCount LanguageCount => FormatLanguageCount.SourceAndTarget;
+ public LanguageSupport LanguageSupport => LanguageSupport.SourceAndTarget;
+
+ ///
+ public ICollection TranslationUnits { get; } = new List();
private static readonly Regex
RegexMarker =
- new(@"^\[{3}(?.*)\]{3}$",
- RegexOptions.Singleline); // Regex to get the id with square brackets around it.
+ RegexMarkerGenerator(); // Regex to get the id with square brackets around it.
private static readonly Regex
- RegexWithoutMarker = new(@"^(?.*)$"); // Regex to get the id without square brackets around it.
+ RegexWithoutMarker = RegexWithoutMarkerGenerator(); // Regex to get the id without square brackets around it.
///
public async Task ReadAsync(Stream stream, FormatReadOptions? options = null)
@@ -44,17 +46,18 @@ public async Task ReadAsync(Stream stream, FormatReadOptions? options = null)
var workbook = WorkbookFactory.Create(stream); // Create the workbook from the stream
var sheet = workbook.GetSheetAt(0); // Get the first sheet
- Guard.IsNotNullOrWhiteSpace(Header.TargetLanguage,
+ Guard.IsNotNullOrWhiteSpace(Header.TargetLanguage.Value,
nameof(Header.TargetLanguage)); // The target language has to be set
ReadTranslations(sheet); // Read the translations from the sheet
}
private async Task ConfigureOptionsAsync(FormatReadOptions options)
{
+
var setTargetLanguage =
- string.IsNullOrWhiteSpace(options.TargetLanguage); // Check if the target language needs to be set
+ options.TargetLanguage.IsNullOrWhitespace(); // Check if the target language needs to be set
var setSourceLanguage =
- string.IsNullOrWhiteSpace(options.SourceLanguage); // Check if the source language needs to be set
+ options.SourceLanguage.IsNullOrWhitespace(); // Check if the source language needs to be set
if (setTargetLanguage || setSourceLanguage)
{
if (options.FormatOptionsCallback is null)
@@ -63,7 +66,7 @@ private async Task ConfigureOptionsAsync(FormatReadOptions options)
FormatStringOption sourceLanguageOption = new("Source language");
FormatStringOption targetLanguageOption = new("Target language", true);
- List optionList = new();
+ List optionList = [];
if (setSourceLanguage) optionList.Add(sourceLanguageOption);
if (setTargetLanguage) optionList.Add(targetLanguageOption);
@@ -79,12 +82,12 @@ private async Task ConfigureOptionsAsync(FormatReadOptions options)
Header.SourceLanguage =
setSourceLanguage
- ? sourceLanguageOption.Value
+ ? Language.Parse(sourceLanguageOption.Value)
: options.SourceLanguage; // Set the source language if it was not set
Header.TargetLanguage =
- setTargetLanguage
- ? targetLanguageOption.Value
- : options.TargetLanguage!; // Set the target language if it was not set
+ (setTargetLanguage
+ ? Language.Parse(targetLanguageOption.Value)
+ : options.TargetLanguage)!; // Set the target language if it was not set
}
else
{
@@ -105,21 +108,23 @@ private void ReadTranslations(ISheet sheet)
var translations = CreateTranslations(row); // Create the translations from the row
if (translations is null) continue;
- var translationUnit = new DefaultTranslationUnit(translations.Item1.Id) // Create the translation unit
+ var translationUnit = new DefaultTranslationUnit(translations.Item3) // Create the translation unit
{
- [translations.Item1.Language] = translations.Item1,
- [translations.Item2.Language] = translations.Item2
+ Translations =
+ {
+ translations.Item1,
+ translations.Item2
+ }
};
-
- Add(translationUnit); // Add the translation unit to the hash set of translation units
+ TranslationUnits.Add(translationUnit); // Add the translation unit to the hash set of translation units
}
}
- private Tuple? CreateTranslations(IRow row)
+ private Tuple? CreateTranslations(IRow row)
{
var idCell = row.TryGetCell(0); // Get the first cell
if (idCell is null) return null; // If the cell is null, return null
- if(idCell.Address.Column != 0) return null; // If the first cell is not in the first column, skip the row
+ if (idCell.Address.Column != 0) return null; // If the first cell is not in the first column, skip the row
var id = idCell.StringCellValue ?? string.Empty; // Get the id from the cell
if (string.IsNullOrWhiteSpace(id)) return null; // If the id is empty, skip the row
@@ -128,7 +133,7 @@ private void ReadTranslations(ISheet sheet)
var sourceCell = row.TryGetCell(1); // Get the second cell
if (sourceCell is null) return null; // If the cell is null, return null
- if(sourceCell.Address.Column != 1) return null; // If the second cell is not in the second column, skip the row
+ if (sourceCell.Address.Column != 1) return null; // If the second cell is not in the second column, skip the row
var source = sourceCell.StringCellValue ?? string.Empty; // Get the source from the cell
if (string.IsNullOrWhiteSpace(source)) return null; // If the source is empty, skip the row
@@ -147,10 +152,10 @@ private void ReadTranslations(ISheet sheet)
(
id,
target,
- Header.TargetLanguage ?? throw new ArgumentNullException(nameof(Header.TargetLanguage))
+ Header.TargetLanguage
);
- return new Tuple(sourceTranslation, targetTranslation);
+ return new Tuple(sourceTranslation, targetTranslation, id);
}
private string RemoveMarker(string str)
@@ -171,7 +176,7 @@ public void Write(Stream stream)
CreateHeaderRow(sheet); // Create the header row
var rowCount = 1; // The row count
- foreach (var translationUnit in this) // Loop through all translation units
+ foreach (var translationUnit in TranslationUnits) // Loop through all translation units
{
var row = sheet.CreateRow(rowCount); // Create a new row
@@ -181,8 +186,8 @@ public void Write(Stream stream)
}
row.Cells[0].SetCellValue($"[[[{translationUnit.Id}]]]"); // Set the id with square brackets around it
- row.Cells[1].SetCellValue((translationUnit.TryGet(Header.SourceLanguage!) as ITranslationString)?.Value);
- row.Cells[2].SetCellValue((translationUnit.TryGet(Header.TargetLanguage) as ITranslationString)?.Value);
+ row.Cells[1].SetCellValue(translationUnit.Translations.GetTranslation((Language)Header.SourceLanguage!).Value);
+ row.Cells[2].SetCellValue(translationUnit.Translations.GetTranslation(Header.TargetLanguage).Value);
rowCount++;
}
@@ -190,10 +195,17 @@ public void Write(Stream stream)
AutosizeColumns(sheet, 0, 3); // Autosize the columns
- workbook.Write(stream, true); // Write the workbook to the stream, and leave the stream open TODO: Is this correct?
+ workbook.Write(stream,
+ true); // Write the workbook to the stream, and leave the stream open TODO: Is this correct?
+ }
+
+ ///
+ public Task WriteAsync(Stream stream)
+ {
+ throw new NotImplementedException();
}
- private void CreateHeaderRow(ISheet sheet)
+ private void CreateHeaderRow(ISheet sheet)
{
var row = sheet.CreateRow(0); // Create a new row
@@ -215,13 +227,8 @@ private void AutosizeColumns(ISheet sheet, int start, int count)
}
}
- ///
- public Func BuildFormatProvider()
- {
- return builder => builder.SetId("gengo")
- .SetSupportedFileExtensions(new[] { ".xlsx", ".xls" })
- .SetFormatType()
- .SetFormatBuilder()
- .Create();
- }
+ [GeneratedRegex(@"^\[{3}(?.*)\]{3}$", RegexOptions.Singleline)]
+ private static partial Regex RegexMarkerGenerator();
+ [GeneratedRegex(@"^(?.*)$")]
+ private static partial Regex RegexWithoutMarkerGenerator();
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormatBuilder.cs
index ef539dc..c18f437 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormatBuilder.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormatBuilder.cs
@@ -1,4 +1,5 @@
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using Microsoft.Toolkit.Diagnostics;
@@ -7,23 +8,23 @@ namespace Ashampoo.Translation.Systems.Formats.Gengo;
///
/// Builder for the .
///
-public class GengoFormatBuilder : IFormatBuilderWithSourceAndTarget
+public class GengoFormatBuilder : IFormatBuilderWithSourceAndTarget
{
- private string? sourceLanguage;
- private string? targetLanguage;
- private readonly Dictionary translations = new();
+ private Language? _sourceLanguage;
+ private Language? _targetLanguage;
+ private readonly Dictionary _translations = new();
///
public void Add(string id, string source, string target)
{
- translations.Add(id, (source, target));
+ _translations.Add(id, (source, target));
}
///
- public IFormat Build()
+ public GengoFormat Build()
{
- Guard.IsNotNullOrWhiteSpace(sourceLanguage, nameof(sourceLanguage));
- Guard.IsNotNullOrWhiteSpace(targetLanguage, nameof(targetLanguage));
+ Guard.IsNotNullOrWhiteSpace(_sourceLanguage?.Value, nameof(_sourceLanguage));
+ Guard.IsNotNullOrWhiteSpace(_targetLanguage?.Value, nameof(_targetLanguage));
//Create new Gengo format and add translations
@@ -31,45 +32,48 @@ public IFormat Build()
{
Header =
{
- SourceLanguage = sourceLanguage,
- TargetLanguage = targetLanguage
+ SourceLanguage = _sourceLanguage,
+ TargetLanguage = _targetLanguage.Value
}
};
// ReSharper disable once ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
- foreach (var keyValuePair in translations)
+ foreach (var keyValuePair in _translations)
{
var sourceTranslationString =
new DefaultTranslationString(keyValuePair.Key, keyValuePair.Value.Item1,
- sourceLanguage); //Create new translation string
+ _sourceLanguage.Value); //Create new translation string
var targetTranslationString =
new DefaultTranslationString(keyValuePair.Key, keyValuePair.Value.Item2,
- targetLanguage); //Create new translation string
+ _targetLanguage.Value); //Create new translation string
var translationUnit = new DefaultTranslationUnit(keyValuePair.Key) //Create new translation unit
{
- sourceTranslationString,
- targetTranslationString
+ Translations =
+ {
+ sourceTranslationString,
+ targetTranslationString
+ }
};
- gengoFormat.Add(translationUnit); //Add translation unit to format
+ gengoFormat.TranslationUnits.Add(translationUnit); //Add translation unit to format
}
return gengoFormat;
}
///
- public void SetSourceLanguage(string language)
+ public void SetSourceLanguage(Language language)
{
- sourceLanguage = language;
+ _sourceLanguage = language;
}
///
- public void SetTargetLanguage(string language)
+ public void SetTargetLanguage(Language language)
{
- targetLanguage = language;
+ _targetLanguage = language;
}
-
+
///
/// This method is not supported because does not support header information,
/// it will do nothing.
diff --git a/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormatProvider.cs b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormatProvider.cs
new file mode 100644
index 0000000..fafa2d9
--- /dev/null
+++ b/src/Ashampoo.Translation.Systems.Formats.Gengo/src/GengoFormatProvider.cs
@@ -0,0 +1,27 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+
+namespace Ashampoo.Translation.Systems.Formats.Gengo;
+
+///
+/// for the Gengo format.
+///
+public sealed class GengoFormatProvider : IFormatProvider
+{
+ ///
+ public string Id { get; } = "gengo";
+
+ ///
+ public GengoFormat Create() => new();
+
+ ///
+ public bool SupportsFileName(string fileName)
+ {
+ return SupportedFileExtensions.Any(ext => fileName.EndsWith(ext, StringComparison.OrdinalIgnoreCase));
+ }
+
+ ///
+ public string[] SupportedFileExtensions { get; } = [".xlsx", ".xls"];
+
+ ///
+ public IFormatBuilder GetFormatBuilder() => new GengoFormatBuilder();
+}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/Ashampoo.Translation.Systems.Formats.Gengo.Tests.csproj b/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/Ashampoo.Translation.Systems.Formats.Gengo.Tests.csproj
index a90b4f5..7805575 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/Ashampoo.Translation.Systems.Formats.Gengo.Tests.csproj
+++ b/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/Ashampoo.Translation.Systems.Formats.Gengo.Tests.csproj
@@ -1,18 +1,14 @@
- net7.0
+ net8.0
enable
false
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/FormatTest.cs b/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/FormatTest.cs
index 2803313..c5630e7 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/FormatTest.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/FormatTest.cs
@@ -2,9 +2,10 @@
using System.IO;
using System.Threading.Tasks;
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
using Ashampoo.Translation.Systems.TestBase;
+using FluentAssertions;
using NPOI.XSSF.UserModel;
using Xunit;
@@ -12,13 +13,6 @@ namespace Ashampoo.Translation.Systems.Formats.Gengo.Tests;
public class FormatTest : FormatTestBase
{
- private readonly IFormatFactory formatFactory;
-
- public FormatTest(IFormatFactory formatFactory)
- {
- this.formatFactory = formatFactory;
- }
-
private static XSSFWorkbook CreateFileWithHeaderRow()
{
var workbook = new XSSFWorkbook();
@@ -37,30 +31,23 @@ private static XSSFWorkbook CreateFileWithHeaderRow()
return workbook;
}
- [Fact]
- public void IsAssignableFrom()
- {
- IFormat format = CreateFormat();
-
- Assert.IsAssignableFrom(format);
- }
-
[Fact]
public void NewFormat()
{
var format = CreateFormat();
- Assert.NotNull(format);
- Assert.Empty(format);
- Assert.Null(format.Header.SourceLanguage);
- Assert.Equal(string.Empty, format.Header.TargetLanguage);
- Assert.Equal(FormatLanguageCount.SourceAndTarget, format.LanguageCount);
-
- format = new GengoFormat { Header = new DefaultFormatHeader() { SourceLanguage = "en-US", TargetLanguage = "de-DE" } };
- Assert.NotNull(format);
- Assert.Empty(format);
- Assert.Equal("en-US", format.Header.SourceLanguage);
- Assert.Equal("de-DE", format.Header.TargetLanguage);
+ format.Should().NotBeNull();
+ format.TranslationUnits.Should().BeEmpty();
+ format.Header.SourceLanguage.Should().BeNull();
+ format.Header.TargetLanguage.Value.Should().BeEmpty();
+ format.LanguageSupport.Should().Be(LanguageSupport.SourceAndTarget);
+
+ format = new GengoFormat
+ { Header = new DefaultFormatHeader() { SourceLanguage = new Language("en-US"), TargetLanguage = new Language("de-DE") } };
+ format.Should().NotBeNull();
+ format.TranslationUnits.Should().BeEmpty();
+ format.Header.SourceLanguage.Should().Be(new Language("en-US"));
+ format.Header.TargetLanguage.Should().Be(new Language("de-DE"));
}
@@ -68,50 +55,50 @@ public void NewFormat()
public void ReadFromFile()
{
var format = CreateAndReadFromFile("normalized-excel-test.xlsx",
- new FormatReadOptions { SourceLanguage = "de-DE", TargetLanguage = "en-US" });
+ new FormatReadOptions { SourceLanguage = new Language("de-DE"), TargetLanguage = new Language("en-US") });
- Assert.Equal(4, format.Count);
+ format.TranslationUnits.Count.Should().Be(4);
const string id = "MESSAGES.MESSAGE_BETAVERSION_EXPIRED";
- var foundById = format[id];
- var translationString = foundById?["en-US"] as ITranslationString;
+ var foundById = format.TranslationUnits.GetTranslationUnit(id);
+ var translationString = foundById.Translations.GetTranslation(new Language("en-US"));
const string target =
- @"Unfortunately, the beta version of the software has expired. Please install the final version of this software.%CRLFYou can download it from ‘www.ashampoo.com’.";
- Assert.NotNull(foundById);
- Assert.Equal(2, foundById.Count);
- Assert.Equal(target, translationString?.Value);
+ "Unfortunately, the beta version of the software has expired. Please install the final version of this software.%CRLFYou can download it from ‘www.ashampoo.com’.";
+ foundById.Should().NotBeNull();
+ foundById.Translations.Count.Should().Be(2);
+ translationString.Value.Should().Be(target);
}
[Fact]
public void ReadFromFileWithoutTarget()
{
IFormat format = CreateAndReadFromFile("empty-target-excel-test.xlsx",
- new FormatReadOptions { SourceLanguage = "de-DE", TargetLanguage = "en-US" });
+ new FormatReadOptions { SourceLanguage = new Language("de-DE"), TargetLanguage = new Language("en-US") });
- Assert.Equal("en-US", format.Header.TargetLanguage);
- Assert.Equal("de-DE", format.Header.SourceLanguage);
+ format.Header.TargetLanguage.Should().Be(new Language("en-US"));
+ format.Header.SourceLanguage.Should().Be(new Language("de-DE"));
- Assert.Equal(4, format.Count);
- foreach (var unit in format)
+ format.TranslationUnits.Count.Should().Be(4);
+ foreach (var unit in format.TranslationUnits)
{
- Assert.Equal(2, unit.Count);
+ unit.Translations.Count.Should().Be(2);
}
const string id = "MESSAGES.MESSAGE_BETAVERSION_EXPIRED";
const string value =
"Diese Betaversion der Software ist leider abgelaufen. Bitte installieren Sie die finale Version dieser Software.%CRLFSie können diese z.B. von \"www.ashampoo.com\" herunterladen.";
- var foundById = format[id];
- var translationString = foundById?["de-DE"] as ITranslationString;
-
- Assert.Equal(value, translationString?.Value);
+ var foundById = format.TranslationUnits.GetTranslationUnit(id);
+ foundById.Should().NotBeNull();
+ foundById.Translations.TryGetTranslation(new Language("de-DE"), out var translation).Should().BeTrue();
+ translation?.Value.Should().Be(value);
}
[Fact]
public void ReadAndWrite()
{
IFormat format = CreateAndReadFromFile("normalized-excel-test.xlsx",
- new FormatReadOptions { SourceLanguage = "de-DE", TargetLanguage = "en-US" });
+ new FormatReadOptions { SourceLanguage = new Language("de-DE"), TargetLanguage = new Language("en-US") });
var temp = Path.GetTempPath();
using var outStream = new FileStream($"{temp}temp-normalized-excel-test.xlsx", FileMode.Create,
@@ -128,7 +115,7 @@ public void ReadAndWrite()
public void ReadWithoutTargetAndWrite()
{
IFormat format = CreateAndReadFromFile("empty-target-excel-test.xlsx",
- new FormatReadOptions { SourceLanguage = "de-DE", TargetLanguage = "en-US" });
+ new FormatReadOptions { SourceLanguage = new Language("de-DE"), TargetLanguage = new Language("en-US") });
var temp = Path.GetTempPath();
var outStream = new FileStream($"{temp}temp-empty-target-excel-test.xlsx", FileMode.Create,
FileAccess.Write, FileShare.ReadWrite);
@@ -139,83 +126,6 @@ public void ReadWithoutTargetAndWrite()
File.Delete($"{temp}temp-empty-target-excel-test.xlsx");
}
- [Fact]
- public void ImportSuccessTest()
- {
- IFormat format = CreateAndReadFromFile("normalized-excel-test.xlsx",
- new FormatReadOptions { SourceLanguage = "de-DE", TargetLanguage = "en-US" });
-
- const string id = "MESSAGES.MESSAGE_TRANSLATOR_NAME";
- const string valueSource = "Import Test Source";
- const string valueTarget = "Import Test Target";
- var importedWithUnits =
- format.ImportMockTranslationWithUnits(language: "de-DE", id: id, value: valueSource);
-
- Assert.NotNull(importedWithUnits);
- Assert.Single(importedWithUnits);
- Assert.Equal("Import Test Source", (format[id]?["de-DE"] as ITranslationString)?.Value);
-
- importedWithUnits = format.ImportMockTranslationWithUnits(language: "en-US", id: id, value: valueTarget);
- Assert.NotNull(importedWithUnits);
- Assert.Single(importedWithUnits);
- Assert.Equal("Import Test Target", (format[id]?["en-US"] as ITranslationString)?.Value);
- }
-
- [Fact]
- public void NoMatchImportTest()
- {
- IFormat format = CreateAndReadFromFile("normalized-excel-test.xlsx",
- new FormatReadOptions { SourceLanguage = "de-DE", TargetLanguage = "en-US" });
-
- const string id = "Not matching Import-Id";
- const string value = "Import Test";
- var imported = format.ImportMockTranslationWithUnits(language: "de-DE", id: id, value: value);
- Assert.Empty(imported);
- }
-
- [Fact]
- public void ImportEqualTranslationTest()
- {
- IFormat format = CreateAndReadFromFile("normalized-excel-test.xlsx",
- new FormatReadOptions { SourceLanguage = "de-DE", TargetLanguage = "en-US" });
-
- const string id = "MESSAGES.MESSAGE_TRANSLATOR_NAME";
- const string value = "Ashampoo Development GmbH & Co. KG";
- var imported = format.ImportMockTranslationWithUnits(language: "de-DE", id: id, value: value);
- Assert.Empty(imported);
- }
-
- [Fact]
- public async Task ConvertWithSourceSetTest()
- {
- var mockFormat =
- MockFormatWithTranslationUnits.CreateMockFormatWithTranslationUnits(language: "de-DE",
- id: "Convert Test", value: "Hallo Welt");
- var options = new AssignOptions
- { SourceLanguage = "de-DE", TargetLanguage = "en-US", Filter = new DefaultTranslationFilter() };
- var convertedFormat = await mockFormat.ConvertToAsync(formatFactory, options);
-
- Assert.NotNull(convertedFormat);
-
- Assert.Single(convertedFormat);
- Assert.NotNull(convertedFormat["Convert Test"]);
- Assert.Equal("Hallo Welt", (convertedFormat["Convert Test"]?["de-DE"] as ITranslationString)?.Value);
- }
-
- [Fact]
- public async Task AssignWithSimpleFilter()
- {
- var mockFormat =
- MockFormatWithTranslationUnits.CreateMockFormatWithTranslationUnits(language: "de-DE",
- id: "Convert Test", value: "Hallo Welt");
- var options = new AssignOptions
- { Filter = new IsEmptyTranslationFilter(), SourceLanguage = "de-DE", TargetLanguage = "en-US" };
- var assignedFormat = await mockFormat.ConvertToAsync(formatFactory, options);
- Assert.NotNull(assignedFormat);
-
- Assert.Empty(assignedFormat);
- }
-
[Fact]
public async Task IncompatibleExcelFileTest()
{
@@ -232,7 +142,7 @@ public async Task IncompatibleExcelFileTest()
ms.Seek(0, SeekOrigin.Begin);
var format = CreateFormat();
- var options = new FormatReadOptions { SourceLanguage = "en-US", TargetLanguage = "de-DE" };
+ var options = new FormatReadOptions { SourceLanguage = new Language("en-US"), TargetLanguage = new Language("de-DE") };
await format.ReadAsync(ms, options);
}
@@ -253,18 +163,18 @@ public async Task EmptySourceTest()
ms.Seek(0, SeekOrigin.Begin);
var format = CreateFormat();
- var options = new FormatReadOptions { SourceLanguage = "en-US", TargetLanguage = "de-DE" };
+ var options = new FormatReadOptions { SourceLanguage = new Language("en-US"), TargetLanguage = new Language("de-DE") };
await format.ReadAsync(ms, options);
- Assert.Empty(format);
+ format.TranslationUnits.Should().BeEmpty();
}
[Fact]
public async Task EmptyCellsTest()
{
var format = await CreateAndReadFromFileAsync("empty-cells-excel-test.xlsx",
- new FormatReadOptions { SourceLanguage = "en-US", TargetLanguage = "de-DE" });
- Assert.Empty(format);
+ new FormatReadOptions { SourceLanguage = new Language("en-US"), TargetLanguage = new Language("de-DE") });
+ format.TranslationUnits.Should().BeEmpty();
}
[Fact]
@@ -295,8 +205,8 @@ Task OptionsCallback(FormatOptions options)
var options = new FormatReadOptions { FormatOptionsCallback = OptionsCallback };
await format.ReadAsync(ms, options);
- Assert.True(options.IsCancelled);
- Assert.Empty(format);
+ options.IsCancelled.Should().BeTrue();
+ format.TranslationUnits.Should().BeEmpty();
}
[Fact]
@@ -313,7 +223,7 @@ public async Task NoOptionsCallbackTest()
var exception =
await Assert.ThrowsAsync(async () => await format.ReadAsync(ms, options));
- Assert.Equal("Callback for Format options required.", exception.Message);
+ exception.Message.Should().Be("Callback for Format options required.");
}
[Fact]
@@ -345,11 +255,13 @@ Task OptionsCallback(FormatOptions options)
var options = new FormatReadOptions { FormatOptionsCallback = OptionsCallback };
await format.ReadAsync(ms, options);
- Assert.Equal("en-US", format.Header.SourceLanguage);
- Assert.Equal("de-DE", format.Header.TargetLanguage);
- Assert.Single(format);
- Assert.NotNull(format["ID Test"]);
- Assert.Equal("Test source", (format["ID Test"]?["en-US"] as ITranslationString)?.Value);
- Assert.Equal("Test target", (format["ID Test"]?["de-DE"] as ITranslationString)?.Value);
+ format.Header.SourceLanguage.Should().Be(new Language("en-US"));
+ format.Header.TargetLanguage.Should().Be(new Language("de-DE"));
+ format.TranslationUnits.Should().ContainSingle();
+ format.TranslationUnits.GetTranslationUnit("ID Test").Should().NotBeNull();
+ format.TranslationUnits.GetTranslationUnit("ID Test").Translations.GetTranslation(new Language("en-US")).Value.Should()
+ .Be("Test source");
+ format.TranslationUnits.GetTranslationUnit("ID Test").Translations.GetTranslation(new Language("de-DE")).Value.Should()
+ .Be("Test target");
}
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/Startup.cs b/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/Startup.cs
deleted file mode 100644
index b19cff2..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Gengo/tests/Startup.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.IO;
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Toolkit.Diagnostics;
-
-namespace Ashampoo.Translation.Systems.Formats.Gengo.Tests;
-
-public class Startup
-{
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddFormatFactory().RegisterFormat();
- }
-}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Json/src/Ashampoo.Translation.Systems.Formats.Json.csproj b/src/Ashampoo.Translation.Systems.Formats.Json/src/Ashampoo.Translation.Systems.Formats.Json.csproj
index 252bc8f..0a26875 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Json/src/Ashampoo.Translation.Systems.Formats.Json.csproj
+++ b/src/Ashampoo.Translation.Systems.Formats.Json/src/Ashampoo.Translation.Systems.Formats.Json.csproj
@@ -1,25 +1,10 @@
- net7.0
+ net8.0
enable
enable
- Ashampoo.Translation.Systems.Formats.Json
- tjorvenK
- ashampoo
- Package containing the implementation for the Json format.
- LICENSE
- localization;translation;json
true
- true
- true
- true
- snupkg
- ash-logo-icon-big-128x.png
- v
- true
- prerelease.0
- README.md
@@ -33,7 +18,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Ashampoo.Translation.Systems.Formats.Json/src/DependencyInjectionExtension.cs b/src/Ashampoo.Translation.Systems.Formats.Json/src/DependencyInjectionExtension.cs
new file mode 100644
index 0000000..93a71b2
--- /dev/null
+++ b/src/Ashampoo.Translation.Systems.Formats.Json/src/DependencyInjectionExtension.cs
@@ -0,0 +1,31 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Ashampoo.Translation.Systems.Formats.Json;
+
+///
+/// Static class that contains extension methods for .
+///
+public static class DependencyInjection
+{
+ ///
+ /// Registers all necessary services for the Gengo format.
+ ///
+ ///
+ /// The to register the services with.
+ ///
+ ///
+ /// The for chaining.
+ ///
+ ///
+ /// Thrown if something went wrong during the registration.
+ ///
+ public static IServiceCollection AddJsonFormatFeatures(this IServiceCollection services)
+ {
+ services.AddSingleton()
+ .AddSingleton>(sp => sp.GetRequiredService())
+ .AddSingleton>(sp => sp.GetRequiredService());
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormat.cs b/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormat.cs
index e08f7f1..6cfaea2 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormat.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormat.cs
@@ -3,22 +3,26 @@
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using CommunityToolkit.Diagnostics;
-using IFormatProvider = Ashampoo.Translation.Systems.Formats.Abstractions.IFormatProvider;
namespace Ashampoo.Translation.Systems.Formats.Json;
///
/// Implementation of interface, for JSON files.
///
-public class JsonFormat : AbstractTranslationUnits, IFormat
+public class JsonFormat : IFormat
{
///
public IFormatHeader Header { get; init; } = new DefaultFormatHeader();
///
- public FormatLanguageCount LanguageCount => FormatLanguageCount.OnlyTarget;
+ public LanguageSupport LanguageSupport => LanguageSupport.OnlyTarget;
+
+ ///
+ public ICollection TranslationUnits { get; } = new List();
+
private const string Divider = "/";
private static readonly Regex ArrayIdentifierRegex = new(@"\[\d+\]");
@@ -33,7 +37,7 @@ public async Task ReadAsync(Stream stream, FormatReadOptions? options = null)
return;
}
- Guard.IsNotNullOrWhiteSpace(Header.TargetLanguage, nameof(Header.TargetLanguage)); //Target language is required
+ Guard.IsNotNullOrWhiteSpace(Header.TargetLanguage.Value, nameof(Header.TargetLanguage)); //Target language is required
var root = await JsonSerializer.DeserializeAsync(stream); // Deserialize JSON
Parse(root); // Parse JSON to TranslationUnits
@@ -41,7 +45,7 @@ public async Task ReadAsync(Stream stream, FormatReadOptions? options = null)
private async Task ConfigureOptionsAsync(FormatReadOptions? options)
{
- if (string.IsNullOrWhiteSpace(options?.TargetLanguage))
+ if (string.IsNullOrWhiteSpace(options?.TargetLanguage.Value))
{
ArgumentNullException.ThrowIfNull(options?.FormatOptionsCallback,
nameof(options.FormatOptionsCallback)); // Format options callback is required
@@ -49,20 +53,20 @@ private async Task ConfigureOptionsAsync(FormatReadOptions? options)
FormatStringOption targetLanguageOption = new("Target language", true);
FormatOptions formatOptions = new()
{
- Options = new FormatOption[]
- {
+ Options =
+ [
targetLanguageOption
- }
+ ]
};
await options.FormatOptionsCallback.Invoke(formatOptions); // Invoke callback to get format options
if (formatOptions.IsCanceled) return false; // If user cancelled, return false
- Header.TargetLanguage = targetLanguageOption.Value;
+ Header.TargetLanguage = Language.Parse(targetLanguageOption.Value);
}
else
{
- Header.TargetLanguage = options.TargetLanguage;
+ Header.TargetLanguage = (Language)options.TargetLanguage!;
}
return true;
@@ -106,10 +110,13 @@ private void ParseObject(string id, JsonElement element)
var translationUnit = new DefaultTranslationUnit(nextId) // Create translation unit
{
- translationString
+ Translations =
+ {
+ translationString
+ }
};
- Add(translationUnit); // Add translation unit to list
+ TranslationUnits.Add(translationUnit); // Add translation unit to list
}
else
Parse(nextId, property.Value); // Parse next element
@@ -139,8 +146,13 @@ private void ParseArray(string id, JsonElement element)
);
var translationUnit = new DefaultTranslationUnit(nextId)
- { translationString }; // Create translation unit
- Add(translationUnit); // Add translation unit to list
+ {
+ Translations =
+ {
+ translationString
+ }
+ };
+ TranslationUnits.Add(translationUnit); // Add translation unit to list
break;
}
case JsonValueKind.Array:
@@ -181,7 +193,7 @@ public async Task WriteAsync(Stream stream)
private void CreateJsonObjects(JsonObject obj)
{
- foreach (var unit in this)
+ foreach (var unit in TranslationUnits)
{
var id = unit.Id;
var index = id.IndexOf(Divider,
@@ -199,8 +211,8 @@ private void CreateJsonObject(string id, string trailing, JsonObject obj, ITrans
{
if (trailing.Length == 0) // If trailing is empty, element has no nested elements
{
- var value = (unit[Header.TargetLanguage] as ITranslationString)?.Value ??
- throw new ArgumentNullException(nameof(ITranslationString.Value));
+ var value = unit.Translations.GetTranslation(Header.TargetLanguage).Value ??
+ throw new ArgumentNullException(nameof(ITranslation.Value));
var jsonValue = JsonValue.Create(value); // create json element from value
obj.Add(id, jsonValue);
return;
@@ -212,18 +224,18 @@ private void CreateJsonObject(string id, string trailing, JsonObject obj, ITrans
{
> 0 => trailing[(index + Divider.Length)..],
_ => string.Empty
- };
+ };
if (ArrayIdentifierRegex.IsMatch(front)) // Check if next element is an array item
{
// Next element is an array item, so current element is an array
-
+
foreach (var (key, value) in obj)
{
if (key != id) continue; // find the array element with the same id as the current element
var array = value?.AsArray() ?? throw new Exception("Expected array."); // get the array
- CreateJsonArray(front, newTrailing, array, unit);
+ CreateJsonArray(front, newTrailing, array, unit);
return;
}
@@ -232,9 +244,9 @@ private void CreateJsonObject(string id, string trailing, JsonObject obj, ITrans
obj.Add(id, newArray);
return;
}
-
+
// Next element is an object element
-
+
foreach (var pair in obj) // Check if object element with the same id exists
{
if (pair.Key != id) continue;
@@ -243,7 +255,7 @@ private void CreateJsonObject(string id, string trailing, JsonObject obj, ITrans
CreateJsonObject(front, newTrailing, jsonObj, unit);
return;
}
-
+
// Object element doesn't exist, create new object
var newJsonObj = new JsonObject();
@@ -257,19 +269,24 @@ private void CreateJsonArray(string id, string trailing, JsonArray array, ITrans
{
if (trailing.Length == 0) // Current element does not have nested elements
{
- var value = (unit[Header.TargetLanguage] as ITranslationString)?.Value ??
- throw new ArgumentNullException(nameof(ITranslationString.Value));
+ var value = unit.Translations.GetTranslation(Header.TargetLanguage).Value ??
+ throw new ArgumentNullException(nameof(ITranslation.Value));
var jsonValue = JsonValue.Create(value);
array.Add(jsonValue);
return;
}
-
+
// Current element has nested elements
var position = int.Parse(id.Trim('[', ']')); // Get array index from id
var index = trailing.IndexOf(Divider, StringComparison.Ordinal); // Get index of next divider
- var front = index > 0 ? trailing[..index] : trailing; // Front part of trailing is the id for the next element
- var newTrailing = index > 0 ? trailing[(index + Divider.Length)..] : ""; // Trailing part of trailing is the trailing part of the next element
+ var front = index > 0
+ ? trailing[..index]
+ : trailing; // Front part of trailing is the id for the next element
+ var newTrailing =
+ index > 0
+ ? trailing[(index + Divider.Length)..]
+ : ""; // Trailing part of trailing is the trailing part of the next element
var element = array.ElementAtOrDefault(position); // Get array element at position
@@ -297,14 +314,4 @@ private void CreateJsonArray(string id, string trailing, JsonArray array, ITrans
break;
}
}
-
- ///
- public Func BuildFormatProvider()
- {
- return builder => builder.SetId("json")
- .SetSupportedFileExtensions(new[] { ".json" })
- .SetFormatType()
- .SetFormatBuilder()
- .Create();
- }
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormatBuilder.cs b/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormatBuilder.cs
index 3c45601..d996b5d 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormatBuilder.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormatBuilder.cs
@@ -1,4 +1,5 @@
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
using CommunityToolkit.Diagnostics;
@@ -7,37 +8,37 @@ namespace Ashampoo.Translation.Systems.Formats.Json;
///
/// Builder for the .
///
-public class JsonFormatBuilder : IFormatBuilderWithTarget
+public class JsonFormatBuilder : IFormatBuilderWithTarget
{
- private string? targetLanguage;
- private readonly Dictionary translations = new();
+ private Language? _targetLanguage;
+ private readonly Dictionary _translations = new();
///
public void Add(string id, string target)
{
- translations.Add(id, target);
+ _translations.Add(id, target);
}
///
- public IFormat Build()
+ public JsonFormat Build()
{
- Guard.IsNotNullOrWhiteSpace(targetLanguage, nameof(targetLanguage));
+ Guard.IsNotNullOrWhiteSpace(_targetLanguage?.Value, nameof(_targetLanguage));
//create new json format and add translations
var jsonFormat = new JsonFormat
{
Header =
{
- TargetLanguage = targetLanguage
+ TargetLanguage = (Language)_targetLanguage!
}
};
- foreach (var translation in translations)
+ foreach (var translation in _translations)
{
var translationUnit = new DefaultTranslationUnit(translation.Key);
- var translationString = new DefaultTranslationString(translation.Key, translation.Value, targetLanguage);
- translationUnit.Add(translationString);
- jsonFormat.Add(translationUnit);
+ var translationString = new DefaultTranslationString(translation.Key, translation.Value, (Language)_targetLanguage);
+ translationUnit.Translations.Add(translationString);
+ jsonFormat.TranslationUnits.Add(translationUnit);
}
return jsonFormat;
@@ -63,8 +64,8 @@ public void AddHeaderInformation(string key, string value)
}
///
- public void SetTargetLanguage(string language)
+ public void SetTargetLanguage(Language language)
{
- targetLanguage = language;
+ _targetLanguage = language;
}
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormatProvider.cs b/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormatProvider.cs
new file mode 100644
index 0000000..0e6b388
--- /dev/null
+++ b/src/Ashampoo.Translation.Systems.Formats.Json/src/JsonFormatProvider.cs
@@ -0,0 +1,27 @@
+using Ashampoo.Translation.Systems.Formats.Abstractions;
+
+namespace Ashampoo.Translation.Systems.Formats.Json;
+
+///
+/// The for the .
+///
+public sealed class JsonFormatProvider : IFormatProvider
+{
+ ///
+ public string Id { get; } = "json";
+
+ ///
+ public JsonFormat Create() => new();
+
+ ///
+ public bool SupportsFileName(string fileName)
+ {
+ return SupportedFileExtensions.Any(ext => fileName.EndsWith(ext, StringComparison.OrdinalIgnoreCase));
+ }
+
+ ///
+ public string[] SupportedFileExtensions { get; } = [".json"];
+
+ ///
+ public IFormatBuilder GetFormatBuilder() => new JsonFormatBuilder();
+}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Json/tests/Ashampoo.Translation.Systems.Formats.Json.Tests.csproj b/src/Ashampoo.Translation.Systems.Formats.Json/tests/Ashampoo.Translation.Systems.Formats.Json.Tests.csproj
index 8ceae5b..7d53566 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Json/tests/Ashampoo.Translation.Systems.Formats.Json.Tests.csproj
+++ b/src/Ashampoo.Translation.Systems.Formats.Json/tests/Ashampoo.Translation.Systems.Formats.Json.Tests.csproj
@@ -1,18 +1,14 @@
- net7.0
+ net8.0
enable
false
-
-
-
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
-
+
+
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/src/Ashampoo.Translation.Systems.Formats.Json/tests/FormatTest.cs b/src/Ashampoo.Translation.Systems.Formats.Json/tests/FormatTest.cs
index b8d55e9..2c9fc88 100644
--- a/src/Ashampoo.Translation.Systems.Formats.Json/tests/FormatTest.cs
+++ b/src/Ashampoo.Translation.Systems.Formats.Json/tests/FormatTest.cs
@@ -4,59 +4,44 @@
using System.Text.Json;
using System.Threading.Tasks;
using Ashampoo.Translation.Systems.Formats.Abstractions;
+using Ashampoo.Translation.Systems.Formats.Abstractions.Models;
using Ashampoo.Translation.Systems.Formats.Abstractions.Translation;
-using Ashampoo.Translation.Systems.Formats.Abstractions.TranslationFilter;
using Ashampoo.Translation.Systems.TestBase;
+using FluentAssertions;
using Xunit;
namespace Ashampoo.Translation.Systems.Formats.Json.Tests;
public class FormatTest : FormatTestBase
{
- private readonly IFormatFactory formatFactory;
-
- public FormatTest(IFormatFactory formatFactory)
- {
- this.formatFactory = formatFactory;
- }
-
- [Fact]
- public void IsAssignableFrom()
- {
- IFormat format = CreateFormat();
-
- // provides translation units
- Assert.IsAssignableFrom(format);
- }
-
[Fact]
public void NewFormat()
{
var format = CreateFormat();
- Assert.NotNull(format);
- Assert.Empty(format);
- Assert.Null(format.Header.SourceLanguage);
- Assert.Equal(string.Empty, format.Header.TargetLanguage);
- Assert.Equal(FormatLanguageCount.OnlyTarget, format.LanguageCount);
+ format.Should().NotBeNull();
+ format.TranslationUnits.Should().BeEmpty();
+ format.Header.SourceLanguage.Should().BeNull();
+ format.Header.TargetLanguage.Value.Should().BeEmpty();
+ format.LanguageSupport.Should().Be(LanguageSupport.OnlyTarget);
}
[Fact]
public void ReadFromFile()
{
- IFormat format = CreateAndReadFromFile("en-us.json", new FormatReadOptions { TargetLanguage = "en-US" });
-
- Assert.Equal(301, format.Count);
- Assert.Equal("en-US", format.Header.TargetLanguage);
- Assert.Null(format.Header.SourceLanguage);
- Assert.Equal("Save", (format["settings/save"]?["en-US"] as ITranslationString)?.Value);
+ IFormat format = CreateAndReadFromFile("en-us.json", new FormatReadOptions { TargetLanguage = new Language("en-US") });
+
+ format.TranslationUnits.Should().HaveCount(301);
+ format.Header.TargetLanguage.Should().Be(new Language("en-US"));
+ format.Header.SourceLanguage.Should().BeNull();
+ format.TranslationUnits.GetTranslationUnit("settings/save").Translations.GetTranslation(new Language("en-US")).Value.Should().Be("Save");
}
[Fact]
public void ReadAndWrite()
{
- IFormat format = CreateAndReadFromFile("de-DE.json", new FormatReadOptions { TargetLanguage = "de-DE" });
+ IFormat format = CreateAndReadFromFile("de-DE.json", new FormatReadOptions { TargetLanguage = new Language("de-DE") });
var ms = new MemoryStream();
format.Write(ms);
@@ -75,106 +60,29 @@ public void ReadAndWrite()
[InlineData("json-value-kind-number-test.json")]
public async Task InvalidValueKindsTest(string filename)
{
- var options = new FormatReadOptions { TargetLanguage = "de-DE" };
+ var options = new FormatReadOptions { TargetLanguage = new Language("de-DE") };
var exception =
await Assert.ThrowsAsync(async () => await CreateAndReadFromFileAsync(filename, options));
- Assert.Equal("Array element must be either an object, array or a string.", exception.Message);
+ exception.Message.Should().Be("Array element must be either an object, array or a string.");
}
[Fact]
public async Task ReadAndWriteWithReorder()
{
- var format = await CreateAndReadFromFileAsync("de-DE.json", new FormatReadOptions { TargetLanguage = "de-DE" });
+ var format = await CreateAndReadFromFileAsync("de-DE.json", new FormatReadOptions { TargetLanguage = new Language("de-DE") });
- var units = format.OrderBy(u => u.Id);
+ var units = format.TranslationUnits.OrderBy(u => u.Id);
format = new JsonFormat { Header = format.Header };
foreach (var unit in units)
{
- format.Add(unit);
+ format.TranslationUnits.Add(unit);
}
var ms = new MemoryStream();
await format.WriteAsync(ms);
ms.Seek(0, SeekOrigin.Begin);
}
-
- [Fact]
- public async Task SimpleAssign()
- {
- var mock =
- MockFormatWithTranslationUnits.CreateMockFormatWithTranslationUnits("en-US", "ID", "Hello World");
- var converted = await mock.ConvertToAsync(formatFactory,
- new AssignOptions { TargetLanguage = "en-US", Filter = new DefaultTranslationFilter() });
-
- Assert.Single(converted);
- Assert.Single(converted["ID"] ?? Enumerable.Empty());
- Assert.Equal("Hello World", (converted["ID"]?["en-US"] as ITranslationString)?.Value);
- }
-
- [Fact]
- public async Task ComplexAssign()
- {
- const string id = "peru.CSystem.CreateUniqueFileFailed";
- var mockFormat = new MockFormatWithTranslationUnits
- {
- { "en-US", id, "Error creating unique file name." },
- { "de-DE", id, "Fehler beim Erzeugen eines eindeutigen Dateinamens" }
- };
-
- var optionsEn = new AssignOptions { TargetLanguage = "en-US", Filter = new DefaultTranslationFilter() };
- var optionsDe = new AssignOptions { TargetLanguage = "de-DE", Filter = new DefaultTranslationFilter() };
-
- var convertedEnUs = await mockFormat.ConvertToAsync(formatFactory, optionsEn);
- var convertedDeDe = await mockFormat.ConvertToAsync(formatFactory, optionsDe);
-
-
- Assert.Equal("en-US", convertedEnUs.Header.TargetLanguage);
- Assert.Equal("de-DE", convertedDeDe.Header.TargetLanguage);
-
- Assert.Equal("Error creating unique file name.",
- (convertedEnUs[id]?["en-US"] as ITranslationString)?.Value);
- Assert.Equal("Fehler beim Erzeugen eines eindeutigen Dateinamens",
- (convertedDeDe[id]?["de-DE"] as ITranslationString)?.Value);
- }
-
- [Fact]
- public void ImportSuccessTest()
- {
- IFormat format = CreateAndReadFromFile("en-us.json", new FormatReadOptions { TargetLanguage = "en-US" });
-
- const string id = "settings/save";
- const string value = "Import Test";
-
- var importedWithUnits = format.ImportMockTranslationWithUnits(language: "en-US", id: id, value: value);
- Assert.NotNull(importedWithUnits);
- Assert.Single(importedWithUnits);
- Assert.Equal("Import Test", (format[id]?["en-US"] as ITranslationString)?.Value);
- }
-
- [Fact]
- public void NoMatchImportTest()
- {
- IFormat format = CreateAndReadFromFile("en-us.json", new FormatReadOptions { TargetLanguage = "en-US" });
-
- const string id = "Not matching Import-Id";
- const string value = "Import Test";
- var imported = format.ImportMockTranslationWithUnits(language: "en-US", id: id, value: value);
-
- Assert.Empty(imported);
- }
-
- [Fact]
- public void ImportEqualTranslationTest()
- {
- IFormat format = CreateAndReadFromFile("en-us.json", new FormatReadOptions { TargetLanguage = "en-US" });
-
- const string id = "settings/save";
- const string value = "Save";
- var imported = format.ImportMockTranslationWithUnits(language: "en-US", id: id, value: value);
-
- Assert.Empty(imported);
- }
-
+
[Fact]
public async Task OptionsCallbackCancelledTest()
{
@@ -186,9 +94,9 @@ Task OptionsCallback(FormatOptions options)
var format = await CreateAndReadFromFileAsync("en-us.json",
new FormatReadOptions { FormatOptionsCallback = OptionsCallback });
- Assert.Null(format.Header.SourceLanguage);
- Assert.Equal(string.Empty, format.Header.TargetLanguage);
- Assert.Empty(format);
+ format.Header.SourceLanguage.Should().BeNull();
+ format.Header.TargetLanguage.Value.Should().BeEmpty();
+ format.TranslationUnits.Should().BeEmpty();
}
[Fact]
@@ -196,8 +104,8 @@ public async Task NoOptionsCallbackTest()
{
var exception = await Assert.ThrowsAsync(async () =>
await CreateAndReadFromFileAsync("en-us.json", new FormatReadOptions()));
- Assert.Equal("Value cannot be null. (Parameter 'FormatOptionsCallback')", exception.Message);
- Assert.Equal("FormatOptionsCallback", exception.ParamName);
+ exception.Message.Should().Be("Value cannot be null. (Parameter 'FormatOptionsCallback')");
+ exception.ParamName.Should().Be("FormatOptionsCallback");
}
[Fact]
@@ -212,8 +120,8 @@ Task OptionsCallback(FormatOptions options)
var options = new FormatReadOptions { FormatOptionsCallback = OptionsCallback };
var format = await CreateAndReadFromFileAsync("de-DE.json", options);
- Assert.Equal("de-DE", format.Header.TargetLanguage);
- Assert.Null(format.Header.SourceLanguage);
- Assert.Equal(189, format.Count);
+ format.Header.TargetLanguage.Should().Be(new Language("de-DE"));
+ format.Header.SourceLanguage.Should().BeNull();
+ format.TranslationUnits.Should().HaveCount(189);
}
}
\ No newline at end of file
diff --git a/src/Ashampoo.Translation.Systems.Formats.Json/tests/Startup.cs b/src/Ashampoo.Translation.Systems.Formats.Json/tests/Startup.cs
deleted file mode 100644
index fae9891..0000000
--- a/src/Ashampoo.Translation.Systems.Formats.Json/tests/Startup.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.IO;
-using Ashampoo.Translation.Systems.Formats.Abstractions;
-using CommunityToolkit.Diagnostics;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Ashampoo.Translation.Systems.Formats.Json.Tests;
-
-public class Startup
-{
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddFormatFactory().RegisterFormat