Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -112,7 +112,7 @@ grün, en/de gepflegt, Build grün, Browser-Check ok).
| S8 | `reports` | ✅ | ReportsPage localized (new `reports` ns); burndown + velocity section titles/empty-states + recharts legend series labels via `name` |
| S9 | `notifications` | ✅ | NotificationBell (aria) + NotificationsPage localized (new `notifications` ns); scanner-blind `typeLabel` switch → keyed `t()`; hand-rolled `formatTime` → `formatRelativeTime` (relative-time rollout) |
| S10 | `projects` | ✅ | ProjectCreatePage localized (new `projects` ns); placeholders routed through `t()`; ProjectListPage already string-free (not allowlisted) |
| S11 | `project-settings` | | MembersPage, LabelsPage, VersionsPage, CustomFieldsPage, ProjectAuditPage, OrganizationSettingsPage, SettingsLayout (Rest) |
| S11 | `project-settings` | | All 7 files localized (new `project-settings` ns); scanner-blind ROLE_LABELS/FIELD_TYPES/audit column headers/settings-nav labels localized at render site; members org banner via `<Trans>`; audit timestamp via `formatDateTime`; CustomFields `t`-shadow (`ft`) handled |
| S12 | `workflow` | ⬜ | WorkflowEditorPage, WorkflowCanvas, StatusNode, TransitionArrow, TransitionGuardPanel |
| S13 | `automation` | ⬜ | AutomationPage, AutomationRuleEditorPage, AdminAutomationPage, RuleEditor, ActionList, ActionRow, ConditionGroupBuilder, ConditionRow, TriggerSelector |
| S14 | `admin` | ⬜ | AdminUsersPage, AuditLogPage, SsoSettingsPage |
Expand Down
7 changes: 0 additions & 7 deletions frontend/scripts/i18n-allowlist.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,6 @@
"src/components/automation/TriggerSelector.tsx",
"src/components/workflow/TransitionGuardPanel.tsx",
"src/layouts/AuthLayout.tsx",
"src/layouts/SettingsLayout.tsx",
"src/pages/admin/AdminAutomationPage.tsx",
"src/pages/admin/AdminUsersPage.tsx",
"src/pages/admin/AuditLogPage.tsx",
Expand All@@ -19,12 +18,6 @@
"src/pages/orgs/OrgsPage.tsx",
"src/pages/projects/servicedesk/IncidentDashboardPage.tsx",
"src/pages/projects/servicedesk/ServiceDeskPage.tsx",
"src/pages/projects/settings/CustomFieldsPage.tsx",
"src/pages/projects/settings/LabelsPage.tsx",
"src/pages/projects/settings/MembersPage.tsx",
"src/pages/projects/settings/OrganizationSettingsPage.tsx",
"src/pages/projects/settings/ProjectAuditPage.tsx",
"src/pages/projects/settings/VersionsPage.tsx",
"src/pages/settings/AccessTokensPage.tsx",
"src/pages/settings/ApiKeysPage.tsx",
"src/pages/settings/IntegrationsPage.tsx",
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/i18n/index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,8 @@ import enNotifications from './locales/en/notifications.json'
import deNotifications from './locales/de/notifications.json'
import enProjects from './locales/en/projects.json'
import deProjects from './locales/de/projects.json'
import enProjectSettings from './locales/en/project-settings.json'
import deProjectSettings from './locales/de/project-settings.json'

export const SUPPORTED_LANGUAGES = ['en', 'de'] as const
export type AppLanguage = (typeof SUPPORTED_LANGUAGES)[number]
Expand All@@ -39,14 +41,14 @@ i18n
.use(initReactI18next)
.init({
resources: {
en: { common: enCommon, settings: enSettings, nav: enNav, auth: enAuth, issues: enIssues, 'issues-fields': enIssuesFields, comments: enComments, board: enBoard, backlog: enBacklog, sprints: enSprints, dashboard: enDashboard, reports: enReports, notifications: enNotifications, projects: enProjects },
de: { common: deCommon, settings: deSettings, nav: deNav, auth: deAuth, issues: deIssues, 'issues-fields': deIssuesFields, comments: deComments, board: deBoard, backlog: deBacklog, sprints: deSprints, dashboard: deDashboard, reports: deReports, notifications: deNotifications, projects: deProjects },
en: { common: enCommon, settings: enSettings, nav: enNav, auth: enAuth, issues: enIssues, 'issues-fields': enIssuesFields, comments: enComments, board: enBoard, backlog: enBacklog, sprints: enSprints, dashboard: enDashboard, reports: enReports, notifications: enNotifications, projects: enProjects, 'project-settings': enProjectSettings },
de: { common: deCommon, settings: deSettings, nav: deNav, auth: deAuth, issues: deIssues, 'issues-fields': deIssuesFields, comments: deComments, board: deBoard, backlog: deBacklog, sprints: deSprints, dashboard: deDashboard, reports: deReports, notifications: deNotifications, projects: deProjects, 'project-settings': deProjectSettings },
},
fallbackLng: 'en',
supportedLngs: [...SUPPORTED_LANGUAGES],
nonExplicitSupportedLngs: true,
defaultNS: 'common',
ns: ['common', 'settings', 'nav', 'auth', 'issues', 'issues-fields', 'comments', 'board', 'backlog', 'sprints', 'dashboard', 'reports', 'notifications', 'projects'],
ns: ['common', 'settings', 'nav', 'auth', 'issues', 'issues-fields', 'comments', 'board', 'backlog', 'sprints', 'dashboard', 'reports', 'notifications', 'projects', 'project-settings'],
interpolation: { escapeValue: false },
detection: {
order: ['localStorage', 'navigator'],
Expand Down
99 changes: 99 additions & 0 deletions frontend/src/i18n/locales/de/project-settings.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
{
"form": {
"name": "Name",
"create": "Erstellen",
"edit": "Bearbeiten",
"delete": "Löschen",
"nameRequired": "Name ist erforderlich",
"maxChars": "Maximal 50 Zeichen"
},
"members": {
"title": "Mitglieder",
"noPermission": "Du hast keine Berechtigung, die Mitglieder dieses Projekts zu verwalten.",
"orgBanner": "Dieses Projekt gehört zu <b>{{org}}</b>. Deren Eigentümer und Admins sind hier Admins; deren Mitglieder sind Betrachter. Diese Personen haben Zugriff, ohne in der Liste unten aufzutauchen.",
"orgFallback": "einer Organisation",
"add": "Mitglied hinzufügen",
"searchPlaceholder": "Nach Name oder E-Mail suchen…",
"addButton": "Hinzufügen",
"remove": "Entfernen",
"owner": "Eigentümer",
"you": "Du",
"removeConfirm": "{{name}} aus diesem Projekt entfernen?",
"alreadyMember": "Dieser Benutzer ist bereits Mitglied.",
"addFailed": "Mitglied konnte nicht hinzugefügt werden.",
"role": {
"VIEWER": "Nur Lesen",
"MEMBER": "Lesen & Schreiben",
"ADMIN": "Admin"
}
},
"labels": {
"title": "Labels",
"new": "Neues Label",
"namePlaceholder": "Label-Name",
"color": "Farbe",
"preview": "Vorschau",
"previewName": "Vorschau",
"duplicate": "Ein Label mit diesem Namen existiert bereits.",
"deleteConfirm": "Dieses Label löschen? Es wird von allen Vorgängen entfernt.",
"empty": "Noch keine Labels. Erstelle dein erstes!"
},
"versions": {
"title": "Versionen",
"new": "Neue Version",
"namePlaceholder": "z. B. v1.0.0",
"duplicate": "Eine Version mit diesem Namen existiert bereits.",
"deleteConfirm": "Diese Version löschen? Sie wird von allen Vorgängen entfernt.",
"empty": "Noch keine Versionen. Erstelle deine erste!"
},
"fields": {
"title": "Benutzerdefinierte Felder",
"new": "Neues Feld",
"namePlaceholder": "Feldname",
"requiredLabel": "Pflichtfeld",
"requiredBadge": "Pflicht",
"duplicate": "Ein Feld mit diesem Namen existiert bereits.",
"deleteConfirm": "Dieses Feld löschen? Alle Werte gehen verloren.",
"empty": "Noch keine benutzerdefinierten Felder.",
"showOptions": "Optionen",
"hideOptions": "Optionen ausblenden",
"optionPlaceholder": "Neue Options-Bezeichnung",
"add": "Hinzufügen",
"type": {
"TEXT": "Text",
"NUMBER": "Zahl",
"DATE": "Datum",
"DROPDOWN": "Auswahlliste",
"CHECKBOX": "Kontrollkästchen"
}
},
"audit": {
"title": "Audit-Protokoll",
"filterPlaceholder": "Aktion filtern…",
"empty": "Keine Audit-Ereignisse",
"col": {
"time": "Zeit",
"user": "Benutzer",
"action": "Aktion",
"level": "Stufe",
"resource": "Ressource"
},
"level": {
"SECURITY": "Sicherheit",
"WRITE": "Schreiben",
"ALL": "Alle"
}
},
"org": {
"title": "Organisation",
"noPermission": "Du hast keine Berechtigung, die Organisation dieses Projekts zu verwalten.",
"current": "Aktuelle Organisation",
"notAssigned": "Nicht zugewiesen",
"assignLabel": "Einer Organisation zuweisen",
"selectPlaceholder": "Organisation auswählen…",
"assign": "Zuweisen",
"removeFromOrg": "Aus Organisation entfernen",
"forbidden": "Du musst Eigentümer oder Admin der Zielorganisation sein, um dieses Projekt zuzuweisen.",
"updateFailed": "Organisation konnte nicht aktualisiert werden."
}
}
3 changes: 3 additions & 0 deletions frontend/src/i18n/locales/de/settings.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,9 @@
"changing": "Wird geändert…",
"changePassword": "Passwort ändern"
},
"tokens": {
"title": "Zugriffstokens"
},
"account": {
"title": "Konto",
"deleteAccount": "Konto löschen",
Expand Down
99 changes: 99 additions & 0 deletions frontend/src/i18n/locales/en/project-settings.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
{
"form": {
"name": "Name",
"create": "Create",
"edit": "Edit",
"delete": "Delete",
"nameRequired": "Name is required",
"maxChars": "Max 50 characters"
},
"members": {
"title": "Members",
"noPermission": "You don’t have permission to manage members for this project.",
"orgBanner": "This project belongs to <b>{{org}}</b>. Its owners and admins are admins here; its members are viewers. Those people have access without appearing in the list below.",
"orgFallback": "an organization",
"add": "Add member",
"searchPlaceholder": "Search by name or email…",
"addButton": "Add",
"remove": "Remove",
"owner": "Owner",
"you": "You",
"removeConfirm": "Remove {{name}} from this project?",
"alreadyMember": "This user is already a member.",
"addFailed": "Could not add member.",
"role": {
"VIEWER": "Read-only",
"MEMBER": "Read & Write",
"ADMIN": "Admin"
}
},
"labels": {
"title": "Labels",
"new": "New Label",
"namePlaceholder": "Label name",
"color": "Color",
"preview": "Preview",
"previewName": "Preview",
"duplicate": "A label with that name already exists.",
"deleteConfirm": "Delete this label? It will be removed from all issues.",
"empty": "No labels yet. Create your first one!"
},
"versions": {
"title": "Versions",
"new": "New Version",
"namePlaceholder": "e.g. v1.0.0",
"duplicate": "A version with that name already exists.",
"deleteConfirm": "Delete this version? It will be removed from all issues.",
"empty": "No versions yet. Create your first one!"
},
"fields": {
"title": "Custom Fields",
"new": "New Field",
"namePlaceholder": "Field name",
"requiredLabel": "Required",
"requiredBadge": "required",
"duplicate": "A field with that name already exists.",
"deleteConfirm": "Delete this field? All values will be lost.",
"empty": "No custom fields yet.",
"showOptions": "Options",
"hideOptions": "Hide options",
"optionPlaceholder": "New option label",
"add": "Add",
"type": {
"TEXT": "Text",
"NUMBER": "Number",
"DATE": "Date",
"DROPDOWN": "Dropdown",
"CHECKBOX": "Checkbox"
}
},
"audit": {
"title": "Audit Log",
"filterPlaceholder": "Filter action…",
"empty": "No audit events",
"col": {
"time": "Time",
"user": "User",
"action": "Action",
"level": "Level",
"resource": "Resource"
},
"level": {
"SECURITY": "Security",
"WRITE": "Write",
"ALL": "All"
}
},
"org": {
"title": "Organization",
"noPermission": "You don't have permission to manage this project's organization.",
"current": "Current organization",
"notAssigned": "Not assigned",
"assignLabel": "Assign to organization",
"selectPlaceholder": "Select an organization…",
"assign": "Assign",
"removeFromOrg": "Remove from organization",
"forbidden": "You must be an owner or admin of the target organization to assign this project to it.",
"updateFailed": "Could not update the organization."
}
}
3 changes: 3 additions & 0 deletions frontend/src/i18n/locales/en/settings.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,9 @@
"changing": "Changing…",
"changePassword": "Change password"
},
"tokens": {
"title": "Access Tokens"
},
"account": {
"title": "Account",
"deleteAccount": "Delete account",
Expand Down
18 changes: 10 additions & 8 deletions frontend/src/layouts/SettingsLayout.tsx
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
import { Outlet, NavLink } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { User, Shield, Bell, KeyRound, UserX } from 'lucide-react'

const items = [
{ to: '/settings/profile', label: 'Profile', icon: User },
{ to: '/settings/security', label: 'Security', icon: Shield },
{ to: '/settings/notifications', label: 'Notifications', icon: Bell },
{ to: '/settings/tokens', label: 'Access Tokens', icon: KeyRound },
{ to: '/settings/account', label: 'Account', icon: UserX },
{ to: '/settings/profile', labelKey: 'profile.title', icon: User },
{ to: '/settings/security', labelKey: 'security.title', icon: Shield },
{ to: '/settings/notifications', labelKey: 'notifications.title', icon: Bell },
{ to: '/settings/tokens', labelKey: 'tokens.title', icon: KeyRound },
{ to: '/settings/account', labelKey: 'account.title', icon: UserX },
]

export function SettingsLayout() {
const { t } = useTranslation('settings')
return (
<div className="flex gap-8 h-full min-h-0">
<nav className="w-48 shrink-0 flex flex-col gap-1">
<h2 className="px-3 text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Settings</h2>
{items.map(({ to, label, icon: Icon }) => (
<h2 className="px-3 text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">{t('nav:section.settings')}</h2>
{items.map(({ to, labelKey, icon: Icon }) => (
<NavLink
key={to}
to={to}
Expand All@@ -25,7 +27,7 @@ export function SettingsLayout() {
}
>
<Icon size={18} className="shrink-0" />
<span className="truncate">{label}</span>
<span className="truncate">{t(labelKey)}</span>
</NavLink>
))}
</nav>
Expand Down
Loading
Loading