diff --git a/gradle/changelog/uniform_configuration_document_titles.yaml b/gradle/changelog/uniform_configuration_document_titles.yaml
new file mode 100644
index 0000000000..c4976a352a
--- /dev/null
+++ b/gradle/changelog/uniform_configuration_document_titles.yaml
@@ -0,0 +1,2 @@
+- type: changed
+ description: Uniform document titles for configuration pages
diff --git a/scm-ui/ui-components/src/config/ConfigurationBinder.tsx b/scm-ui/ui-components/src/config/ConfigurationBinder.tsx
index a28a09bed4..18dafaa1fb 100644
--- a/scm-ui/ui-components/src/config/ConfigurationBinder.tsx
+++ b/scm-ui/ui-components/src/config/ConfigurationBinder.tsx
@@ -21,6 +21,8 @@ import { Route } from "react-router-dom";
import { useTranslation, WithTranslation, withTranslation } from "react-i18next";
import { Link, Links, Namespace, Repository } from "@scm-manager/ui-types";
import { urls } from "@scm-manager/ui-api";
+import { useDocumentTitleForRepository } from "@scm-manager/ui-core";
+import { useDocumentTitle } from "@scm-manager/ui-core";
type GlobalRouteProps = {
url: string;
@@ -75,10 +77,20 @@ class ConfigurationBinder {
// route for global configuration, passes the link from the index resource to component
const ConfigRoute = ({ url, links, ...additionalProps }: GlobalRouteProps) => {
const link = links[linkName];
+
+ const TitledGlobalSettingComponent: FC = ({ children }) => {
+ const [t] = useTranslation(this.i18nNamespace);
+ const [commonTranslation] = useTranslation("commons");
+ useDocumentTitle(t(labelI18nKey), commonTranslation("documentTitle.globalConfiguration"));
+ return <>{children}>;
+ };
+
if (link) {
return this.route(
url + "/settings" + to,
-
+
+
+
);
}
};
@@ -138,7 +150,13 @@ class ConfigurationBinder {
binder.bind("repository.route", RepoRoute, repoPredicate);
}
- bindRepositorySetting(to: string, labelI18nKey: string, linkName: string, RepositoryComponent: any, sortKey?: string) {
+ bindRepositorySetting(
+ to: string,
+ labelI18nKey: string,
+ linkName: string,
+ RepositoryComponent: any,
+ sortKey?: string
+ ) {
// create predicate based on the link name of the current repository route
// if the linkname is not available, the navigation link and the route are not bound to the extension points
const repoPredicate = (props: any) => {
@@ -156,10 +174,24 @@ class ConfigurationBinder {
// route for global configuration, passes the current repository to component
const RepoRoute = ({ url, repository, ...additionalProps }: RepositoryRouteProps) => {
const link = repository._links[linkName];
+
+ const TitledRepositorySettingComponent: FC = ({ children }) => {
+ const [t] = useTranslation(this.i18nNamespace);
+ const [commonTranslation] = useTranslation("commons");
+ useDocumentTitleForRepository(
+ repository,
+ t(labelI18nKey),
+ commonTranslation("documentTitle.repositoryConfiguration")
+ );
+ return <>{children}>;
+ };
+
if (link) {
return this.route(
urls.unescapeUrlForRoute(url) + "/settings" + to,
-
+
+
+
);
}
};
@@ -186,10 +218,24 @@ class ConfigurationBinder {
const NamespaceRoute: FC = ({ url, namespace, ...additionalProps }) => {
const link = namespace._links[linkName];
+
+ const TitledNamespaceSettingComponent: FC = ({ children }) => {
+ const [t] = useTranslation(this.i18nNamespace);
+ const [commonTranslation] = useTranslation("commons");
+ useDocumentTitle(
+ t(labelI18nKey),
+ commonTranslation("documentTitle.namespaceConfiguration"),
+ namespace.namespace
+ );
+ return <>{children}>;
+ };
+
if (link) {
return this.route(
urls.unescapeUrlForRoute(url) + "/settings" + to,
-
+
+
+
);
}
return null;
diff --git a/scm-ui/ui-core/src/base/helpers/useDocumentTitle.ts b/scm-ui/ui-core/src/base/helpers/useDocumentTitle.ts
index f117fd6480..867b04b179 100644
--- a/scm-ui/ui-core/src/base/helpers/useDocumentTitle.ts
+++ b/scm-ui/ui-core/src/base/helpers/useDocumentTitle.ts
@@ -17,6 +17,7 @@
import { useEffect } from "react";
import { binder, extensionPoints } from "@scm-manager/ui-extensions";
import { Repository } from "@scm-manager/ui-types";
+import { useTranslation } from "react-i18next";
/**
* Hook to set the document title.
@@ -25,9 +26,10 @@ import { Repository } from "@scm-manager/ui-types";
* Title parts should be sorted with the highest specificity first.
*/
export default function useDocumentTitle(...titleParts: string[]) {
+ const [t] = useTranslation("commons");
useEffect(() => {
const extension = binder.getExtension("document.title");
- let title = `${titleParts.join(" - ")} - SCM-Manager`;
+ let title = [...titleParts, t("documentTitle.suffix")].join(" - ");
if (extension) {
if (typeof extension.documentTitle === "string") {
title += ` (${extension.documentTitle})`;
diff --git a/scm-ui/ui-webapp/public/locales/de/commons.json b/scm-ui/ui-webapp/public/locales/de/commons.json
index 2b58957a83..ce0ebf7c74 100644
--- a/scm-ui/ui-webapp/public/locales/de/commons.json
+++ b/scm-ui/ui-webapp/public/locales/de/commons.json
@@ -361,5 +361,11 @@
"next": "Fokussiere den nächsten Listeneintrag",
"previous": "Fokussiere den vorherigen Listeneintrag"
}
+ },
+ "documentTitle": {
+ "suffix": "SCM-Manager",
+ "repositoryConfiguration": "Konfiguration",
+ "namespaceConfiguration": "Konfiguration",
+ "globalConfiguration": "Konfiguration"
}
}
diff --git a/scm-ui/ui-webapp/public/locales/en/commons.json b/scm-ui/ui-webapp/public/locales/en/commons.json
index f06ca9b4bb..bc1787b7af 100644
--- a/scm-ui/ui-webapp/public/locales/en/commons.json
+++ b/scm-ui/ui-webapp/public/locales/en/commons.json
@@ -362,5 +362,11 @@
"next": "Focus next list item",
"previous": "Focus previous list item"
}
+ },
+ "documentTitle": {
+ "suffix": "SCM-Manager",
+ "repositoryConfiguration": "Configuration",
+ "namespaceConfiguration": "Configuration",
+ "globalConfiguration": "Configuration"
}
}
diff --git a/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx b/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx
index 9145606716..2c4d764327 100644
--- a/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx
+++ b/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx
@@ -45,8 +45,10 @@ const usePermissionData = (namespaceOrRepository: Namespace | Repository) => {
const Permissions: FC = ({ namespaceOrRepository }) => {
const { isLoading, error, permissions, availablePermissions } = usePermissionData(namespaceOrRepository);
const [t] = useTranslation("repos");
+ const [commonTranslation] = useTranslation("commons");
useDocumentTitle(
t("repositoryRoot.menu.permissionsNavLink"),
+ commonTranslation(`documentTitle.${isRepository(namespaceOrRepository) ? "repository" : "namespace"}Configuration`),
namespaceOrRepository.namespace + (isRepository(namespaceOrRepository) ? "/" + namespaceOrRepository.name : "")
);