diff --git a/.gitignore b/.gitignore
index 4d8e5d72f..90e2dd891 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,6 @@
**/node_modules/
.vscode
*.sqlite3
+django_secret_key
temp/
db/
diff --git a/backend/core/helpers.py b/backend/core/helpers.py
index 4592de23e..fe2b09699 100644
--- a/backend/core/helpers.py
+++ b/backend/core/helpers.py
@@ -1,4 +1,5 @@
from datetime import date, timedelta
+from re import sub
from django.db.models import Count
from django.shortcuts import get_object_or_404
@@ -23,6 +24,11 @@
"transfer": "#91cc75",
}
+def camel_case(s):
+ s = sub(r"(_|-)+", " ", s).title().replace(" ", "")
+
+ return ''.join([s[0].lower(), s[1:]])
+
def security_measure_priority(user: User):
def get_quadrant(security_measure):
@@ -410,6 +416,7 @@ def risk_per_status(user: User):
def security_measure_per_status(user: User):
values = list()
labels = list()
+ local_lables = list()
color_map = {
"--": "#93c5fd",
"planned": "#fdba74",
@@ -432,7 +439,8 @@ def security_measure_per_status(user: User):
v = {"value": count, "itemStyle": {"color": color_map[st[0]]}}
values.append(v)
labels.append(st[1])
- return {"labels": labels, "values": values}
+ local_lables = [camel_case(str(l)) for l in labels]
+ return {"localLables": local_lables,"labels": labels, "values": values}
def security_measure_per_cur_risk(user: User):
@@ -526,7 +534,6 @@ def aggregate_risks_per_field(
values[m["risk"][i][field]]["count"] += count
return values
-
def risks_count_per_level(user: User, risk_assessments: list | None = None):
current_level = list()
residual_level = list()
@@ -535,14 +542,14 @@ def risks_count_per_level(user: User, risk_assessments: list | None = None):
user, "name", risk_assessments=risk_assessments
).items():
current_level.append(
- {"name": r[0], "value": r[1]["count"], "color": r[1]["color"]}
+ {"name": r[0], "value": r[1]["count"], "color": r[1]["color"], "localName": camel_case(r[0])}
)
for r in aggregate_risks_per_field(
user, "name", residual=True, risk_assessments=risk_assessments
).items():
residual_level.append(
- {"name": r[0], "value": r[1]["count"], "color": r[1]["color"]}
+ {"name": r[0], "value": r[1]["count"], "color": r[1]["color"], "localName": camel_case(r[0])}
)
return {"current": current_level, "residual": residual_level}
diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index 5816b0994..faecb3f4e 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -1,5 +1,177 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
- "Home": "Home",
- "Overview": "Overview"
+ "addButton": "Add {model}",
+ "associatedObject": "Associated {model}",
+ "french": "French",
+ "english": "English",
+ "home": "Home",
+ "edit": "Edit",
+ "overview": "Overview",
+ "context": "Context",
+ "governance": "Governance",
+ "risk": "Risk",
+ "compliance": "Compliance",
+ "organization": "Organisation",
+ "extra": "Extra",
+ "analytics": "Analytics",
+ "calendar": "Calendar",
+ "threats": "Threats",
+ "securityFunctions": "Security functions",
+ "securityMeasures": "Security measures",
+ "assets": "Assets",
+ "asset": "Asset",
+ "policies": "Policies",
+ "riskMatrices": "Risk matrices",
+ "riskAssessments": "Risk assessments",
+ "riskScenarios": "Risk scenarios",
+ "riskScenario": "Risk scenario",
+ "riskAcceptances": "Risk acceptances",
+ "riskAcceptance": "Risk acceptance",
+ "complianceAssessments": "Compliance assessments",
+ "complianceAssessment": "Compliance assessment",
+ "evidences": "Evidences",
+ "evidence": "Evidence",
+ "frameworks": "Frameworks",
+ "domains": "Domains",
+ "projects": "Projects",
+ "users": "Users",
+ "user": "User",
+ "userGroups": "User groups",
+ "roleAssignments": "Role assignments",
+ "xRays": "X-rays",
+ "scoringAssistant": "Scoring assistant",
+ "libraries": "Libraries",
+ "backupRestore": "Backup & restore",
+ "myProfile": "My profile",
+ "aboutCiso": "About CISO Assistant",
+ "Logout": "Log out",
+ "name": "Name",
+ "description": "Description",
+ "parentDomain": "Parent domain",
+ "ref": "Ref",
+ "refId": "Ref ID",
+ "businessValue": "Business value",
+ "email": "Email",
+ "firstName": "First name",
+ "lastName": "Last name",
+ "category": "Category",
+ "eta": "ETA",
+ "securityFunction": "Security function",
+ "securityMeasure": "Security measure",
+ "provider": "Provider",
+ "domain": "Domain",
+ "urn": "URN",
+ "id": "ID",
+ "treatmentStatus": "Treatment status",
+ "currentLevel": "Current level",
+ "residualLevel": "Residual level",
+ "riskMatrix": "Risk matrix",
+ "project": "Project",
+ "folder": "Folder",
+ "riskAssessment": "Risk assessment",
+ "threat": "Threat",
+ "framework": "Framework",
+ "file": "File",
+ "language": "Language",
+ "builtin": "Builtin",
+ "next": "Next",
+ "previous": "Previous",
+ "show": "Show",
+ "entries": "entries",
+ "searchPlaceholder": "Search...",
+ "noEntriesFound": "No entries found",
+ "rowCount": "Showing {start} to {end} of {total}",
+ "status": "Status",
+ "effort": "Effort",
+ "impact": "Impact",
+ "expiryDate": "Expiry date",
+ "link": "Link",
+ "createdAt": "Created at",
+ "updatedAt": "Updated at",
+ "acceptedAt": "Accepted at",
+ "rejectedAt": "Rejected at",
+ "revokedAt": "Revoked at",
+ "locale": "Locale",
+ "defaultLocale": "Default locale",
+ "annotation": "Annotation",
+ "library": "Library",
+ "typicalEvidence": "Typical evidence",
+ "parentAsset": "Parent asset",
+ "parentAssets": "Parent assets",
+ "approver": "Approver",
+ "state": "State",
+ "justification": "Justification",
+ "parentFolder": "Parent folder",
+ "contentType": "Content type",
+ "lcStatus": "Status",
+ "internalReference": "Internal reference",
+ "isActive": "Is active",
+ "dateJoined": "Date joined",
+ "version": "Version",
+ "treatment": "Treatment",
+ "currentProba": "Current probability",
+ "currentImpact": "Current impact",
+ "residualProba": "Residual probability",
+ "residualImpact": "Residual impact",
+ "existingMeasures": "Existing measures",
+ "strengthOfKnowledge": "Strength of knowledge",
+ "dueDate": "Due date",
+ "attachment": "Attachment",
+ "observation": "Observation",
+ "importMatrices": "Import matrices",
+ "importFrameworks": "Import frameworks",
+ "summary": "Summary",
+ "composer": "Composer",
+ "statistics": "Statistics",
+ "myProjects": "My projects",
+ "scenarios": "Scenarios",
+ "assignedObjects": "Assigned to {number} {object}",
+ "currentRiskLevelPerScenario": "Current risk level per risk scenario",
+ "residualRiskLevelPerScenario": "Residual risk level per risk scenario",
+ "securityMeasuresStatus": "Security measures status",
+ "currentRisk": "Current risk",
+ "residualRisk": "Residual risk",
+ "veryLow": "Very low",
+ "low": "Low",
+ "medium": "Medium",
+ "high": "High",
+ "veryHigh": "Very high",
+ "planned": "Planned",
+ "active": "Active",
+ "inactive": "Inactive",
+ "watchlist": "Watch list",
+ "watchlistDescription": "Items that have expired or with close ETA",
+ "measuresToReview": "Measures to review",
+ "exceptionsToReview": "Exceptions to review",
+ "expired": "Expired",
+ "upcoming": "Upcoming",
+ "today": "Today",
+ "actionRequested": "Action requested",
+ "noObjectYet": "No {object} yet",
+ "authors": "Authors",
+ "reviewers": "Reviewers",
+ "process": "Process",
+ "selectTargets": "Select your targets",
+ "composerDescription": "This will help you aggregate multiple components (projects) to get the compiled view on your risk. This is particularly useful for two use cases",
+ "composerDescription1": "business intelligence approach to focus on a specific subset across different project domains (eg. across divisions)",
+ "composerDescription2": "you are interested in the risk assessment of a specific system, for which you need the risk assessment of the underlying components",
+ "overallCompliance": "Overall compliance",
+ "exportButton": "Export",
+ "treatmentProgressOverview": "Treatment progress overview",
+ "pendingMeasures": "Your pending measures",
+ "orderdByRankingScore": "Ordered by ranking score",
+ "rankingScore": "Ranking score",
+ "noPendingObject": "No pending {object}",
+ "rankingScoreDefintion": "Ranking score is an adaptive metric that combines the information of effort and current risk level, and crosses it with the other data to assist you for the prioritization",
+ "actions": "Actions",
+ "projectsSummaryEmpty": "Projects summary is empty",
+ "riskOpen": "Risk: open",
+ "riskMitigate": "Risk: mitigate",
+ "riskAccept": "Risk: accept",
+ "riskAvoid": "Risk: avoid",
+ "measureOpen": "Measure: open",
+ "measureProgress": "Measure: in progress",
+ "measureHold": "Measure: on hold",
+ "measureDone": "Measure: done"
+
}
diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json
index 4d4a17a3f..124e7f29a 100644
--- a/frontend/messages/fr.json
+++ b/frontend/messages/fr.json
@@ -1,5 +1,177 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
- "Home": "Accueil",
- "Overview": "Vue d'ensemble"
+ "addButton": "Ajouter {determinant} {model}",
+ "associatedObject": "{model} associé{e}s",
+ "french": "Français",
+ "english": "Anglais",
+ "home": "Accueil",
+ "edit": "Modifier",
+ "overview": "Vue d'ensemble",
+ "context": "Contexte",
+ "governance": "Gouvernance",
+ "risk": "Risque",
+ "compliance": "Conformité",
+ "organization": "Organisation",
+ "extra": "Extra",
+ "analytics": "Analytiques",
+ "calendar": "Calendrier",
+ "threats": "Menaces",
+ "securityFunctions": "Fonctions de sécurité",
+ "securityMeasures": "Mesures de sécurité",
+ "assets": "Biens sensibles",
+ "asset": "Bien sensible",
+ "policies": "Politiques",
+ "riskMatrices": "Matrices de risque",
+ "riskAssessments": "Évaluations de risque",
+ "riskScenarios": "Scénarios de risque",
+ "riskScenario": "Scénario de risque",
+ "riskAcceptances": "Acceptations de risque",
+ "riskAcceptance": "Acceptation de risque",
+ "complianceAssessments": "Évaluations de conformité",
+ "complianceAssessment": "Évaluation de conformité",
+ "evidences": "Preuves",
+ "evidence": "Preuve",
+ "frameworks": "Cadres",
+ "domains": "Domaines",
+ "projects": "Projets",
+ "users": "Utilisateurs",
+ "user": "Utilisateur",
+ "userGroups": "Groupes d'utilisateurs",
+ "roleAssignments": "Affectations de rôle",
+ "xRays": "X-rays",
+ "scoringAssistant": "Assistat d'évaluation",
+ "libraries": "Bibliothèques",
+ "backupRestore": "Sauvegarde et restauration",
+ "myProfile": "Mon profil",
+ "aboutCiso": "À propos de CISO Assistant",
+ "Logout": "Se déconnecter",
+ "name": "Nom",
+ "description": "Description",
+ "parentDomain": "Domaine parent",
+ "ref": "Réf",
+ "refId": "ID de référence",
+ "businessValue": "Valeur commerciale",
+ "email": "E-mail",
+ "firstName": "Prénom",
+ "lastName": "Nom de famille",
+ "category": "Catégorie",
+ "eta": "ETA",
+ "securityFunction": "Fonction de sécurité",
+ "securityMeasure": "Mesure de sécurité",
+ "provider": "Fournisseur",
+ "domain": "Domaine",
+ "urn": "URN",
+ "id": "ID",
+ "treatmentStatus": "Statut de traitement",
+ "currentLevel": "Niveau actuel",
+ "residualLevel": "Niveau résiduel",
+ "riskMatrix": "Matrice de risque",
+ "project": "Projet",
+ "folder": "Domaine",
+ "riskAssessment": "Évaluation de risque",
+ "threat": "Menace",
+ "framework": "Cadre",
+ "file": "Fichier",
+ "language": "Langue",
+ "builtin": "Intégré",
+ "next": "Suivant",
+ "previous": "Précédent",
+ "show": "Afficher",
+ "entries": "entrées",
+ "searchPlaceholder": "Rechercher...",
+ "noEntriesFound": "Aucune entrée trouvée",
+ "rowCount": "Affichage de {start} à {end} sur {total}",
+ "status": "Statut",
+ "effort": "Effort",
+ "impact": "Impact",
+ "expiryDate": "Date d'expiration",
+ "link": "Lien",
+ "createdAt": "Créé le",
+ "updatedAt": "Mis à jour le",
+ "acceptedAt": "Accepté le",
+ "rejectedAt": "Rejeté le",
+ "revokedAt": "Révoqué le",
+ "locale": "Locale",
+ "defaultLocale": "Locale par défaut",
+ "annotation": "Annotation",
+ "library": "Bibliothèque",
+ "typicalEvidence": "Preuve typique",
+ "parentAsset": "Bien sensible parent",
+ "parentAssets": "Biens sensibles parents",
+ "approver": "Approbateur",
+ "state": "État",
+ "justification": "Justification",
+ "parentFolder": "Domaine parent",
+ "contentType": "Type de contenu",
+ "lcStatus": "Statut",
+ "internalReference": "Référence interne",
+ "isActive": "Est actif",
+ "dateJoined": "Date d'adhésion",
+ "version": "Version",
+ "treatment": "Traitement",
+ "currentProba": "Probabilité actuelle",
+ "currentImpact": "Impact actuel",
+ "residualProba": "Probabilité résiduelle",
+ "residualImpact": "Impact résiduel",
+ "existingMeasures": "Mesures existantes",
+ "strengthOfKnowledge": "Expertise",
+ "dueDate": "Date d'échéance",
+ "attachment": "Pièce jointe",
+ "observation": "Observation",
+ "importMatrices": "Importer des matrices",
+ "importFrameworks": "Importer des cadres",
+ "summary": "Synthèse",
+ "composer": "Compositeur",
+ "statistics": "Statistiques",
+ "myProjects": "Mes projets",
+ "scenarios": "Scénarios",
+ "assignedObjects": "Assigné à {number} {object}",
+ "currentRiskLevelPerScenario": "Niveau de risque courant par scénario de risque",
+ "residualRiskLevelPerScenario": "Niveau de risque résiduel par scénario de risque",
+ "securityMeasuresStatus": "Statut des mesures de sécurité",
+ "currentRisk": "Risque courant",
+ "residualRisk": "Risque résiduel",
+ "veryLow": "Très faible",
+ "low": "Faible",
+ "medium": "Moyen",
+ "high": "Élevé",
+ "veryHigh": "Très élevé",
+ "planned": "Planifié",
+ "active": "Active",
+ "inactive": "Inactive",
+ "watchlist": "Liste de surveillance",
+ "watchlistDescription": "Objets expirés ou ayant une ETA proche",
+ "measuresToReview": "Mesures à revoir",
+ "exceptionsToReview": "Exceptions à revoir",
+ "expired": "Expiré",
+ "upcoming": "À venir",
+ "today": "Aujourd'hui",
+ "actionRequested": "Action requise",
+ "noObjectYet": "Aucun{e} {object} pour le moment",
+ "authors": "Auteurs",
+ "reviewers": "Relecteurs",
+ "process": "Traiter",
+ "selectTargets": "Selectionnez vos cibles",
+ "composerDescription": "Cela vous aidera à agréger plusieurs composants (projets) pour obtenir une vue d'ensemble de vos risques. Ceci est particulièrement utile pour deux cas d'utilisation",
+ "composerDescription1": "une approche de veille stratégique pour se concentrer sur un sous-ensemble spécifique à travers différents domaines de projet (par exemple, à travers les divisions)",
+ "composerDescription2": "vous êtes intéressé par l'évaluation des risques d'un système spécifique, pour lequel vous avez besoin de l'évaluation des risques des composants sous-jacents",
+ "overallCompliance": "Conformité globale",
+ "exportButton": "Exporter",
+ "treatmentProgressOverview": "Vue d'ensemble de l'avancement du traitement",
+ "pendingMeasures": "Vos mesures en attente",
+ "orderdByRankingScore": "Classées par score",
+ "rankingScore": "Score de classement",
+ "noPendingObject": "Aucun{e} {objet} en attente",
+ "rankingScoreDefintion": "Le score de classement est une mesure adaptative qui combine les informations relatives à l'effort et au niveau de risque actuel, et les croise avec d'autres données pour vous aider à établir des priorités",
+ "actions": "Actions",
+ "projectsSummaryEmpty": "Le résumé des projets est vide",
+ "riskOpen": "Risque: ouvert",
+ "riskMitigate": "Risque: atténué",
+ "riskAccept": "Risque: accepté",
+ "riskAvoid": "Risque: évité",
+ "measureOpen": "Mesure: ouverte",
+ "measureProgress": "Mesure: en cours",
+ "measureHold": "Mesure: en attente",
+ "measureDone": "Mesure: terminée"
}
+
diff --git a/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte b/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
index c6bcd2690..6c72faf1d 100644
--- a/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
+++ b/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
@@ -1,11 +1,24 @@
@@ -41,7 +54,11 @@
{#if c.icon}
{/if}
- {c.label}
+ {#if localItems(languageTag())[c.label]}
+ {localItems(languageTag())[c.label]}
+ {:else}
+ {c.label}
+ {/if}
{:else}
@@ -53,14 +70,22 @@
{#if c.icon}
{/if}
- {c.label}
+ {#if localItems(languageTag())[c.label]}
+ {localItems(languageTag())[c.label]}
+ {:else}
+ {c.label}
+ {/if}
{:else}
{#if c.icon}
{/if}
- {c.label}
+ {#if localItems(languageTag())[c.label]}
+ {localItems(languageTag())[c.label]}
+ {:else}
+ {c.label}
+ {/if}
{/if}
diff --git a/frontend/src/lib/components/Chart/DonutChart.svelte b/frontend/src/lib/components/Chart/DonutChart.svelte
index b479d867e..684edde32 100644
--- a/frontend/src/lib/components/Chart/DonutChart.svelte
+++ b/frontend/src/lib/components/Chart/DonutChart.svelte
@@ -1,11 +1,17 @@
@@ -145,7 +181,7 @@
{#each Object.entries(source.head) as [key, heading]}
- {heading}
+ {headTranslate[heading]}
{/each}
{#if displayActions}
diff --git a/frontend/src/lib/components/ModelTable/Pagination.svelte b/frontend/src/lib/components/ModelTable/Pagination.svelte
index 0f9272a7a..6151c070e 100644
--- a/frontend/src/lib/components/ModelTable/Pagination.svelte
+++ b/frontend/src/lib/components/ModelTable/Pagination.svelte
@@ -1,5 +1,6 @@
{#if $rowCount.total > 0}
- Showing {$rowCount.start}
- to {$rowCount.end}
- of {$rowCount.total}
+ {m.rowCount({start: $rowCount.start, end: $rowCount.end, total: $rowCount.total})}
{:else}
- No entries found
+ {m.noEntriesFound()}
{/if}
diff --git a/frontend/src/lib/components/ModelTable/RowsPerPage.svelte b/frontend/src/lib/components/ModelTable/RowsPerPage.svelte
index 060388b05..8378067ef 100644
--- a/frontend/src/lib/components/ModelTable/RowsPerPage.svelte
+++ b/frontend/src/lib/components/ModelTable/RowsPerPage.svelte
@@ -1,12 +1,13 @@
- Show
+ {m.show()}
{#each options as option}
@@ -14,5 +15,5 @@
{/each}
- entries
+ {m.entries()}
diff --git a/frontend/src/lib/components/ModelTable/Search.svelte b/frontend/src/lib/components/ModelTable/Search.svelte
index 9744f686a..98caf7ee0 100644
--- a/frontend/src/lib/components/ModelTable/Search.svelte
+++ b/frontend/src/lib/components/ModelTable/Search.svelte
@@ -1,13 +1,14 @@
handler.search(value)}
/>
diff --git a/frontend/src/lib/components/SideBar/SideBarCategory.svelte b/frontend/src/lib/components/SideBar/SideBarCategory.svelte
index 24c5a2ceb..f0db86274 100644
--- a/frontend/src/lib/components/SideBar/SideBarCategory.svelte
+++ b/frontend/src/lib/components/SideBar/SideBarCategory.svelte
@@ -1,7 +1,9 @@
-{item.name}
+
+ {localItems(languageTag())[item.name]}
+
diff --git a/frontend/src/lib/components/SideBar/SideBarFooter.svelte b/frontend/src/lib/components/SideBar/SideBarFooter.svelte
index c65d48b56..3f32995d0 100644
--- a/frontend/src/lib/components/SideBar/SideBarFooter.svelte
+++ b/frontend/src/lib/components/SideBar/SideBarFooter.svelte
@@ -7,6 +7,11 @@
import { LOCALE_MAP } from '$lib/utils/locales';
import * as m from '$paraglide/messages';
+ const language: any = {
+ french: m.french(),
+ english: m.english()
+ }
+
const modalStore = getModalStore();
let value = languageTag();
@@ -63,7 +68,7 @@
My profile {m.myProfile()}
{#each availableLanguageTags as lang}
{LOCALE_MAP[lang].flag} {LOCALE_MAP[lang].name} {LOCALE_MAP[lang].flag} {language[LOCALE_MAP[lang].name]}
{/each}
@@ -80,13 +85,13 @@
on:click={modalBuildInfo}
class="cursor-pointer flex items-center gap-2 w-full px-4 py-2.5 text-left text-sm hover:bg-gray-100 disabled:text-gray-500 text-gray-800"
data-testid="about-button"
- > About CISO Assistant {m.aboutCiso()}
diff --git a/frontend/src/lib/components/SideBar/SideBarItem.svelte b/frontend/src/lib/components/SideBar/SideBarItem.svelte
index 760167962..fb3bfd3d5 100644
--- a/frontend/src/lib/components/SideBar/SideBarItem.svelte
+++ b/frontend/src/lib/components/SideBar/SideBarItem.svelte
@@ -1,6 +1,7 @@
@@ -22,7 +50,13 @@
- {$pageTitle}
+
+ {#if items[$pageTitle]}
+ {items[$pageTitle]}
+ {:else}
+ {$pageTitle}
+ {/if}
+
diff --git a/frontend/src/routes/(app)/[model=urlmodel]/+page.svelte b/frontend/src/routes/(app)/[model=urlmodel]/+page.svelte
index d84b8db92..de77be9c7 100644
--- a/frontend/src/routes/(app)/[model=urlmodel]/+page.svelte
+++ b/frontend/src/routes/(app)/[model=urlmodel]/+page.svelte
@@ -11,6 +11,9 @@
import { getModalStore, getToastStore } from '@skeletonlabs/skeleton';
import { superForm } from 'sveltekit-superforms/client';
import type { PageData } from './$types';
+ import * as m from '$paraglide/messages';
+ import { localItems, getDeterminant } from '$lib/utils/locales';
+ import { languageTag } from '$paraglide/runtime';
export let data: PageData;
@@ -91,15 +94,17 @@
class="btn variant-filled-primary self-end"
data-testid="add-button"
on:click={modalCreateForm}
- > New {data.model.verboseName.toLowerCase()}
+ {m.addButton({determinant:getDeterminant(languageTag(), "undefined", data.model), model: localItems(languageTag())[data.model.localName].toLowerCase()})}
+
{:else if data.URLModel === 'risk-matrices'}
Import matrices {m.importMatrices()}
{:else if data.URLModel === 'frameworks'}
Import framework {m.importFrameworks()}
{/if}
diff --git a/frontend/src/routes/(app)/[model=urlmodel]/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/[model=urlmodel]/[id=uuid]/+page.svelte
index 332c08cb3..da964e510 100644
--- a/frontend/src/routes/(app)/[model=urlmodel]/[id=uuid]/+page.svelte
+++ b/frontend/src/routes/(app)/[model=urlmodel]/[id=uuid]/+page.svelte
@@ -15,6 +15,9 @@
import { getModelInfo } from '$lib/utils/crud.js';
import { URL_MODEL_MAP } from '$lib/utils/crud';
import { isURL } from '$lib/utils/helpers';
+ import { localItems, toCamelCase, getDeterminant } from '$lib/utils/locales.js';
+ import { languageTag } from '$paraglide/runtime.js';
+ import * as m from '$paraglide/messages.js';
const modalStore: ModalStore = getModalStore();
const toastStore: ToastStore = getToastStore();
@@ -172,30 +175,33 @@
{#each Object.entries(data.data).filter(([key, _]) => !['id', 'is_published'].includes(key)) as [key, value]}
-
- {key.replace('_', ' ')}
+
+ {localItems(languageTag())[toCamelCase(key.toLowerCase())]}
{#if value}
{#if Array.isArray(value)}
-
- {#each value as val}
-
- {#if val.str && val.id}
- {@const itemHref = `/${
- URL_MODEL_MAP[data.urlModel]['foreignKeyFields']?.find(
- (item) => item.field === key
- )?.urlModel
- }/${val.id}`}
- {val.str}
- {:else}
- {value}
- {/if}
-
- {/each}
-
+ {#if Object.keys(value).length > 0}
+
+ {#each value as val}
+
+ {#if val.str && val.id}
+ {@const itemHref = `/${
+ URL_MODEL_MAP[data.urlModel]['foreignKeyFields']?.find(
+ (item) => item.field === key
+ )?.urlModel
+ }/${val.id}`}
+ {val.str}
+ {:else}
+ {value}
+ {/if}
+
+ {/each}
+
+ {:else}
+ --
+ {/if}
{:else if value.id}
{@const itemHref = `/${
URL_MODEL_MAP[data.urlModel]['foreignKeyFields']?.find(
@@ -220,8 +226,7 @@
Edit {m.edit()}
{/if}
@@ -232,7 +237,7 @@
{#each Object.entries(data.relatedModels) as [urlmodel, model], index}
- {model.info.verboseNamePlural}
+ {localItems(languageTag())[model.info.localNamePlural]}
{#if model.table.body.length > 0}
{model.table.body.length}
{/if}
@@ -243,12 +248,16 @@
{#if tabSet === index}
- Associated {model.info.verboseNamePlural}
+ {#if model.info.localFrGender === 'f'}
+ {m.associatedObject({model: localItems(languageTag())[model.info.localNamePlural].toLowerCase(), e: 'e'})}
+ {:else}
+ {m.associatedObject({model: localItems(languageTag())[model.info.localNamePlural].toLowerCase(), e: ''})}
+ {/if}
modalCreateForm(model)}
- > New {model.info.verboseName} {m.addButton({determinant:getDeterminant(languageTag(), "undefined", model.info), model: localItems(languageTag())[model.info.localName].toLowerCase()})}
{#if model.table}
diff --git a/frontend/src/routes/(app)/analytics/+page.svelte b/frontend/src/routes/(app)/analytics/+page.svelte
index f8881cf56..827f2747e 100644
--- a/frontend/src/routes/(app)/analytics/+page.svelte
+++ b/frontend/src/routes/(app)/analytics/+page.svelte
@@ -11,6 +11,10 @@
import { RISK_COLOR_PALETTE } from '$lib/utils/constants';
+ import * as m from '$paraglide/messages';
+ import { localItems } from '$lib/utils/locales.js';
+ import { languageTag } from '$paraglide/runtime';
+
export let data;
let user: User = data.user;
@@ -29,11 +33,15 @@
let openTab = 1;
- const cur_rsk_label = 'Current risk';
- const rsd_rsk_label = 'Residual risk';
+ const cur_rsk_label = m.currentRisk();
+ const rsd_rsk_label = m.residualRisk();
let dropdown_selected_values: any;
+ for (const item in security_measure_status.labels) {
+ security_measure_status.labels[item] = localItems(languageTag())[security_measure_status.localLables[item]];
+ }
+
onMount(async () => {
const echarts = await import('echarts');
let echart_element = document.getElementById('security_measures_status_div');
@@ -179,6 +187,10 @@
let dropdown = new Dropdown();
+
+ {m.analytics()}
+
+
{
if (openTab === 3 && dropdown.show) {
@@ -199,7 +211,7 @@
- Summary
+ {m.summary()}
- Compliance
+ {m.compliance()}
- Treatment
+ {m.treatment()}
- Composer
+ {m.composer()}
-
+
{counters.RiskAssessment}
-
Risk assessments
+
{m.riskAssessments()}
{counters.RiskScenario}
-
Scenarios
+
{m.scenarios()}
{counters.SecurityMeasure}
-
Security measures
+
{m.securityMeasures()}
{counters.RiskAcceptance}
-
Risk acceptances
+
{m.riskAcceptances()}
@@ -278,14 +290,18 @@
-
+
- Assigned to {counters.Project} projects
+ {#if counters.Project > 1}
+ {m.assignedObjects({number: counters.Project, object: m.projects()})}
+ {:else}
+ {m.assignedObjects({number: counters.Project, object: m.project()})}
+ {/if}
- Current risk level per risk scenario
+ {m.currentRiskLevelPerScenario()}
- Residual risk level per risk scenario
+ {m.residualRiskLevelPerScenario()}
-
Security measures status
+
{m.securityMeasuresStatus()}
@@ -316,19 +332,19 @@
-
+
- Items that have expired or with close ETA
+ {m.watchlistDescription()}
- Measures to review
+ {m.measuresToReview()}
- Exceptions to review
+ {m.exceptionsToReview()}
@@ -338,23 +354,20 @@
-
Composer
+
{m.composer()}
{#each risk_assessments as risk_assessment}
{risk_assessment.name}
{/each}
- This will help you aggregate multiple components (projects) to get the compiled view on
- your risk. This is particularly useful for two use cases:
+ {m.composerDescription()}:
- business intelligence approach to focus on a specific subset across different project
- domains (eg. across divisions)
+ {m.composerDescription1()}
- you are interested in the risk assessment of a specific system, for which you need the
- risk assessment of the underlying components
+ {m.composerDescription2()}
@@ -372,7 +385,7 @@
Process {m.process()}
@@ -491,24 +504,24 @@ c0.27-0.268,0.707-0.268,0.979,0l7.908,7.83c0.27,0.268,0.27,0.701,0,0.969c-0.271,
{#if agg_data.names.length}
-
Treatment progress overview
+
{m.treatmentProgressOverview()}
-
Your pending measures:
-
ordered by ranking score
+
{m.pendingMeasures()}
+
{m.orderdByRankingScore()}
- Domain
- Measure
- Ranking score
- Status
- ETA
- Actions
+ {m.domain()}
+ {m.securityMeasure()}
+ {m.rankingScore()}
+ {m.status()}
+ {m.eta()}
+ {m.actions()}
{#if measures}
@@ -542,21 +555,19 @@ c0.27-0.268,0.707-0.268,0.979,0l7.908,7.83c0.27,0.268,0.27,0.701,0,0.969c-0.271,
- No pending measure.
+ {m.noPendingObject({object:m.securityMeasure().toLowerCase(), e:'e'})}.
{/if}
- Ranking score is an adaptive metric that combines the
- information of effort and current risk level, and crosses it with the other data to assist
- you for the prioritization.
+ {m.rankingScoreDefintion()}.
{:else}
-
Projects summary is empty.
+
{m.projectsSummaryEmpty()}.
{/if}
@@ -567,7 +578,7 @@ c0.27-0.268,0.707-0.268,0.979,0l7.908,7.83c0.27,0.268,0.27,0.701,0,0.969c-0.271,
{#if openTab === 2}
-
+
{#each data.projects as project}
@@ -591,18 +602,18 @@ c0.27-0.268,0.707-0.268,0.979,0l7.908,7.83c0.27,0.268,0.27,0.701,0,0.969c-0.271,
-
Name
+
{m.name()}
{compliance_assessment.name}
-
Framework
+
{m.framework()}
{compliance_assessment.framework.str}
@@ -610,12 +621,12 @@ c0.27-0.268,0.707-0.268,0.979,0l7.908,7.83c0.27,0.268,0.27,0.701,0,0.969c-0.271,
Export
+ > {m.exportButton()}
Edit
+ > {m.edit()}