UC2: Automatisch vertalen project
- 1. Front-end componenten
- 2. Back-end componenten
- 3. API endpoints
- 4. Technische keuzes
- 5. Test cases
- Bronnen
Functioneel ontwerp use case: UC2: Automatisch vertalen project
1. Front-end componenten
Voor de selectie van namespaces zijn 2 nieuwe componenten gemaakt:
- NamespaceOption & NamespaceControl: Dit component wordt gebruikt om een namespace in een project te selecteren uit de namespaces die in het project beschikbaar zijn. Dit component gebruikt als basis nog steeds het template FormSelect component, maar de Option en Control componenten daarbinnen zijn vervangen om de namespaces mooi weer te kunnen geven.
Voor deze use case zijn een tweetal nieuwe pagina's gemaakt, namelijk:
- Vertalingen pagina: Hier kan de gebruiker de vertalingen in het project bekijken.
- Vertalingen niet geconfigureerd pagina: Hier wordt de gebruiker naartoe gestuurd wanneer het project niet verbonden is met een versiebeheersysteem repository.
De vertalingen pagina bevat een tabel met de vertalingen in het project. De vertalingen worden opgehaald via de back-end API. Als het project niet verbonden is met een versiebeheersysteem repository, wordt de gebruiker naar de vertalingen niet geconfigureerd pagina gestuurd.
2. Back-end componenten
In het volgende diagram zijn de verschillende onderdelen van de back-end van UC2 weergegeven. De TranslationsFileService bevat een lijst van interfaces ITranslationFileType (deze staat niet in het diagram). In dit geval is er alleen een implementatie voor JSONTranslationFile, maar deze kan in de toekomst uitgebreid worden. De TranslatorService bevat één implementatie van de ITranslateService interface (deze staat niet in het diagram). In dit geval is dit de GoogleCloudTranslateService die communiceert met de API via de Google.Cloud.Translate.V3 Nuget Package.
3. API endpoints
3.1. VersionControlController
Webhook endpoint voor merge request
Endpoint: /api/VersionControl/systems/{system}/webhooks/{repositoryProjectId}
Method: GET
Rechten: Publiek
Beschrijving: Handelt de webhook van het git versiebeheersysteem af
Query parameters:
- system (VersionControlType): het systeem waarvan de webhook vandaan komt (GitLab, Azure DevOps, etc.)
- repositoryProjectId (string): het projectId van het versiebeheersysteem repository project
3.2. TranslatorController
Ophalen ondersteunde talen
Endpoint: /api/Translator/languages
Method: GET
Rechten: Ingelogd
Beschrijving: Haalt een lijst van ondersteunde talen op
3.3. ProjectTranslationsController
ProjectTranslationsController
Ophalen gebruikte talen in project
Endpoint: /api/ProjectTranslations/{projectId:guid}/languages
Method: GET
Rechten: Ingelogd
Beschrijving: Haalt een lijst van gebruikte talen in het project op
Responses
Status code: 200 (OK), gebruikte talen in project zijn opgehaald.
Status code: 400 (Bad Request), mogelijke oorzaken:
- project ID is incorrect
- project is niet verbonden met versiebeheersysteem repository
Ophalen namespaces
Endpoint: /api/ProjectTranslations/{projectId:guid}/namespaces
Method: GET
Rechten: Ingelogd
Beschrijving: Haalt een lijst van namespaces in het project op
Responses
Status code: 200 (OK), namespaces zijn opgehaald.
Status code: 400 (Bad Request), mogelijke oorzaken:
- project ID is incorrect
- project is niet verbonden met versiebeheersysteem repository
- Geen vertalingsbestanden gevonden in het project
- Geen vertalingsbestanden gevonden in de opgegeven taal
Ophalen vertalingen in namespace
Endpoint: /api/ProjectTranslations/{projectId:guid}/translations?namespace={namespace}&basePath={basePath}
Method: GET
Rechten: Ingelogd
Beschrijving: Haalt vertalingen in alle talen binnen een namespace op
Query parameters:
- namespace (string): namespace waarvan de vertalingen opgehaald moeten worden (common.json, etc.)
- basePath (string): het pad waarin de vertalingsbestanden zich bevinden (/template.bluenotion.nl/src/i18n/, etc.)
Responses
Status code: 200 (OK), vertalingen zijn opgehaald.
Status code: 400 (Bad Request), mogelijke oorzaken:
- project ID is incorrect
- project is niet verbonden met versiebeheersysteem repository
- Geen vertalingsbestanden gevonden in het project
- Geen vertalingsbestanden gevonden in de opgegeven taal
- Namespace is niet gevonden in het project
- Geen vertalingsbestanden gevonden voor de opgegeven namespace of basePath
- namespace is niet opgegeven
- basePath is niet opgegeven
Cache voor project leegmaken
Endpoint: /api/ProjectTranslations/{projectId:guid}/cache
Method: DELETE
Rechten: Ingelogd
Beschrijving: Maakt de cache voor een project leeg
Ophalen kosten genereren taal
Endpoint: /api/ProjectTranslations/{projectId:guid}/costs-calculation
Method: GET
Rechten: Ingelogd
Beschrijving: Haalt de variabelen voor het berekenen van de kosten bij het genereren van een nieuwe taal in het project
Responses
Status code: 200 (OK), kosten zijn opgehaald.
Status code: 400 (Bad Request), mogelijke oorzaken:
- project ID is incorrect
- project is niet verbonden met versiebeheersysteem repository
- er konden geen vertalingsbestanden gevonden worden in het project
Status code: 422 (Unprocessable Entity), mogelijke oorzaken:
- targetLanguageCode is niet opgegeven
Aanmaken nieuwe gegenereerde taal
Endpoint: /api/ProjectTranslations/{projectId:guid}/languages
Method: POST
Rechten: Ingelogd
Beschrijving: Maakt een nieuwe taal in het project aan
Responses
Status code: 200 (OK), nieuwe taal is gegenereerd.
Status code: 400 (Bad Request), mogelijke oorzaken:
- project ID is incorrect
- project is niet verbonden met versiebeheersysteem repository
- targetLanguageCode wordt niet ondersteund
- geen vertalingsbestanden gevonden
- vertalingen in de opgegeven targetLanguageCode bestaan al in het project
Status code: 422 (Unprocessable Entity), mogelijke oorzaken:
- targetLanguageCode is niet opgegeven
- targetLanguageCode is te lang (max 10 characters)
- useAITranslator is niet opgegeven
4. Technische keuzes
4.1. Abstractie voor koppeling met AI vertaling tool
De applicatie maakt gebruik van een AI tool om vertalingen uit vertalingsbestanden in een project automatisch te vertalen. In de huidige planning voor de MVP wordt de applicatie alleen gebouwd om Google Cloud Translate te ondersteunen (Volgens het onderzoek). De reden voor deze keuze is om de applicatie in de toekomst compatibel te maken met andere AI tools, mocht de prestatie of functionaliteit van de nieuwe tools beter zijn dan de huidige AI tool. Om de applicatie hierop voor te bereiden is er een abstractie laag gemaakt die de koppeling met AI tools afhandelt. Hierdoor is het mogelijk om in de toekomst eenvoudig een andere AI tool te koppelen.
Voor de abstractie is gebruik gemaakt van het (Strategy Pattern, n.d.). Dit patroon zorgt ervoor dat er gemakkelijk gewisseld kan worden van implementatie mocht dit ook nodig zijn.
4.2. Abstractie voor de vertalingsbestanden
De applicatie maakt gebruik van vertalingsbestanden om de vertalingen op te slaan. In de huidige planning voor de MVP wordt de applicatie alleen gebouwd om JSON bestanden te ondersteunen. Er is tijdens het vastleggen van de functionele eisen echter wel duidelijk geworden dat de applicatie in de toekomst mogelijk ook gebruikt gaat worden met andere type vertalingsbestanden. Om de applicatie hierop voor te bereiden is er een abstractie laag gemaakt die ervoor zorgt dat de applicatie eenvoudig kan worden uitgebreid met andere type vertalingsbestanden. In Sprint 6 is de MVP afgerond, en is de ondersteuning van Scriban bestanden toegevoegd aan de planning, waarna deze is afgerond in Sprint 7. In Sprint 8 is de ondersteuning van ResX bestanden toegevoegd aan de planning, waarna deze is afgerond in Sprint 9.
Voor de abstractie is gebruik gemaakt van het (Adapter Pattern, n.d.). Dit patroon zorgt ervoor dat er gemakkelijk meerdere implementaties toegevoegd kunnen worden.De interface van de adapter maakt het mogelijk om meerdere vertalingsbestanden tegelijk te ondersteunen.
Het volgende diagram geeft een idee over hoe de abstractie laag werkt, in het diagram zijn verschillende modellen, methoden en properties weggelaten om het diagram overzichtelijk te houden. De TranslationFileService bevat een lijst van alle ondersteunde vertalingsbestand types . De IsTranslationFile methode probeert de passende implementatie te vinden, als deze geen passende implementatie kan vinden wordt het bestand overslagen.
Het pad van het bestand wordt gebruikt om te bepalen of het bestand een ondersteund type vertalingsbestand is en welke implementatie daarbij hoort.
TranslationFileType is een enum met alle verschillende ondersteunde vertalingsbestand types.
classDiagram
ITranslationFileType <|.. JsonTranslationFile : implements
JsonTranslationFile : public bool IsTranslationFile(string filePath)
JsonTranslationFile : public TranslationFileModel GetTranslationFileModel(string fileName, string filePath, string fileNameWithoutExtension)
JsonTranslationFile : ...
JsonTranslationFile : ...()
ITranslationFileType <|.. ScribanTranslationFile : implements
ScribanTranslationFile : public bool IsTranslationFile(string filePath)
ScribanTranslationFile : public TranslationFileModel GetTranslationFileModel(string fileName, string filePath, string fileNameWithoutExtension)
ScribanTranslationFile : ...
ScribanTranslationFile : ...()
ITranslationFileType <|.. ResXTranslationFile : implements
ResXTranslationFile : public bool IsTranslationFile(string filePath)
ResXTranslationFile : public TranslationFileModel GetTranslationFileModel(string fileName, string filePath, string fileNameWithoutExtension)
ResXTranslationFile : ...
ResXTranslationFile : ...()
TranslationFileService --> ITranslationFileType : contains
ITranslationFileType : public bool IsTranslationFile(string filePath)
ITranslationFileType : public TranslationFileModel GetTranslationFileModel(string fileName, string filePath, string fileNameWithoutExtension)
<<interface>> ITranslationFileType
ITranslationFileType : ...
ITranslationFileType : ...()
class TranslationFileService{
private readonly ITranslationFileType[] translationFileTypes
...
public bool IsTranslationFile(string filePath)
...()
}
class TranslationFileModel{
public TranslationFileType FileType
public string FileName
public string FileNameWithoutExtension
public string FilePath
...
}
4.3 Cache
Tijdens de ontwikkeling van de back-end voor deze use case bleek dat het ophalen van de vertalingsbestanden in een project veel tijd op kon nemen. Om de prestatie van de applicatie te verbeteren is er een cache geïmplementeerd. De cache zorgt ervoor dat vertalingen niet elke keer opnieuw opgehaald hoeven te worden uit de vertalingsbestanden. De cache wordt geleegd wanneer er nieuwe vertalingen worden gegenereerd in het project. Ook is er een optie om de endpoint aan te roepen met een forceUpdate parameter, waardoor de cache wordt geleegd en de vertalingen opnieuw worden opgehaald.
Er is cache geïmplementeerd voor:
1. Ophalen van alle vertalingsbestanden (met inhoud) in een project vanuit het versiebeheersysteem.
2. Ophalen van alle vertalingsbestanden (zonder inhoud) in een project vanuit het versiebeheersysteem.
4.4 Merge hook
Tijdens het uitwerken van de koppeling met GitLab ontstond een probleem, als de vertalingen bij iedere aanpassing op een nieuwe branch zouden komen te staan zou het erg lastig worden om bij te houden wat de huidige vertalingen zijn. Vandaar dat er na overleg met de product owner besloten is om alle aanpassingen gerelateerd aan de vertalingen in één branch te zetten. Dit loste het probleem over de meerdere locaties op, in de database zou bijgehouden worden welke branch nu actief was. Dit zou dan de main branch, of de branch met de vertalingen zijn.
Hierna bleek nog een obstakel te ontstaan, namelijk: hoe zorgen we ervoor dat de vertalingen weer van de main branch opgehaald worden als de branch met de vertalingen verdwijnt, doordat de merge request bijvoorbeeld afgerond werd, of werd verwijdert. Hieruit volgde, na nogmaals een keer overleg met de product owner, de keuze om GitLab een webhook af te laten vuren wanneer er een actie op een merge request uitgevoerd werd.
Deze flow geldt ook voor het later toegevoegde Azure DevOps systeem.
In de server kon dan gecontroleerd worden of het event de bijbehorende waardes bevatte, en als dit zo was kon de branch in de database bijgewerkt worden. Verder bleek na wat literatuur onderzoek ook dat vrijwel alle git versiebeheersystemen een soortgelijke webhook ondersteunen, waardoor deze oplossing voor andere systemen ook mogelijk zou moeten zijn.
Tot slot wordt de webhook via de server ingesteld zodat de systeem administrator of de developers dit niet zelf hoeven te doen.
Zie het volgende diagram voor de flow van de merge hook:
flowchart LR
A[Webhook van
versiebeheersysteem] --> B{Is bekend
versiebeheersysteem?}
B -->|Ja| C[Zoek project]
B -->|Nee| D[Einde/Error]
C --> E{Is project gekoppeld
aan intern project?}
E -->|Ja| F[Update branch]
E -->|Nee| G[Einde]
E --> H{Is CDN gekoppeld
aan project?}
H -->|Ja| I[Leeg cache en
verwijder vertalingen op CDN]
H -->|Nee| J[Einde]
4.5 Cost Audit
Om de kosten van het genereren van vertalingen in de portal inzichtelijk te maken is er een cost audit toegevoegd. De cost audits houden bij hoeveel karakters tegen welke prijs per karakter, en door wie de vertalingen gedaan zijn. Dit zorgt ervoor dat de klant weet wie verantwoordelijk is voor de kosten en hoeveel er betaald moet worden. De cost audits worden opgeslagen in de database en kunnen opgevraagd worden via de API. Wanneer de pagina voor het overzicht in de volgende use case gebouwd is kunnen de cost audits ook daar getoond worden. De kosten per karakter worden opgeslagen in de database als decimal zodat er geen afrondingsfouten ontstaan.
Waarom het datatype decimal?
Bekijk de algemene technisch keuzes
5. Test cases
Er is gekozen om end to end tests te schrijven voor deze use case, omdat het genereren van de vertalingen een complex proces is. De meest gemakkelijke manier om alle scenario's te testen is door de flow van de use case te testen. Omdat de back-end en front-end in verbinding met elkaar staan aan de hand van de API endpoints, is het belangrijk dat deze verbinding goed werkt. De end to end tests valideren of de flow van de use case werkt, en of de back-end en front-end goed met elkaar communiceren.
5.1 end-to-end tests
- Test 1: Admin wordt naar instellingen pagina gestuurd wanneer project niet verbonden is met versiebeheersysteem repository
- Test 2: Gebruiker wordt naar vertalingen niet geconfigureerd pagina gestuurd wanneer project niet verbonden is met versiebeheersysteem repository
- Test 3: Admin kan de vertalingen in het project bekijken wanneer het project verbonden is met versiebeheersysteem repository
- Test 4: Gebruiker kan de vertalingen in het project bekijken wanneer het project verbonden is met versiebeheersysteem repository
- Test 5: Gebruiker kan niet bij de admin vertalingen pagina komen.
- Test 6: Genereren nieuwe taal in project die nog niet bestaat
- Test 7: Genereren nieuwe taal in project die niet ondersteund wordt
- Test 8: Genereren nieuwe taal in project die al bestaat
- Test 9: Namespaces worden opgehaald wanneer project geconfigureerd is
- Test 10: Kolom in Json tabel wordt niet getoond wanneer deze gefilterd is vanuit het filter menu.
- Test 11: Kolom in ResX tabel wordt niet getoond wanneer deze gefilterd is vanuit het filter menu.
- Test 12: Mail template editor wordt niet getoond wanneer deze gefilterd is vanuit het filter menu.
Bronnen
- Strategy Pattern (n.d.). Geraadpleegd op 16 December 2024, van https://refactoring.guru/design-patterns/strategy
- Adapter Pattern (n.d.). Geraadpleegd op 16 December 2024, van https://refactoring.guru/design-patterns/adapter