aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2024-11-05 18:07:01 +0100
committersonartech <sonartech@sonarsource.com>2024-11-13 20:05:48 +0000
commit29168ab9565bcc29a752a58efb3c09848f7b2b03 (patch)
tree9c1435e35e724a829fb5a5856ba4049d5e3d1887
parentb2a3c8a65a92fdac25042a05cd37c43fa18c67c3 (diff)
downloadsonarqube-29168ab9565bcc29a752a58efb3c09848f7b2b03.tar.gz
sonarqube-29168ab9565bcc29a752a58efb3c09848f7b2b03.zip
SONAR-23596 New branding
SONAR-23597 SONAR-23598 SONAR-23595
-rw-r--r--server/sonar-web/config/jest/SetupFailOnConsole.ts3
-rw-r--r--server/sonar-web/package.json2
-rw-r--r--server/sonar-web/public/images/SonarLint-connection-ok.pngbin6308 -> 0 bytes
-rw-r--r--server/sonar-web/public/images/SonarLint-connection-request.pngbin6605 -> 0 bytes
-rw-r--r--server/sonar-web/public/images/sq-sl.svg1
-rw-r--r--server/sonar-web/src/main/js/app/components/AdminContainer.tsx10
-rw-r--r--server/sonar-web/src/main/js/app/components/ComponentContainer.tsx10
-rw-r--r--server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx23
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx8
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx10
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx1
-rw-r--r--server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx8
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchHelpTooltip.tsx13
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMore.tsx4
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/global/MainSonarQubeBar.tsx6
-rw-r--r--server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx12
-rw-r--r--server/sonar-web/src/main/js/app/index.ts13
-rw-r--r--server/sonar-web/src/main/js/apps/account/Account.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-it.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRuleDetails-it.ts11
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts8
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts8
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx9
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/create/project/__tests__/MonorepoProjectCreate-it.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssueDetails.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/TotalEffort.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projectNewCode/components/BranchListRow.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/projectNewCode/components/NewCodeDefinitionSettingReferenceBranch.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/ProjectNewCodeDefinitionApp-it.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/sessions/components/Login.tsx15
-rw-r--r--server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts8
-rw-r--r--server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx6
-rw-r--r--server/sonar-web/src/main/js/components/branding/SonarQubeConnectionIllustration.tsx199
-rw-r--r--server/sonar-web/src/main/js/components/branding/SonarQubeIDEPromotionIllustration.tsx107
-rw-r--r--server/sonar-web/src/main/js/components/branding/SonarQubeProductLogo.tsx38
-rw-r--r--server/sonar-web/src/main/js/components/branding/__tests__/SonarQubeConnectionIllustration-test.tsx39
-rw-r--r--server/sonar-web/src/main/js/components/branding/__tests__/__snapshots__/SonarQubeConnectionIllustration-test.tsx.snap189
-rw-r--r--server/sonar-web/src/main/js/components/embed-docs-modal/EmbedDocsPopup.tsx53
-rw-r--r--server/sonar-web/src/main/js/components/intl/TranslatedMessage.tsx28
-rw-r--r--server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx5
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/RepositoryVariables.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx2
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/test-utils.ts8
-rw-r--r--server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx2
-rw-r--r--server/sonar-web/src/main/js/helpers/__tests__/l10n-test.ts32
-rw-r--r--server/sonar-web/src/main/js/helpers/__tests__/l10nBundle-test.ts11
-rw-r--r--server/sonar-web/src/main/js/helpers/__tests__/measures-test.ts8
-rw-r--r--server/sonar-web/src/main/js/helpers/l10n.ts9
-rw-r--r--server/sonar-web/src/main/js/helpers/l10nBundle.ts26
-rw-r--r--server/sonar-web/src/main/js/sonar-aligned/helpers/__tests__/measures-test.ts13
-rw-r--r--server/sonar-web/yarn.lock68
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties297
61 files changed, 1078 insertions, 367 deletions
diff --git a/server/sonar-web/config/jest/SetupFailOnConsole.ts b/server/sonar-web/config/jest/SetupFailOnConsole.ts
index 182cafd7e37..251becf1978 100644
--- a/server/sonar-web/config/jest/SetupFailOnConsole.ts
+++ b/server/sonar-web/config/jest/SetupFailOnConsole.ts
@@ -22,6 +22,9 @@ import failOnConsole from 'jest-fail-on-console';
const IGNORED_ERROR_MESSAGES: string[] = [
// react-virtualized & react-draggable use `findDOMNode` which is deprecated
'findDOMNode is deprecated and will be removed in the next major release',
+
+ // react-intl warning
+ '[@formatjs/intl] "defaultRichTextElements" was specified but "message" was not pre-compiled.',
];
failOnConsole({
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json
index cba1b568d50..cdc60b42ce7 100644
--- a/server/sonar-web/package.json
+++ b/server/sonar-web/package.json
@@ -10,7 +10,7 @@
"@primer/octicons-react": "19.11.0",
"@react-spring/rafz": "9.7.4",
"@react-spring/web": "9.7.4",
- "@sonarsource/echoes-react": "0.9.0",
+ "@sonarsource/echoes-react": "0.10.1",
"@tanstack/react-query": "5.56.2",
"@types/validator": "13.12.2",
"axios": "1.7.7",
diff --git a/server/sonar-web/public/images/SonarLint-connection-ok.png b/server/sonar-web/public/images/SonarLint-connection-ok.png
deleted file mode 100644
index e4071d0e2f0..00000000000
--- a/server/sonar-web/public/images/SonarLint-connection-ok.png
+++ /dev/null
Binary files differ
diff --git a/server/sonar-web/public/images/SonarLint-connection-request.png b/server/sonar-web/public/images/SonarLint-connection-request.png
deleted file mode 100644
index 421a79baa81..00000000000
--- a/server/sonar-web/public/images/SonarLint-connection-request.png
+++ /dev/null
Binary files differ
diff --git a/server/sonar-web/public/images/sq-sl.svg b/server/sonar-web/public/images/sq-sl.svg
deleted file mode 100644
index 76860987bdd..00000000000
--- a/server/sonar-web/public/images/sq-sl.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 88"><defs><style>.cls-1{fill:#cb2029;}.cls-2{fill:#549dd0;}.cls-3{fill:#a0a5a8;}</style></defs><g id="Group_7691" data-name="Group 7691"><path id="Path_10468" data-name="Path 10468" class="cls-1" d="M15,87.26a14,14,0,1,1,14-14,14,14,0,0,1-14,14m0-25.42A11.44,11.44,0,1,0,26.43,73.28,11.46,11.46,0,0,0,15,61.84"/></g><path id="Path_10469" data-name="Path 10469" class="cls-1" d="M24.78,72.54c-.37-.73-.83-1.65-1.73-1.65s-1.35.91-1.72,1.65-.54,1-1,1-.62-.35-1-1-.82-1.65-1.72-1.65-1.35.91-1.73,1.65-.53,1-1,1-.62-.35-1-1-.82-1.65-1.72-1.65-1.36.91-1.73,1.65-.53,1-1,1-.63-.35-1-1-.82-1.65-1.72-1.65-1.36.87-1.72,1.6c0,0-.28.62-.4.91s-.6,1.41-.17,1.95c.25.3.6-.13.8-.45s.5-.94.5-.94c.37-.65.56-1,1-1s.62.36,1,1,.82,1.64,1.73,1.64S11,74.73,11.32,74s.54-1,1-1,.62.36,1,1,.83,1.64,1.73,1.64,1.35-.91,1.72-1.64.54-1,1-1,.62.36,1,1,.82,1.64,1.72,1.64,1.35-.91,1.73-1.64.53-1,1-1,.62.36,1,1l.38.7c.1.17.48.8.81.78s.4-1,.07-1.77C25.06,73.17,24.78,72.54,24.78,72.54Z"/><g id="SonarQube_Icon" data-name="SonarQube Icon"><path id="Path_10444" data-name="Path 10444" class="cls-2" d="M26.24,29.05H24.61A24.1,24.1,0,0,0,.37,5.15V3.52A25.73,25.73,0,0,1,26.24,29.05Z"/><path id="Path_10445" data-name="Path 10445" class="cls-2" d="M27.37,20.26a24.64,24.64,0,0,0-17-17.58l.38-1.31A26,26,0,0,1,28.7,20Z"/><path id="Path_10446" data-name="Path 10446" class="cls-2" d="M28.63,12.5A25,25,0,0,0,18.92,1.67l.57-.93A26.19,26.19,0,0,1,29.63,12.05Z"/></g><path class="cls-3" d="M16.25,42.09h4v2.33h-4V49H13.77V44.42h-4V42.09h4V37.85h2.48Z"/></svg> \ No newline at end of file
diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
index faa3e3c4fa3..65e122d5618 100644
--- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx
@@ -26,7 +26,8 @@ import { getSettingsNavigation } from '../../api/navigation';
import { getPendingPlugins } from '../../api/plugins';
import { getSystemStatus, waitSystemUPStatus } from '../../api/system';
import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization';
-import { translate, translateWithParameters } from '../../helpers/l10n';
+import { translate } from '../../helpers/l10n';
+import { getIntl } from '../../helpers/l10nBundle';
import { AdminPagesContext } from '../../types/admin';
import { AppState } from '../../types/appstate';
import { PendingPluginResult } from '../../types/plugins';
@@ -46,6 +47,7 @@ interface State {
}
export class AdminContainer extends React.PureComponent<AdminContainerProps, State> {
+ intl = getIntl();
mounted = false;
portalAnchor: Element | null = null;
state: State = {
@@ -129,9 +131,9 @@ export class AdminContainer extends React.PureComponent<AdminContainerProps, Sta
<>
<Helmet
defer={false}
- titleTemplate={translateWithParameters(
- 'page_title.template.with_category',
- translate('layout.settings'),
+ titleTemplate={this.intl.formatMessage(
+ { id: 'page_title.template.with_category' },
+ { page: translate('layout.settings') },
)}
/>
{this.portalAnchor &&
diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
index 34913e58aff..31fb9b35a42 100644
--- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
@@ -22,6 +22,7 @@ import { differenceBy } from 'lodash';
import * as React from 'react';
import { createPortal } from 'react-dom';
import { Helmet } from 'react-helmet-async';
+import { useIntl } from 'react-intl';
import { Outlet } from 'react-router-dom';
import { CenteredLayout, Spinner } from '~design-system';
import { useLocation, useRouter } from '~sonar-aligned/components/hoc/withRouter';
@@ -31,7 +32,6 @@ import { validateProjectAlmBinding } from '../../api/alm-settings';
import { getTasksForComponent } from '../../api/ce';
import { getComponentData } from '../../api/components';
import { getComponentNavigation } from '../../api/navigation';
-import { translateWithParameters } from '../../helpers/l10n';
import { HttpStatus } from '../../helpers/request';
import { getPortfolioUrl, getProjectUrl, getPullRequestUrl } from '../../helpers/urls';
import { useCurrentBranchQuery } from '../../queries/branch';
@@ -63,6 +63,8 @@ function ComponentContainer({ hasFeature }: Readonly<WithAvailableFeaturesProps>
} = useLocation();
const router = useRouter();
+ const intl = useIntl();
+
const [component, setComponent] = React.useState<Component>();
const [currentTask, setCurrentTask] = React.useState<Task>();
const [tasksInProgress, setTasksInProgress] = React.useState<Task[]>();
@@ -285,9 +287,9 @@ function ComponentContainer({ hasFeature }: Readonly<WithAvailableFeaturesProps>
<div>
<Helmet
defer={false}
- titleTemplate={translateWithParameters(
- 'page_title.template.with_instance',
- component?.name ?? '',
+ titleTemplate={intl.formatMessage(
+ { id: 'page_title.template.with_instance' },
+ { project: component?.name ?? '' },
)}
/>
{component &&
diff --git a/server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx b/server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx
index cce2c4419d6..106ca9b1439 100644
--- a/server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx
+++ b/server/sonar-web/src/main/js/app/components/SonarLintConnection.tsx
@@ -33,6 +33,7 @@ import {
Title,
} from '~design-system';
import { Image } from '~sonar-aligned/components/common/Image';
+import { SonarQubeConnectionIllustration } from '../../components/branding/SonarQubeConnectionIllustration';
import { whenLoggedIn } from '../../components/hoc/whenLoggedIn';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { generateSonarLintUserToken, portIsValid, sendUserToken } from '../../helpers/sonarlint';
@@ -88,13 +89,9 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {
{status === Status.request && (
<>
<Title>{translate('sonarlint-connection.request.title')}</Title>
- <Image
- alt="sonarlint-connection-request"
- className="sw-my-4"
- src="/images/SonarLint-connection-request.png"
- />
+ <SonarQubeConnectionIllustration className="sw-my-4" connected={false} />
<p className="sw-my-4">
- {translateWithParameters('sonarlint-connection.request.description', ideName)}
+ <FormattedMessage id="sonarlint-connection.request.description" values={{ ideName }} />
</p>
<p className="sw-mb-10">{translate('sonarlint-connection.request.description2')}</p>
@@ -110,7 +107,7 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {
{status === Status.tokenError && (
<>
- <Image alt="sonarlint-token-error" className="sw-my-4 sw-pt-2" src="/images/cross.svg" />
+ <Image aria-hidden className="sw-my-4 sw-pt-2" src="/images/cross.svg" />
<Title>{translate('sonarlint-connection.token-error.title')}</Title>
<p className="sw-my-4">{translate('sonarlint-connection.token-error.description')}</p>
<p className="sw-mb-4">
@@ -131,11 +128,7 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {
{status === Status.tokenCreated && newToken && (
<>
- <Image
- alt="sonarlint-connection-error"
- className="sw-my-4 sw-pt-2"
- src="/images/check.svg"
- />
+ <Image aria-hidden className="sw-my-4 sw-pt-2" src="/images/check.svg" />
<Title>{translate('sonarlint-connection.connection-error.title')}</Title>
<p className="sw-my-6">
{translate('sonarlint-connection.connection-error.description')}
@@ -167,11 +160,7 @@ export function SonarLintConnection({ currentUser }: Readonly<Props>) {
{status === Status.tokenSent && newToken && (
<>
<Title>{translate('sonarlint-connection.success.title')}</Title>
- <Image
- alt="sonarlint-connection-success"
- className="sw-mb-4"
- src="/images/SonarLint-connection-ok.png"
- />
+ <SonarQubeConnectionIllustration className="sw-mb-4" connected />
<p className="sw-my-4">
{translateWithParameters('sonarlint-connection.success.description', newToken.name)}
</p>
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx
index 54ed12766d7..8dc51f4ca7c 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/AdminContainer-test.tsx
@@ -29,6 +29,14 @@ import { AdminPagesContext } from '../../../types/admin';
import { AdminContainer, AdminContainerProps } from '../AdminContainer';
import AdminContext from '../AdminContext';
+jest.mock('../../../helpers/l10nBundle', () => {
+ const bundle = jest.requireActual('../../../helpers/l10nBundle');
+ return {
+ ...bundle,
+ getIntl: () => ({ formatMessage: jest.fn() }),
+ };
+});
+
jest.mock('../../../api/navigation', () => ({
getSettingsNavigation: jest
.fn()
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
index 70e84d95c97..b9976eb3e21 100644
--- a/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
+++ b/server/sonar-web/src/main/js/app/components/__tests__/GlobalFooter-test.tsx
@@ -35,15 +35,15 @@ afterEach(() => {
});
it('should render the logged-in information', async () => {
- renderGlobalFooter();
+ renderGlobalFooter({}, { edition: EditionKey.community });
expect(ui.databaseWarningMessage.query()).not.toBeInTheDocument();
expect(ui.footerListItems.getAll()).toHaveLength(7);
expect(byText('Community Edition').get()).toBeInTheDocument();
- expect(ui.versionLabel('4.2').get()).toBeInTheDocument();
- expect(await ui.ltaDocumentationLinkActive.find()).toBeInTheDocument();
+ expect(await ui.versionLabel('4.2').find()).toBeInTheDocument();
+ expect(ui.ltaDocumentationLinkActive.query()).not.toBeInTheDocument();
expect(ui.apiLink.get()).toBeInTheDocument();
});
@@ -85,7 +85,7 @@ it('should not render missing logged-in information', () => {
});
it('should not render the logged-in information', () => {
- renderGlobalFooter({ hideLoggedInInfo: true });
+ renderGlobalFooter({ hideLoggedInInfo: true }, { edition: EditionKey.community });
expect(ui.databaseWarningMessage.query()).not.toBeInTheDocument();
@@ -109,7 +109,7 @@ function renderGlobalFooter(
return renderComponent(<GlobalFooter {...props} />, '/', {
appState: mockAppState({
productionDatabase: true,
- edition: EditionKey.community,
+ edition: EditionKey.developer,
version: '4.2',
...appStateOverride,
}),
diff --git a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx
index c1a7473aa79..be82875356e 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/IndexationNotificationRenderer.tsx
@@ -156,7 +156,6 @@ function renderInProgressBanner(completedCount: number, total: number) {
<span>
<FormattedMessage
id="indexation.admin_link"
- defaultMessage={translate('indexation.admin_link')}
values={{
link: renderBackgroundTasksPageLink(false, translate('background_tasks.page')),
}}
diff --git a/server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx b/server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx
index d29d1223ec4..7b06d3ddcd5 100644
--- a/server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx
+++ b/server/sonar-web/src/main/js/app/components/indexation/PageUnavailableDueToIndexation.tsx
@@ -24,7 +24,6 @@ import { CenteredLayout, FlagMessage, Link } from '~design-system';
import withIndexationContext, {
WithIndexationContextProps,
} from '../../../components/hoc/withIndexationContext';
-import { translate } from '../../../helpers/l10n';
export class PageUnavailableDueToIndexation extends React.PureComponent<WithIndexationContextProps> {
componentDidUpdate() {
@@ -41,17 +40,14 @@ export class PageUnavailableDueToIndexation extends React.PureComponent<WithInde
<CenteredLayout className="sw-flex sw-justify-around">
<FlagMessage className="sw-mt-32" variant="info">
<span className="sw-w-[290px]">
- {translate('indexation.page_unavailable.description')}
+ <FormattedMessage id="indexation.page_unavailable.description" />
<span className="sw-ml-1">
<FormattedMessage
- defaultMessage={translate(
- 'indexation.page_unavailable.description.additional_information',
- )}
id="indexation.page_unavailable.description.additional_information"
values={{
link: (
<Link to="https://docs.sonarsource.com/sonarqube/latest/instance-administration/reindexing/">
- {translate('learn_more')}
+ <FormattedMessage id="learn_more" />
</Link>
),
}}
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchHelpTooltip.tsx b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchHelpTooltip.tsx
index 3f72585ddc7..062673766cf 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchHelpTooltip.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/branch-like/BranchHelpTooltip.tsx
@@ -19,11 +19,12 @@
*/
import { Link } from '@sonarsource/echoes-react';
+import { useIntl } from 'react-intl';
import { HelperHintIcon } from '~design-system';
import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip';
import HelpTooltip from '~sonar-aligned/components/controls/HelpTooltip';
import { DocLink } from '../../../../../helpers/doc-links';
-import { translate, translateWithParameters } from '../../../../../helpers/l10n';
+import { translate } from '../../../../../helpers/l10n';
import { getApplicationAdminUrl } from '../../../../../helpers/urls';
import { useProjectBindingQuery } from '../../../../../queries/devops-integration';
import { AlmKeys } from '../../../../../types/alm-settings';
@@ -48,6 +49,8 @@ export default function BranchHelpTooltip({
const { data: projectBinding } = useProjectBindingQuery(component.key);
const isGitLab = projectBinding != null && projectBinding.alm === AlmKeys.GitLab;
+ const intl = useIntl();
+
if (isApplication) {
if (!hasManyBranches && canAdminComponent) {
return (
@@ -72,9 +75,11 @@ export default function BranchHelpTooltip({
<DocHelpTooltip
content={
projectBinding != null
- ? translateWithParameters(
- `branch_like_navigation.no_branch_support.content_x.${isGitLab ? 'mr' : 'pr'}`,
- translate('alm', projectBinding.alm),
+ ? intl.formatMessage(
+ {
+ id: `branch_like_navigation.no_branch_support.content_x.${isGitLab ? 'mr' : 'pr'}`,
+ },
+ { alm: translate('alm', projectBinding.alm) },
)
: translate('branch_like_navigation.no_branch_support.content')
}
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMore.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMore.tsx
index 22ce2083a08..094f55130f1 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMore.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMore.tsx
@@ -19,8 +19,8 @@
*/
import { DropdownMenu, DropdownMenuAlign } from '@sonarsource/echoes-react';
+import { FormattedMessage } from 'react-intl';
import { MainMenuItem } from '~design-system';
-import { translate } from '../../../../helpers/l10n';
import { AppState } from '../../../../types/appstate';
import { Extension } from '../../../../types/types';
import withAppStateContext from '../../app-state/withAppStateContext';
@@ -48,7 +48,7 @@ function GlobalNavMore({ appState: { globalPages = [] } }: Readonly<{ appState:
>
<MainMenuItem>
<a aria-haspopup="menu" href="#" id="global-navigation-more" role="button">
- {translate('more')}
+ <FormattedMessage id="more" />
</a>
</MainMenuItem>
</DropdownMenu.Root>
diff --git a/server/sonar-web/src/main/js/app/components/nav/global/MainSonarQubeBar.tsx b/server/sonar-web/src/main/js/app/components/nav/global/MainSonarQubeBar.tsx
index dc200ed30e2..5a642219a17 100644
--- a/server/sonar-web/src/main/js/app/components/nav/global/MainSonarQubeBar.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/global/MainSonarQubeBar.tsx
@@ -18,9 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { LogoSize } from '@sonarsource/echoes-react';
import * as React from 'react';
-import { MainAppBar, SonarQubeLogo } from '~design-system';
+import { MainAppBar } from '~design-system';
import { Image } from '~sonar-aligned/components/common/Image';
+import { SonarQubeProductLogo } from '../../../../components/branding/SonarQubeProductLogo';
import { translate } from '../../../../helpers/l10n';
import { GlobalSettingKeys } from '../../../../types/settings';
import { AppStateContext } from '../../app-state/AppStateContext';
@@ -41,7 +43,7 @@ function LogoWithAriaText() {
{customLogoUrl ? (
<Image alt={title} src={customLogoUrl} width={customLogoWidth} />
) : (
- <SonarQubeLogo />
+ <SonarQubeProductLogo hasText size={LogoSize.Large} />
)}
</div>
);
diff --git a/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx b/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx
index 9e828b956e3..8bf0ca1a0f9 100644
--- a/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx
+++ b/server/sonar-web/src/main/js/app/components/promotion-notification/PromotionNotification.tsx
@@ -19,11 +19,11 @@
*/
import styled from '@emotion/styled';
-import { Button } from '@sonarsource/echoes-react';
+import { Button, Theme, ThemeProvider } from '@sonarsource/echoes-react';
import * as React from 'react';
import { ButtonPrimary, themeBorder, themeColor } from '~design-system';
-import { Image } from '~sonar-aligned/components/common/Image';
import { dismissNotice } from '../../../api/users';
+import { SonarQubeIDEPromotionIllustration } from '../../../components/branding/SonarQubeIDEPromotionIllustration';
import { translate } from '../../../helpers/l10n';
import { NoticeType, isLoggedIn } from '../../../types/users';
import { CurrentUserContextInterface } from '../current-user/CurrentUserContext';
@@ -48,9 +48,11 @@ export function PromotionNotification(props: CurrentUserContextInterface) {
return (
<PromotionNotificationWrapper className="it__promotion_notification sw-z-global-popup sw-rounded-1 sw-flex sw-items-center sw-px-4">
- <div className="sw-mr-2">
- <Image alt="SonarQube + SonarLint" height={80} src="/images/sq-sl.svg" />
- </div>
+ <ThemeProvider theme={Theme.dark}>
+ <div className="sw-mr-2">
+ <SonarQubeIDEPromotionIllustration />
+ </div>
+ </ThemeProvider>
<PromotionNotificationContent className="sw-flex-1 sw-px-2 sw-py-4">
<span className="sw-typo-semibold">{translate('promotion.sonarlint.title')}</span>
<p className="sw-mt-2">{translate('promotion.sonarlint.content')}</p>
diff --git a/server/sonar-web/src/main/js/app/index.ts b/server/sonar-web/src/main/js/app/index.ts
index 8ad4712c238..e1b81ebe273 100644
--- a/server/sonar-web/src/main/js/app/index.ts
+++ b/server/sonar-web/src/main/js/app/index.ts
@@ -52,10 +52,17 @@ async function initApplication() {
},
);
- const [l10nBundle, currentUser, appState, availableFeatures] = await Promise.all([
- loadL10nBundle(),
+ const appState = isMainApp()
+ ? await getGlobalNavigation().catch((error) => {
+ // eslint-disable-next-line no-console
+ console.error(error);
+ return undefined;
+ })
+ : undefined;
+
+ const [l10nBundle, currentUser, availableFeatures] = await Promise.all([
+ loadL10nBundle(appState),
isMainApp() ? getCurrentUser() : undefined,
- isMainApp() ? getGlobalNavigation() : undefined,
isMainApp() ? getAvailableFeatures() : undefined,
]).catch((error) => {
// eslint-disable-next-line no-console
diff --git a/server/sonar-web/src/main/js/apps/account/Account.tsx b/server/sonar-web/src/main/js/apps/account/Account.tsx
index 8f16428327c..8c7b227834c 100644
--- a/server/sonar-web/src/main/js/apps/account/Account.tsx
+++ b/server/sonar-web/src/main/js/apps/account/Account.tsx
@@ -21,11 +21,12 @@
import * as React from 'react';
import { createPortal } from 'react-dom';
import { Helmet } from 'react-helmet-async';
+import { useIntl } from 'react-intl';
import { Outlet } from 'react-router-dom';
import { LargeCenteredLayout, PageContentFontWrapper, TopBar } from '~design-system';
import A11ySkipTarget from '~sonar-aligned/components/a11y/A11ySkipTarget';
import { useCurrentLoginUser } from '../../app/components/current-user/CurrentUserContext';
-import { translate, translateWithParameters } from '../../helpers/l10n';
+import { translate } from '../../helpers/l10n';
import Nav from './components/Nav';
import UserCard from './components/UserCard';
@@ -33,6 +34,8 @@ export default function Account() {
const currentUser = useCurrentLoginUser();
const [portalAnchor, setPortalAnchor] = React.useState<Element | null>(null);
+ const intl = useIntl();
+
// Set portal anchor on mount
React.useEffect(() => {
setPortalAnchor(document.getElementById('component-nav-portal'));
@@ -61,9 +64,9 @@ export default function Account() {
<Helmet
defaultTitle={title}
defer={false}
- titleTemplate={translateWithParameters(
- 'page_title.template.with_category',
- translate('my_account.page'),
+ titleTemplate={intl.formatMessage(
+ { id: 'page_title.template.with_category' },
+ { page: translate('my_account.page') },
)}
/>
diff --git a/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-it.tsx b/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-it.tsx
index 889e943b41f..daf9032ca96 100644
--- a/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/audit-logs/components/__tests__/AuditApp-it.tsx
@@ -53,6 +53,14 @@ jest.mock('../../../../helpers/dates', () => {
};
});
+jest.mock('../../../../helpers/l10nBundle', () => {
+ const bundle = jest.requireActual('../../../../helpers/l10nBundle');
+ return {
+ ...bundle,
+ getIntl: () => ({ formatMessage: jest.fn(({ id }) => `${id}`) }),
+ };
+});
+
const ui = {
pageTitle: byRole('heading', { name: 'audit_logs.page' }),
downloadButton: byRole('link', { name: 'download_verb' }),
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRuleDetails-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRuleDetails-it.ts
index 37916c840b0..d59b743fc31 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRuleDetails-it.ts
+++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRuleDetails-it.ts
@@ -36,6 +36,15 @@ import { getPageObjects, renderCodingRulesApp } from '../utils-tests';
const rulesHandler = new CodingRulesServiceMock();
const settingsHandler = new SettingsServiceMock();
+
+jest.mock('../../../helpers/l10nBundle', () => {
+ const bundle = jest.requireActual('../../../helpers/l10nBundle');
+ return {
+ ...bundle,
+ getIntl: () => ({ formatMessage: jest.fn() }),
+ };
+});
+
afterEach(() => {
settingsHandler.reset();
rulesHandler.reset();
@@ -53,7 +62,7 @@ describe('rendering', () => {
expect(ui.ruleCleanCodeAttribute(CleanCodeAttribute.Clear).get()).toBeInTheDocument();
// 1 In Rule details + 1 in facet
expect(ui.ruleSoftwareQuality(SoftwareQuality.Maintainability).getAll()).toHaveLength(2);
- expect(document.title).toEqual('page_title.template.with_category.coding_rules.page');
+ expect(document.title).toEqual('coding_rule.page.Java.Awsome java rule');
expect(screen.getByText('Why')).toBeInTheDocument();
expect(screen.getByText('Because')).toBeInTheDocument();
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
index a68adfaaf8b..7e61a2f7aa9 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
+++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
@@ -38,6 +38,14 @@ import { getPageObjects, renderCodingRulesApp } from '../utils-tests';
const rulesHandler = new CodingRulesServiceMock();
const settingsHandler = new SettingsServiceMock();
+jest.mock('../../../helpers/l10nBundle', () => {
+ const bundle = jest.requireActual('../../../helpers/l10nBundle');
+ return {
+ ...bundle,
+ getIntl: () => ({ formatMessage: jest.fn() }),
+ };
+});
+
afterEach(() => {
rulesHandler.reset();
settingsHandler.reset();
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts
index 0deaa28759c..73cbba1cfcc 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts
+++ b/server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts
@@ -28,6 +28,14 @@ import { getPageObjects, renderCodingRulesApp } from '../utils-tests';
const rulesHandler = new CodingRulesServiceMock();
const settingsHandler = new SettingsServiceMock();
+jest.mock('../../../helpers/l10nBundle', () => {
+ const bundle = jest.requireActual('../../../helpers/l10nBundle');
+ return {
+ ...bundle,
+ getIntl: () => ({ formatMessage: jest.fn() }),
+ };
+});
+
afterEach(() => {
rulesHandler.reset();
settingsHandler.reset();
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx
index 1625138526e..77c670e76be 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/CodingRulesApp.tsx
@@ -45,6 +45,7 @@ import { DocLink } from '../../../helpers/doc-links';
import { isInput, isShortcut } from '../../../helpers/keyboardEventHelpers';
import { KeyboardKeys } from '../../../helpers/keycodes';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { getIntl } from '../../../helpers/l10nBundle';
import { SecurityStandard } from '../../../types/security';
import { SettingsKey } from '../../../types/settings';
import { Dict, Paging, Rule, RuleActivation } from '../../../types/types';
@@ -103,6 +104,7 @@ interface State {
const RULE_LIST_HEADER_HEIGHT = 68;
export class CodingRulesApp extends React.PureComponent<Props, State> {
+ intl = getIntl();
mounted = false;
constructor(props: Props) {
@@ -574,9 +576,9 @@ export class CodingRulesApp extends React.PureComponent<Props, State> {
<Helmet
defer={false}
title={translateWithParameters('coding_rule.page', openRule.langName, openRule.name)}
- titleTemplate={translateWithParameters(
- 'page_title.template.with_category',
- translate('coding_rules.page'),
+ titleTemplate={this.intl.formatMessage(
+ { id: 'page_title.template.with_category' },
+ { page: translate('coding_rules.page') },
)}
/>
) : (
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx
index 139b9a7fbdb..2afc4120eb6 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/Azure-it.tsx
@@ -60,13 +60,7 @@ const ui = {
}),
};
-const original = window.location;
-
beforeAll(() => {
- Object.defineProperty(window, 'location', {
- configurable: true,
- value: { replace: jest.fn() },
- });
almIntegrationHandler = new AlmIntegrationsServiceMock();
dopTranslationHandler = new DopTranslationServiceMock();
newCodePeriodHandler = new NewCodeDefinitionServiceMock();
@@ -78,9 +72,6 @@ beforeEach(() => {
dopTranslationHandler.reset();
newCodePeriodHandler.reset();
});
-afterAll(() => {
- Object.defineProperty(window, 'location', { configurable: true, value: original });
-});
it('should ask for PAT when it is not set yet and show the import project feature afterwards', async () => {
const user = userEvent.setup();
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx
index d3ee2e65328..2bee7a314df 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/Bitbucket-it.tsx
@@ -55,13 +55,8 @@ const ui = {
}),
instanceSelector: byLabelText(/alm.configuration.selector.label/),
};
-const original = window.location;
beforeAll(() => {
- Object.defineProperty(window, 'location', {
- configurable: true,
- value: { replace: jest.fn() },
- });
almIntegrationHandler = new AlmIntegrationsServiceMock();
dopTranslationHandler = new DopTranslationServiceMock();
newCodePeriodHandler = new NewCodeDefinitionServiceMock();
@@ -74,10 +69,6 @@ beforeEach(() => {
newCodePeriodHandler.reset();
});
-afterAll(() => {
- Object.defineProperty(window, 'location', { configurable: true, value: original });
-});
-
it('should ask for PAT when it is not set yet and show the import project feature afterwards', async () => {
const user = userEvent.setup();
renderCreateProject();
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx
index ca52dfa93b4..20417f5162a 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/GitLab-it.tsx
@@ -83,13 +83,7 @@ const ui = {
globalSettingRadio: byRole('radio', { name: 'new_code_definition.global_setting' }),
};
-const original = window.location;
-
beforeAll(() => {
- Object.defineProperty(window, 'location', {
- configurable: true,
- value: { replace: jest.fn() },
- });
almIntegrationHandler = new AlmIntegrationsServiceMock();
dopTranslationHandler = new DopTranslationServiceMock();
newCodePeriodHandler = new NewCodeDefinitionServiceMock();
@@ -102,10 +96,6 @@ beforeEach(() => {
newCodePeriodHandler.reset();
});
-afterAll(() => {
- Object.defineProperty(window, 'location', { configurable: true, value: original });
-});
-
it('should ask for PAT when it is not set yet and show the import project feature afterwards', async () => {
const user = userEvent.setup();
renderCreateProject();
diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/MonorepoProjectCreate-it.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/MonorepoProjectCreate-it.tsx
index ce4a24d6a91..a608af96767 100644
--- a/server/sonar-web/src/main/js/apps/create/project/__tests__/MonorepoProjectCreate-it.tsx
+++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/MonorepoProjectCreate-it.tsx
@@ -77,10 +77,6 @@ const ui = {
};
beforeAll(() => {
- Object.defineProperty(window, 'location', {
- configurable: true,
- value: { replace: jest.fn() },
- });
almIntegrationHandler = new AlmIntegrationsServiceMock();
almSettingsHandler = new AlmSettingsServiceMock();
componentsHandler = new ComponentsServiceMock();
diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
index 051bdfb50ea..abf661e987b 100644
--- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx
@@ -130,7 +130,7 @@ describe('issues app', () => {
// Are rule headers present?
expect(screen.getByRole('heading', { level: 1, name: 'Fix that' })).toBeInTheDocument();
- expect(screen.getByRole('link', { name: 'advancedRuleId' })).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: /^advancedRuleId/ })).toBeInTheDocument();
// Select the "why is this an issue" tab and check its content
await user.click(
@@ -182,7 +182,7 @@ describe('issues app', () => {
// Are rule headers present?
expect(screen.getByRole('heading', { level: 1, name: 'Fix this' })).toBeInTheDocument();
- expect(screen.getByRole('link', { name: 'simpleRuleId' })).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: /^simpleRuleId/ })).toBeInTheDocument();
// Select the "why is this an issue tab" and check its content
await user.click(
@@ -195,7 +195,7 @@ describe('issues app', () => {
// Are rule headers present?
expect(screen.getByRole('heading', { level: 1, name: 'Issue on file' })).toBeInTheDocument();
- expect(screen.getByRole('link', { name: 'simpleRuleId' })).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: /^simpleRuleId/ })).toBeInTheDocument();
// The "Where is the issue" tab should be selected by default. Check its content
expect(screen.getAllByRole('button', { name: 'Issue on file' })).toHaveLength(2); // there will be 2 buttons one in concise issue and other in code viewer
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueDetails.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueDetails.tsx
index b6f788ab3d0..0179cd64c9a 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssueDetails.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssueDetails.tsx
@@ -21,6 +21,7 @@
import styled from '@emotion/styled';
import { Spinner } from '@sonarsource/echoes-react';
import { Helmet } from 'react-helmet-async';
+import { useIntl } from 'react-intl';
import {
FlagMessage,
LargeCenteredLayout,
@@ -33,7 +34,7 @@ import ScreenPositionHelper from '../../../components/common/ScreenPositionHelpe
import { IssueSuggestionCodeTab } from '../../../components/rules/IssueSuggestionCodeTab';
import IssueTabViewer from '../../../components/rules/IssueTabViewer';
import { fillBranchLike } from '../../../helpers/branch-like';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
import { useRuleDetailsQuery } from '../../../queries/rules';
import A11ySkipTarget from '../../../sonar-aligned/components/a11y/A11ySkipTarget';
import { isPortfolioLike } from '../../../sonar-aligned/helpers/component';
@@ -82,6 +83,8 @@ export default function IssueDetails({
const { data: ruleData, isLoading: isLoadingRule } = useRuleDetailsQuery({ key: openIssue.rule });
const openRuleDetails = ruleData?.rule;
+ const intl = useIntl();
+
const warning = !canBrowseAllChildProjects && isPortfolioLike(qualifier) && (
<FlagMessage
className="it__portfolio_warning sw-flex"
@@ -100,9 +103,9 @@ export default function IssueDetails({
<Helmet
defer={false}
title={openIssue.message}
- titleTemplate={translateWithParameters(
- 'page_title.template.with_category',
- translate('issues.page'),
+ titleTemplate={intl.formatMessage(
+ { id: 'page_title.template.with_category' },
+ { page: translate('issues.page') },
)}
/>
<h1 className="sw-sr-only">{translate('issues.page')}</h1>
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
index 99d2cccca84..cc01e0de5f0 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
@@ -165,7 +165,7 @@ export default class IssueHeader extends React.PureComponent<Props, State> {
{isExternal ? (
<span>({key})</span>
) : (
- <Link to={getRuleUrl(key)} target="_blank">
+ <Link to={getRuleUrl(key)} shouldOpenInNewTab>
{key}
</Link>
)}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/TotalEffort.tsx b/server/sonar-web/src/main/js/apps/issues/components/TotalEffort.tsx
index 155bbc8878b..4a2111cf2fe 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/TotalEffort.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/TotalEffort.tsx
@@ -20,13 +20,11 @@
import { FormattedMessage } from 'react-intl';
import { formatMeasure } from '~sonar-aligned/helpers/measures';
-import { translate } from '../../../helpers/l10n';
export default function TotalEffort({ effort }: { effort: number }) {
return (
<div className="sw-inline-block">
<FormattedMessage
- defaultMessage={translate('issue.x_effort')}
id="issue.x_effort"
values={{ 0: <strong>{formatMeasure(effort, 'WORK_DUR')}</strong> }}
/>
diff --git a/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchListRow.tsx b/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchListRow.tsx
index d14f7713ba4..c644d5e774c 100644
--- a/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchListRow.tsx
+++ b/server/sonar-web/src/main/js/apps/projectNewCode/components/BranchListRow.tsx
@@ -26,6 +26,7 @@ import {
IconMoreVertical,
Tooltip,
} from '@sonarsource/echoes-react';
+import { useIntl } from 'react-intl';
import {
ActionCell,
Badge,
@@ -97,6 +98,8 @@ function referenceBranchDoesNotExist(
export default function BranchListRow(props: BranchListRowProps) {
const { branch, existingBranches, inheritedSetting } = props;
+ const intl = useIntl();
+
let settingWarning: string | undefined;
if (branchInheritsItselfAsReference(branch, inheritedSetting)) {
settingWarning = translateWithParameters(
@@ -104,9 +107,11 @@ export default function BranchListRow(props: BranchListRowProps) {
branch.name,
);
} else if (referenceBranchDoesNotExist(branch, existingBranches)) {
- settingWarning = translateWithParameters(
- 'baseline.reference_branch.does_not_exist',
- branch.newCodePeriod?.value ?? '',
+ settingWarning = intl.formatMessage(
+ {
+ id: 'baseline.reference_branch.does_not_exist',
+ },
+ { branch: branch.newCodePeriod?.value ?? '' },
);
}
diff --git a/server/sonar-web/src/main/js/apps/projectNewCode/components/NewCodeDefinitionSettingReferenceBranch.tsx b/server/sonar-web/src/main/js/apps/projectNewCode/components/NewCodeDefinitionSettingReferenceBranch.tsx
index 10d64eb50c1..1b2456a3c11 100644
--- a/server/sonar-web/src/main/js/apps/projectNewCode/components/NewCodeDefinitionSettingReferenceBranch.tsx
+++ b/server/sonar-web/src/main/js/apps/projectNewCode/components/NewCodeDefinitionSettingReferenceBranch.tsx
@@ -18,12 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { FormattedMessage } from 'react-intl';
import { MenuPlacement, OptionProps, components } from 'react-select';
import { Badge, FlagErrorIcon, FormField, InputSelect, SelectionCard } from '~design-system';
import Tooltip from '../../../components/controls/Tooltip';
import { NewCodeDefinitionLevels } from '../../../components/new-code-definition/utils';
import MandatoryFieldsExplanation from '../../../components/ui/MandatoryFieldsExplanation';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
import { NewCodeDefinitionType } from '../../../types/new-code-definition';
export interface BaselineSettingReferenceBranchProps {
@@ -60,10 +61,12 @@ function renderBranchOption(props: OptionProps<BranchOption, false>) {
<components.Option {...props}>
{option.isInvalid ? (
<Tooltip
- content={translateWithParameters(
- 'baseline.reference_branch.does_not_exist',
- option.value,
- )}
+ content={
+ <FormattedMessage
+ id="baseline.reference_branch.does_not_exist"
+ values={{ branch: option.value }}
+ />
+ }
>
<span>
{option.value} <FlagErrorIcon className="sw-ml-2" />
diff --git a/server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/ProjectNewCodeDefinitionApp-it.tsx b/server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/ProjectNewCodeDefinitionApp-it.tsx
index 8758e89fc38..e2a7bc5e9a8 100644
--- a/server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/ProjectNewCodeDefinitionApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/projectNewCode/components/__tests__/ProjectNewCodeDefinitionApp-it.tsx
@@ -394,6 +394,7 @@ function getPageObjects() {
branchNCDsBanner: byText(/new_code_definition.auto_update.branch.message/),
dismissButton: byLabelText('dismiss'),
baselineSpecificAnalysisDate: byText(/January 10, 2018/),
+ missingReferenceBranchWarning: byText('baseline.reference_branch.does_not_exist'),
};
async function appIsLoaded() {
diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
index 9ea67fc2774..93f608d26cd 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
@@ -164,7 +164,6 @@ function renderFirstLine(
<span className="sw-typo-semibold" title={formattedAnalysisDate}>
<FormattedMessage
id="projects.last_analysis_on_x"
- defaultMessage={translate('projects.last_analysis_on_x')}
values={{
date: <DateFromNow className="sw-typo-default" date={analysisDate} />,
}}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx
index ad5d7b1510f..0a1b3363e22 100644
--- a/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-gates/components/App.tsx
@@ -22,6 +22,7 @@ import { withTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useCallback, useEffect } from 'react';
import { Helmet } from 'react-helmet-async';
+import { useIntl } from 'react-intl';
import { useNavigate, useParams } from 'react-router-dom';
import {
Card,
@@ -36,7 +37,7 @@ import {
import Suggestions from '../../../components/embed-docs-modal/Suggestions';
import '../../../components/search-navigator.css';
import { DocLink } from '../../../helpers/doc-links';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
import { getQualityGateUrl } from '../../../helpers/urls';
import { useQualityGatesQuery } from '../../../queries/quality-gates';
import { QualityGate } from '../../../types/types';
@@ -47,6 +48,7 @@ import ListHeader from './ListHeader';
export default function App() {
const { data, isLoading } = useQualityGatesQuery();
+ const intl = useIntl();
const { name } = useParams();
const navigate = useNavigate();
const {
@@ -79,9 +81,9 @@ export default function App() {
<PageContentFontWrapper className="sw-typo-default">
<Helmet
defer={false}
- titleTemplate={translateWithParameters(
- 'page_title.template.with_category',
- translate('quality_gates.page'),
+ titleTemplate={intl.formatMessage(
+ { id: 'page_title.template.with_category' },
+ { page: translate('quality_gates.page') },
)}
/>
<div className="sw-grid sw-gap-x-12 sw-gap-y-6 sw-grid-cols-12 sw-w-full">
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx
index f8c72d29f8d..a47c1873b6c 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx
@@ -20,9 +20,10 @@
import * as React from 'react';
import { Helmet } from 'react-helmet-async';
+import { useIntl } from 'react-intl';
import { Outlet, useSearchParams } from 'react-router-dom';
import { useLocation } from '~sonar-aligned/components/hoc/withRouter';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
import ProfileHeader from '../details/ProfileHeader';
import { useQualityProfilesContext } from '../qualityProfilesContext';
import ProfileNotFound from './ProfileNotFound';
@@ -36,6 +37,8 @@ export default function ProfileContainer() {
const context = useQualityProfilesContext();
const { profiles } = context;
+ const intl = useIntl();
+
// try to find a quality profile with the given key
// if managed to find one, redirect to a new version
// otherwise show not found page
@@ -63,9 +66,9 @@ export default function ProfileContainer() {
<Helmet
defer={false}
title={profile.name}
- titleTemplate={translateWithParameters(
- 'page_title.template.with_category',
- translate('quality_profiles.page'),
+ titleTemplate={intl.formatMessage(
+ { id: 'page_title.template.with_category' },
+ { page: translate('quality_profiles.page') },
)}
/>
<ProfileHeader
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx b/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx
index e44730a8428..6b1220bcf68 100644
--- a/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx
+++ b/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx
@@ -19,8 +19,9 @@
*/
import styled from '@emotion/styled';
-import { Spinner } from '@sonarsource/echoes-react';
+import { LogoSize, LogoSonar, Spinner } from '@sonarsource/echoes-react';
import { Helmet } from 'react-helmet-async';
+import { FormattedMessage } from 'react-intl';
import {
Card,
FlagMessage,
@@ -31,8 +32,8 @@ import {
themeBorder,
themeColor,
} from '~design-system';
-import { Image } from '~sonar-aligned/components/common/Image';
import { Location } from '~sonar-aligned/types/router';
+import { SonarQubeProductLogo } from '../../../components/branding/SonarQubeProductLogo';
import { translate } from '../../../helpers/l10n';
import { getReturnUrl } from '../../../helpers/urls';
import { IdentityProvider } from '../../../types/types';
@@ -53,13 +54,15 @@ export default function Login(props: Readonly<LoginProps>) {
const displayError = Boolean(location.query.authorizationError);
return (
- <div className="sw-flex sw-flex-col sw-items-center" id="login_form">
+ <div className="sw-flex sw-flex-col sw-items-center sw-pt-32" id="login_form">
<Helmet defer={false} title={translate('login.page')} />
- <Image alt="" className="sw-mt-32" src="/images/sonar-logo-horizontal.png" />
+ <LogoSonar hasText size={LogoSize.Large} />
<Card className="sw-my-14 sw-p-0 sw-w-abs-350">
<PageContentFontWrapper className="sw-typo-lg sw-flex sw-flex-col sw-items-center sw-py-8 sw-px-4">
- <Image alt="" className="sw-mb-6" src="/images/embed-doc/sq-icon.svg" width={28} />
- <Title className="sw-mb-6">{translate('login.login_to_sonarqube')}</Title>
+ <SonarQubeProductLogo size={LogoSize.Small} />
+ <Title className="sw-my-6 sw-text-center">
+ <FormattedMessage id="login.login_to_sonarqube" />
+ </Title>
<Spinner isLoading={loading}>
<>
{displayError && (
diff --git a/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
index 530c8a9baa3..6e53ac929b9 100644
--- a/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
+++ b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.ts
@@ -40,6 +40,14 @@ jest.mock('../../../helpers/l10n', () => ({
hasMessage: jest.fn(),
}));
+jest.mock('../../../helpers/l10nBundle', () => {
+ const bundle = jest.requireActual('../../../helpers/l10nBundle');
+ return {
+ ...bundle,
+ getIntl: () => ({ formatMessage: jest.fn(({ id }) => `${id}`) }),
+ };
+});
+
const fields = [
{ key: 'foo', type: 'STRING' } as SettingFieldDefinition,
{ key: 'bar', type: 'SINGLE_SELECT_LIST' } as SettingFieldDefinition,
diff --git a/server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx b/server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx
index f15e9e0128d..bd30797fb90 100644
--- a/server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/system/components/__tests__/SystemApp-it.tsx
@@ -23,8 +23,10 @@ import userEvent from '@testing-library/user-event';
import { first } from 'lodash';
import { byRole, byText } from '~sonar-aligned/helpers/testSelector';
import SystemServiceMock from '../../../../api/mocks/SystemServiceMock';
+import { mockAppState } from '../../../../helpers/testMocks';
import { renderAppRoutes } from '../../../../helpers/testReactTestingUtils';
import { AppState } from '../../../../types/appstate';
+import { EditionKey } from '../../../../types/editions';
import routes from '../../routes';
import { LogsLevels } from '../../utils';
@@ -125,7 +127,9 @@ describe('System Info Cluster', () => {
});
function renderSystemApp(appState?: AppState) {
- return renderAppRoutes('system', routes, { appState });
+ return renderAppRoutes('system', routes, {
+ appState: mockAppState({ edition: EditionKey.developer, ...appState }),
+ });
}
function getPageObjects() {
diff --git a/server/sonar-web/src/main/js/components/branding/SonarQubeConnectionIllustration.tsx b/server/sonar-web/src/main/js/components/branding/SonarQubeConnectionIllustration.tsx
new file mode 100644
index 00000000000..11c7b8308be
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/branding/SonarQubeConnectionIllustration.tsx
@@ -0,0 +1,199 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { useAppState } from '../../app/components/app-state/withAppStateContext';
+import { EditionKey } from '../../types/editions';
+
+interface Props {
+ className?: string;
+ connected: boolean;
+}
+
+/**
+ * This component switches between the Community and Server product versions' logo,
+ * and between the connected and requested versions of the illustration
+ */
+export function SonarQubeConnectionIllustration(props: Props) {
+ const { edition } = useAppState();
+
+ return edition === EditionKey.community ? (
+ <CommunityIllustration {...props} />
+ ) : (
+ <ServerIllustration {...props} />
+ );
+}
+
+function CommunityIllustration({ connected, ...imageProps }: Props) {
+ return connected ? (
+ <svg
+ aria-hidden
+ width="179"
+ height="98"
+ viewBox="0 0 179 98"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ {...imageProps}
+ >
+ <path
+ fillRule="evenodd"
+ clipRule="evenodd"
+ d="M72.005 26h-50.01c-8.836 0-16 7.163-16 16v40c0 8.837 7.164 16 16 16h135c8.837 0 16-7.163 16-16V42c0-8.837-7.163-16-16-16h-57.4l-2 2h59.4c7.732 0 14 6.268 14 14v40c0 7.732-6.268 14-14 14h-135c-7.732 0-14-6.268-14-14V42c0-7.732 6.268-14 14-14h52.01l-2-2Z"
+ fill="var(--echoes-color-icon-success)"
+ />
+ <path
+ d="M66.545 64.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM46.355 83.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM32.045 48.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M90.316 63.612h5.232v1.632h-5.232V70.5h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <path
+ d="M155.444 63.552A1.55 1.55 0 0 1 153.892 62c0-11.524-9.373-20.897-20.897-20.897a1.55 1.55 0 0 1-1.551-1.551A1.55 1.55 0 0 1 132.995 38c13.231 0 24 10.769 24 24a1.55 1.55 0 0 1-1.551 1.552ZM132.995 86c-13.231 0-24-10.769-24-24a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 112.099 62c0 11.524 9.372 20.897 20.896 20.897a1.55 1.55 0 0 1 1.552 1.551A1.55 1.55 0 0 1 132.995 86Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M147.582 63.552A1.55 1.55 0 0 1 146.03 62c0-7.19-5.845-13.035-13.035-13.035a1.55 1.55 0 0 1-1.551-1.551 1.55 1.55 0 0 1 1.551-1.552c8.897 0 16.138 7.241 16.138 16.138a1.55 1.55 0 0 1-1.551 1.552ZM132.995 78.138c-8.896 0-16.138-7.242-16.138-16.138a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 119.961 62c0 7.19 5.845 13.034 13.034 13.034a1.55 1.55 0 0 1 1.552 1.552 1.55 1.55 0 0 1-1.552 1.552ZM133.016 70.286a8.254 8.254 0 0 1-5.855-2.42c-3.228-3.228-3.228-8.483 0-11.721a8.232 8.232 0 0 1 5.855-2.431 8.19 8.19 0 0 1 5.855 2.43c.61.611.61 1.594 0 2.194-.61.61-1.593.61-2.193 0a5.133 5.133 0 0 0-3.662-1.52c-1.386 0-2.69.537-3.662 1.52a5.189 5.189 0 0 0 0 7.324 5.188 5.188 0 0 0 7.324 0c.61-.61 1.593-.61 2.193 0 .6.61.61 1.593 0 2.193a8.254 8.254 0 0 1-5.855 2.42v.011Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M110.658 4.837a2.861 2.861 0 0 1 0 4.044L87.804 31.736a2.861 2.861 0 0 1-4.044 0L72.332 20.308a2.861 2.861 0 0 1 0-4.044 2.861 2.861 0 0 1 4.044 0l9.41 9.401 20.837-20.828a2.86 2.86 0 0 1 4.044 0h-.009Z"
+ fill="var(--echoes-color-icon-success)"
+ />
+ </svg>
+ ) : (
+ <svg
+ aria-hidden
+ width="179"
+ height="98"
+ viewBox="0 0 179 98"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ {...imageProps}
+ >
+ <path
+ d="M35.076 27H80.745L64.703 97H16C7.716 97 1 90.284 1 82V42c0-8.284 6.716-15 15-15h19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ strokeWidth="2"
+ />
+ <path
+ d="M56.55 64.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM36.36 83.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM22.05 48.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M90.32 63.612h5.233v1.632H90.32V70.5h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <path
+ d="M143.924 97H98.255l16.042-70H163c8.284 0 15 6.716 15 15v40c0 8.284-6.716 15-15 15h-19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ strokeWidth="2"
+ />
+ <path
+ d="M165.448 62.552A1.55 1.55 0 0 1 163.897 61c0-11.524-9.373-20.897-20.897-20.897a1.55 1.55 0 0 1-1.552-1.551A1.55 1.55 0 0 1 143 37c13.231 0 24 10.769 24 24a1.55 1.55 0 0 1-1.552 1.552ZM143 85c-13.231 0-24-10.769-24-24a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 122.103 61c0 11.524 9.373 20.897 20.897 20.897a1.55 1.55 0 0 1 1.552 1.551A1.55 1.55 0 0 1 143 85Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M157.586 62.552A1.55 1.55 0 0 1 156.034 61c0-7.19-5.844-13.035-13.034-13.035a1.55 1.55 0 0 1-1.552-1.551A1.55 1.55 0 0 1 143 44.862c8.897 0 16.138 7.241 16.138 16.138a1.55 1.55 0 0 1-1.552 1.552ZM143 77.138c-8.897 0-16.138-7.242-16.138-16.138a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 129.966 61c0 7.19 5.844 13.034 13.034 13.034a1.55 1.55 0 0 1 1.552 1.552A1.55 1.55 0 0 1 143 77.138ZM143.021 69.286a8.252 8.252 0 0 1-5.855-2.42c-3.228-3.228-3.228-8.483 0-11.721a8.232 8.232 0 0 1 5.855-2.431c2.213 0 4.293.858 5.855 2.43.61.611.61 1.594 0 2.194-.61.61-1.593.61-2.193 0a5.133 5.133 0 0 0-3.662-1.52c-1.387 0-2.69.537-3.662 1.52a5.187 5.187 0 0 0 0 7.324 5.188 5.188 0 0 0 7.324 0c.61-.61 1.593-.61 2.193 0 .6.61.61 1.593 0 2.193a8.256 8.256 0 0 1-5.855 2.42v.011Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ </svg>
+ );
+}
+
+function ServerIllustration({ connected, ...imageProps }: Props) {
+ return connected ? (
+ <svg
+ aria-hidden
+ width="179"
+ height="98"
+ viewBox="0 0 179 98"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ {...imageProps}
+ >
+ <path
+ fillRule="evenodd"
+ clipRule="evenodd"
+ d="M72.005 26h-50.01c-8.836 0-16 7.163-16 16v40c0 8.837 7.164 16 16 16h135c8.837 0 16-7.163 16-16V42c0-8.837-7.163-16-16-16h-57.4l-2 2h59.4c7.732 0 14 6.268 14 14v40c0 7.732-6.268 14-14 14h-135c-7.732 0-14-6.268-14-14V42c0-7.732 6.268-14 14-14h52.01l-2-2Z"
+ fill="var(--echoes-color-icon-success)"
+ />
+ <path
+ d="M66.545 64.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM46.355 83.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM32.045 48.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M90.316 63.612h5.232v1.632h-5.232V70.5h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <g clipPath="url(#sqciok)" fill="var(--echoes-logos-colors-brand)">
+ <path d="M155.268 63.54a1.54 1.54 0 0 1-1.539-1.54c0-11.433-9.301-20.733-20.734-20.733a1.54 1.54 0 0 1 0-3.079c13.131 0 23.814 10.682 23.814 23.812 0 .85-.69 1.54-1.54 1.54h-.001ZM132.994 85.812c-13.129 0-23.811-10.681-23.811-23.812a1.54 1.54 0 0 1 3.079 0c0 11.433 9.3 20.733 20.732 20.733a1.54 1.54 0 1 1 0 3.079ZM139.677 63.54c-.85 0-1.54-.69-1.54-1.54a5.147 5.147 0 0 0-5.142-5.141 1.54 1.54 0 0 1 0-3.079c4.534 0 8.222 3.688 8.222 8.22 0 .851-.69 1.54-1.539 1.54h-.001ZM132.994 70.222c-4.532 0-8.221-3.688-8.221-8.222a1.54 1.54 0 0 1 3.079 0 5.147 5.147 0 0 0 5.141 5.142 1.54 1.54 0 1 1 0 3.079h.001Z" />
+ <path d="M147.472 63.54a1.54 1.54 0 0 1-1.539-1.54c0-7.133-5.804-12.937-12.939-12.937a1.54 1.54 0 0 1 0-3.078c8.833 0 16.018 7.185 16.018 16.016 0 .85-.69 1.54-1.54 1.54v-.002ZM132.994 78.017c-8.831 0-16.016-7.185-16.016-16.017a1.54 1.54 0 0 1 3.079 0c0 7.134 5.804 12.938 12.936 12.938a1.54 1.54 0 1 1 0 3.08h.001Z" />
+ </g>
+ <path
+ d="M110.658 4.837a2.861 2.861 0 0 1 0 4.044L87.804 31.736a2.861 2.861 0 0 1-4.044 0L72.332 20.308a2.861 2.861 0 0 1 0-4.044 2.861 2.861 0 0 1 4.044 0l9.41 9.401 20.837-20.828a2.86 2.86 0 0 1 4.044 0h-.009Z"
+ fill="var(--echoes-color-icon-success)"
+ />
+ <defs>
+ <clipPath id="sqciok">
+ <path fill="#fff" transform="translate(108.995 38)" d="M0 0h48v48H0z" />
+ </clipPath>
+ </defs>
+ </svg>
+ ) : (
+ <svg
+ aria-hidden
+ width="179"
+ height="98"
+ viewBox="0 0 179 98"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ {...imageProps}
+ >
+ <path
+ d="M35.076 27H80.745L64.703 97H16C7.716 97 1 90.284 1 82V42c0-8.284 6.716-15 15-15h19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ strokeWidth="2"
+ />
+ <path
+ d="M56.55 64.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM36.36 83.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM22.05 48.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M90.32 63.612h5.233v1.632H90.32V70.5h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <path
+ d="M143.924 97H98.255l16.042-70H163c8.284 0 15 6.716 15 15v40c0 8.284-6.716 15-15 15h-19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ strokeWidth="2"
+ />
+ <g clipPath="url(#sqcireq)" fill="var(--echoes-logos-colors-brand)">
+ <path d="M165.273 63.54a1.54 1.54 0 0 1-1.539-1.54c0-11.433-9.301-20.733-20.734-20.733a1.54 1.54 0 0 1 0-3.079c13.131 0 23.813 10.682 23.813 23.812 0 .85-.69 1.54-1.539 1.54h-.001ZM142.999 85.812c-13.13 0-23.811-10.681-23.811-23.812a1.54 1.54 0 1 1 3.078 0c0 11.433 9.3 20.733 20.733 20.733a1.54 1.54 0 0 1 0 3.079ZM149.682 63.54a1.54 1.54 0 0 1-1.54-1.54A5.147 5.147 0 0 0 143 56.859a1.54 1.54 0 0 1 0-3.079c4.534 0 8.222 3.688 8.222 8.22 0 .851-.69 1.54-1.54 1.54ZM142.999 70.222c-4.533 0-8.221-3.688-8.221-8.222a1.54 1.54 0 0 1 3.079 0 5.147 5.147 0 0 0 5.141 5.142 1.54 1.54 0 0 1 0 3.079h.001Z" />
+ <path d="M157.477 63.54c-.85 0-1.54-.69-1.54-1.54 0-7.133-5.804-12.937-12.938-12.937a1.54 1.54 0 0 1 0-3.078c8.832 0 16.017 7.185 16.017 16.016 0 .85-.69 1.54-1.539 1.54v-.002ZM142.999 78.017c-8.831 0-16.016-7.185-16.016-16.017a1.54 1.54 0 0 1 3.079 0c0 7.134 5.804 12.938 12.936 12.938a1.54 1.54 0 0 1 0 3.08h.001Z" />
+ </g>
+ <defs>
+ <clipPath id="sqcireq">
+ <path fill="#fff" transform="translate(119 38)" d="M0 0h48v48H0z" />
+ </clipPath>
+ </defs>
+ </svg>
+ );
+}
diff --git a/server/sonar-web/src/main/js/components/branding/SonarQubeIDEPromotionIllustration.tsx b/server/sonar-web/src/main/js/components/branding/SonarQubeIDEPromotionIllustration.tsx
new file mode 100644
index 00000000000..3e4ea0bc9db
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/branding/SonarQubeIDEPromotionIllustration.tsx
@@ -0,0 +1,107 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { useAppState } from '../../app/components/app-state/withAppStateContext';
+import { EditionKey } from '../../types/editions';
+
+interface Props {
+ className?: string;
+}
+/**
+ * This component switches between the Community and Server product versions' logo
+ */
+export function SonarQubeIDEPromotionIllustration({ className }: Props) {
+ const { edition } = useAppState();
+
+ return edition === EditionKey.community ? (
+ <svg
+ aria-hidden
+ height="120"
+ viewBox="0 0 82 173"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ className={className}
+ >
+ <path
+ d="M46.924 71H1.255L17.297 1H66c8.284 0 15 6.716 15 15v40c0 8.284-6.716 15-15 15H46.924Z"
+ stroke="var(--echoes-color-border-weak)"
+ strokeWidth="2"
+ />
+ <path
+ d="M68.448 36.552A1.55 1.55 0 0 1 66.897 35c0-11.524-9.373-20.897-20.897-20.897a1.55 1.55 0 0 1-1.552-1.551A1.55 1.55 0 0 1 46 11c13.231 0 24 10.769 24 24a1.55 1.55 0 0 1-1.552 1.552ZM46 59c-13.231 0-24-10.769-24-24a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 25.103 35c0 11.524 9.373 20.897 20.897 20.897a1.55 1.55 0 0 1 1.552 1.551A1.55 1.55 0 0 1 46 59Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M60.586 36.552A1.55 1.55 0 0 1 59.035 35c0-7.19-5.845-13.035-13.035-13.035a1.55 1.55 0 0 1-1.552-1.551A1.55 1.55 0 0 1 46 18.862c8.897 0 16.138 7.241 16.138 16.138a1.55 1.55 0 0 1-1.552 1.552ZM46 51.138c-8.897 0-16.138-7.241-16.138-16.138a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 32.965 35c0 7.19 5.845 13.035 13.035 13.035a1.55 1.55 0 0 1 1.552 1.551A1.55 1.55 0 0 1 46 51.138ZM46.02 43.286a8.255 8.255 0 0 1-5.855-2.42c-3.227-3.228-3.227-8.483 0-11.721a8.233 8.233 0 0 1 5.856-2.431 8.19 8.19 0 0 1 5.855 2.43c.61.611.61 1.594 0 2.194-.61.61-1.593.61-2.193 0a5.133 5.133 0 0 0-3.662-1.52c-1.386 0-2.69.537-3.662 1.52a5.189 5.189 0 0 0 0 7.324 5.189 5.189 0 0 0 7.324 0c.61-.61 1.593-.61 2.193 0 .6.61.61 1.593 0 2.193a8.255 8.255 0 0 1-5.855 2.42v.011Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M41.82 88.112h5.233v1.632H41.82V95h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <path
+ d="M35.076 102H80.745l-16.042 70H16c-8.284 0-15-6.716-15-15v-40c0-8.284 6.716-15 15-15h19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ strokeWidth="2"
+ />
+ <path
+ d="M56.55 139.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM36.36 158.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM22.05 123.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ </svg>
+ ) : (
+ <svg
+ aria-hidden
+ height="120"
+ viewBox="0 0 82 173"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ className={className}
+ >
+ <path
+ d="M46.924 71H1.255L17.297 1H66c8.284 0 15 6.716 15 15v40c0 8.284-6.716 15-15 15H46.924Z"
+ stroke="var(--echoes-color-border-weak)"
+ strokeWidth="2"
+ />
+ <g clipPath="url(#sqidepi)" fill="var(--echoes-logos-colors-brand)">
+ <path d="M68.273 37.54c-.85 0-1.54-.69-1.54-1.54 0-11.433-9.3-20.733-20.733-20.733a1.54 1.54 0 0 1 0-3.079c13.13 0 23.813 10.682 23.813 23.812 0 .85-.69 1.54-1.539 1.54ZM46 59.812C32.87 59.812 22.186 49.13 22.186 36a1.54 1.54 0 0 1 3.08 0c0 11.433 9.3 20.733 20.732 20.733a1.54 1.54 0 0 1 0 3.079ZM52.682 37.54c-.85 0-1.54-.69-1.54-1.54A5.147 5.147 0 0 0 46 30.859a1.54 1.54 0 0 1 0-3.079c4.534 0 8.222 3.688 8.222 8.22 0 .851-.69 1.54-1.54 1.54ZM46 44.222c-4.534 0-8.222-3.688-8.222-8.222a1.54 1.54 0 0 1 3.079 0 5.147 5.147 0 0 0 5.141 5.142 1.54 1.54 0 0 1 0 3.079H46Z" />
+ <path d="M60.477 37.54c-.85 0-1.54-.69-1.54-1.54 0-7.133-5.804-12.937-12.938-12.937a1.54 1.54 0 0 1 0-3.078c8.832 0 16.017 7.185 16.017 16.016 0 .85-.69 1.54-1.54 1.54v-.002ZM46 52.017c-8.832 0-16.017-7.185-16.017-16.017a1.54 1.54 0 0 1 3.079 0c0 7.134 5.804 12.938 12.936 12.938a1.54 1.54 0 0 1 0 3.08H46Z" />
+ </g>
+ <path
+ d="M41.82 88.112h5.233v1.632H41.82V95h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <path
+ d="M35.076 102H80.745l-16.042 70H16c-8.284 0-15-6.716-15-15v-40c0-8.284 6.716-15 15-15h19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ strokeWidth="2"
+ />
+ <path
+ d="M56.55 139.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM36.36 158.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM22.05 123.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <defs>
+ <clipPath id="sqidepi">
+ <path fill="#fff" transform="translate(22 12)" d="M0 0h48v48H0z" />
+ </clipPath>
+ </defs>
+ </svg>
+ );
+}
diff --git a/server/sonar-web/src/main/js/components/branding/SonarQubeProductLogo.tsx b/server/sonar-web/src/main/js/components/branding/SonarQubeProductLogo.tsx
new file mode 100644
index 00000000000..a9db4c2aa96
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/branding/SonarQubeProductLogo.tsx
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { LogoSonarQubeCommunity, LogoSonarQubeServer } from '@sonarsource/echoes-react';
+import * as React from 'react';
+import { useAppState } from '../../app/components/app-state/withAppStateContext';
+import { EditionKey } from '../../types/editions';
+
+type Props = React.ComponentProps<typeof LogoSonarQubeServer>;
+
+/**
+ * This component switches between the Community and Server product versions' logo
+ */
+export function SonarQubeProductLogo(props: Props) {
+ const { edition } = useAppState();
+
+ const OfficialLogo =
+ edition === EditionKey.community ? LogoSonarQubeCommunity : LogoSonarQubeServer;
+
+ return <OfficialLogo {...props} />;
+}
diff --git a/server/sonar-web/src/main/js/components/branding/__tests__/SonarQubeConnectionIllustration-test.tsx b/server/sonar-web/src/main/js/components/branding/__tests__/SonarQubeConnectionIllustration-test.tsx
new file mode 100644
index 00000000000..b95f33683f1
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/branding/__tests__/SonarQubeConnectionIllustration-test.tsx
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { mockAppState } from '../../../helpers/testMocks';
+import { renderComponent } from '../../../helpers/testReactTestingUtils';
+import { EditionKey } from '../../../types/editions';
+import { SonarQubeConnectionIllustration } from '../SonarQubeConnectionIllustration';
+
+it.each([
+ [EditionKey.community, true],
+ [EditionKey.community, false],
+ [EditionKey.enterprise, true],
+ [EditionKey.enterprise, false],
+])('should render %s edition (variant connected %s) correctly', (edition, connected) => {
+ const { container } = renderComponent(
+ <SonarQubeConnectionIllustration connected={connected} />,
+ '',
+ { appState: mockAppState({ edition }) },
+ );
+
+ expect(container).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/branding/__tests__/__snapshots__/SonarQubeConnectionIllustration-test.tsx.snap b/server/sonar-web/src/main/js/components/branding/__tests__/__snapshots__/SonarQubeConnectionIllustration-test.tsx.snap
new file mode 100644
index 00000000000..400a3b1350d
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/branding/__tests__/__snapshots__/SonarQubeConnectionIllustration-test.tsx.snap
@@ -0,0 +1,189 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render community edition (variant connected false) correctly 1`] = `
+<div>
+ <svg
+ aria-hidden="true"
+ fill="none"
+ height="98"
+ viewBox="0 0 179 98"
+ width="179"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M35.076 27H80.745L64.703 97H16C7.716 97 1 90.284 1 82V42c0-8.284 6.716-15 15-15h19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ stroke-width="2"
+ />
+ <path
+ d="M56.55 64.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM36.36 83.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM22.05 48.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M90.32 63.612h5.233v1.632H90.32V70.5h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <path
+ d="M143.924 97H98.255l16.042-70H163c8.284 0 15 6.716 15 15v40c0 8.284-6.716 15-15 15h-19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ stroke-width="2"
+ />
+ <path
+ d="M165.448 62.552A1.55 1.55 0 0 1 163.897 61c0-11.524-9.373-20.897-20.897-20.897a1.55 1.55 0 0 1-1.552-1.551A1.55 1.55 0 0 1 143 37c13.231 0 24 10.769 24 24a1.55 1.55 0 0 1-1.552 1.552ZM143 85c-13.231 0-24-10.769-24-24a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 122.103 61c0 11.524 9.373 20.897 20.897 20.897a1.55 1.55 0 0 1 1.552 1.551A1.55 1.55 0 0 1 143 85Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M157.586 62.552A1.55 1.55 0 0 1 156.034 61c0-7.19-5.844-13.035-13.034-13.035a1.55 1.55 0 0 1-1.552-1.551A1.55 1.55 0 0 1 143 44.862c8.897 0 16.138 7.241 16.138 16.138a1.55 1.55 0 0 1-1.552 1.552ZM143 77.138c-8.897 0-16.138-7.242-16.138-16.138a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 129.966 61c0 7.19 5.844 13.034 13.034 13.034a1.55 1.55 0 0 1 1.552 1.552A1.55 1.55 0 0 1 143 77.138ZM143.021 69.286a8.252 8.252 0 0 1-5.855-2.42c-3.228-3.228-3.228-8.483 0-11.721a8.232 8.232 0 0 1 5.855-2.431c2.213 0 4.293.858 5.855 2.43.61.611.61 1.594 0 2.194-.61.61-1.593.61-2.193 0a5.133 5.133 0 0 0-3.662-1.52c-1.387 0-2.69.537-3.662 1.52a5.187 5.187 0 0 0 0 7.324 5.188 5.188 0 0 0 7.324 0c.61-.61 1.593-.61 2.193 0 .6.61.61 1.593 0 2.193a8.256 8.256 0 0 1-5.855 2.42v.011Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ </svg>
+</div>
+`;
+
+exports[`should render community edition (variant connected true) correctly 1`] = `
+<div>
+ <svg
+ aria-hidden="true"
+ fill="none"
+ height="98"
+ viewBox="0 0 179 98"
+ width="179"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ clip-rule="evenodd"
+ d="M72.005 26h-50.01c-8.836 0-16 7.163-16 16v40c0 8.837 7.164 16 16 16h135c8.837 0 16-7.163 16-16V42c0-8.837-7.163-16-16-16h-57.4l-2 2h59.4c7.732 0 14 6.268 14 14v40c0 7.732-6.268 14-14 14h-135c-7.732 0-14-6.268-14-14V42c0-7.732 6.268-14 14-14h52.01l-2-2Z"
+ fill="var(--echoes-color-icon-success)"
+ fill-rule="evenodd"
+ />
+ <path
+ d="M66.545 64.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM46.355 83.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM32.045 48.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M90.316 63.612h5.232v1.632h-5.232V70.5h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <path
+ d="M155.444 63.552A1.55 1.55 0 0 1 153.892 62c0-11.524-9.373-20.897-20.897-20.897a1.55 1.55 0 0 1-1.551-1.551A1.55 1.55 0 0 1 132.995 38c13.231 0 24 10.769 24 24a1.55 1.55 0 0 1-1.551 1.552ZM132.995 86c-13.231 0-24-10.769-24-24a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 112.099 62c0 11.524 9.372 20.897 20.896 20.897a1.55 1.55 0 0 1 1.552 1.551A1.55 1.55 0 0 1 132.995 86Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M147.582 63.552A1.55 1.55 0 0 1 146.03 62c0-7.19-5.845-13.035-13.035-13.035a1.55 1.55 0 0 1-1.551-1.551 1.55 1.55 0 0 1 1.551-1.552c8.897 0 16.138 7.241 16.138 16.138a1.55 1.55 0 0 1-1.551 1.552ZM132.995 78.138c-8.896 0-16.138-7.242-16.138-16.138a1.55 1.55 0 0 1 1.552-1.552A1.55 1.55 0 0 1 119.961 62c0 7.19 5.845 13.034 13.034 13.034a1.55 1.55 0 0 1 1.552 1.552 1.55 1.55 0 0 1-1.552 1.552ZM133.016 70.286a8.254 8.254 0 0 1-5.855-2.42c-3.228-3.228-3.228-8.483 0-11.721a8.232 8.232 0 0 1 5.855-2.431 8.19 8.19 0 0 1 5.855 2.43c.61.611.61 1.594 0 2.194-.61.61-1.593.61-2.193 0a5.133 5.133 0 0 0-3.662-1.52c-1.386 0-2.69.537-3.662 1.52a5.189 5.189 0 0 0 0 7.324 5.188 5.188 0 0 0 7.324 0c.61-.61 1.593-.61 2.193 0 .6.61.61 1.593 0 2.193a8.254 8.254 0 0 1-5.855 2.42v.011Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M110.658 4.837a2.861 2.861 0 0 1 0 4.044L87.804 31.736a2.861 2.861 0 0 1-4.044 0L72.332 20.308a2.861 2.861 0 0 1 0-4.044 2.861 2.861 0 0 1 4.044 0l9.41 9.401 20.837-20.828a2.86 2.86 0 0 1 4.044 0h-.009Z"
+ fill="var(--echoes-color-icon-success)"
+ />
+ </svg>
+</div>
+`;
+
+exports[`should render enterprise edition (variant connected false) correctly 1`] = `
+<div>
+ <svg
+ aria-hidden="true"
+ fill="none"
+ height="98"
+ viewBox="0 0 179 98"
+ width="179"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M35.076 27H80.745L64.703 97H16C7.716 97 1 90.284 1 82V42c0-8.284 6.716-15 15-15h19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ stroke-width="2"
+ />
+ <path
+ d="M56.55 64.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM36.36 83.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM22.05 48.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M90.32 63.612h5.233v1.632H90.32V70.5h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <path
+ d="M143.924 97H98.255l16.042-70H163c8.284 0 15 6.716 15 15v40c0 8.284-6.716 15-15 15h-19.076Z"
+ stroke="var(--echoes-color-border-weak)"
+ stroke-width="2"
+ />
+ <g
+ clip-path="url(#sqcireq)"
+ fill="var(--echoes-logos-colors-brand)"
+ >
+ <path
+ d="M165.273 63.54a1.54 1.54 0 0 1-1.539-1.54c0-11.433-9.301-20.733-20.734-20.733a1.54 1.54 0 0 1 0-3.079c13.131 0 23.813 10.682 23.813 23.812 0 .85-.69 1.54-1.539 1.54h-.001ZM142.999 85.812c-13.13 0-23.811-10.681-23.811-23.812a1.54 1.54 0 1 1 3.078 0c0 11.433 9.3 20.733 20.733 20.733a1.54 1.54 0 0 1 0 3.079ZM149.682 63.54a1.54 1.54 0 0 1-1.54-1.54A5.147 5.147 0 0 0 143 56.859a1.54 1.54 0 0 1 0-3.079c4.534 0 8.222 3.688 8.222 8.22 0 .851-.69 1.54-1.54 1.54ZM142.999 70.222c-4.533 0-8.221-3.688-8.221-8.222a1.54 1.54 0 0 1 3.079 0 5.147 5.147 0 0 0 5.141 5.142 1.54 1.54 0 0 1 0 3.079h.001Z"
+ />
+ <path
+ d="M157.477 63.54c-.85 0-1.54-.69-1.54-1.54 0-7.133-5.804-12.937-12.938-12.937a1.54 1.54 0 0 1 0-3.078c8.832 0 16.017 7.185 16.017 16.016 0 .85-.69 1.54-1.539 1.54v-.002ZM142.999 78.017c-8.831 0-16.016-7.185-16.016-16.017a1.54 1.54 0 0 1 3.079 0c0 7.134 5.804 12.938 12.936 12.938a1.54 1.54 0 0 1 0 3.08h.001Z"
+ />
+ </g>
+ <defs>
+ <clippath
+ id="sqcireq"
+ >
+ <path
+ d="M0 0h48v48H0z"
+ fill="#fff"
+ transform="translate(119 38)"
+ />
+ </clippath>
+ </defs>
+ </svg>
+</div>
+`;
+
+exports[`should render enterprise edition (variant connected true) correctly 1`] = `
+<div>
+ <svg
+ aria-hidden="true"
+ fill="none"
+ height="98"
+ viewBox="0 0 179 98"
+ width="179"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ clip-rule="evenodd"
+ d="M72.005 26h-50.01c-8.836 0-16 7.163-16 16v40c0 8.837 7.164 16 16 16h135c8.837 0 16-7.163 16-16V42c0-8.837-7.163-16-16-16h-57.4l-2 2h59.4c7.732 0 14 6.268 14 14v40c0 7.732-6.268 14-14 14h-135c-7.732 0-14-6.268-14-14V42c0-7.732 6.268-14 14-14h52.01l-2-2Z"
+ fill="var(--echoes-color-icon-success)"
+ fill-rule="evenodd"
+ />
+ <path
+ d="M66.545 64.34c0 1.08-1.23 1.71-2.1 1.08-.96-.72-1.56-1.86-2.04-2.76-.66-1.26-1.14-2.01-1.74-2.01-.6 0-1.05.75-1.74 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.74-2.01-.63 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.66-1.26-1.11-2.01-1.71-2.01-.6 0-1.05.75-1.71 2.01-.78 1.47-1.86 3.48-4.14 3.48-2.28 0-3.36-2.01-4.14-3.48-.48-.87-.84-1.53-1.2-1.83-.3-.24-.51-.57-.51-.96v-.21c0-1.08 1.23-1.71 2.1-1.08.96.72 1.56 1.86 2.04 2.76.66 1.26 1.11 2.01 1.71 2.01.6 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.71-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.66 1.26 1.11 2.01 1.74 2.01.63 0 1.05-.75 1.74-2.01.78-1.47 1.86-3.48 4.14-3.48 2.28 0 3.36 2.01 4.14 3.48.48.87.84 1.53 1.23 1.83.3.24.51.57.51.96v.21h-.03ZM46.355 83.18c-4.95 0-9.66-1.71-13.47-4.83-.6-.51-.66-1.44-.12-2.01.51-.54 1.32-.57 1.86-.12 3.42 2.85 7.71 4.32 12.21 4.2 4.5-.12 8.7-1.83 11.97-4.83.54-.48 1.35-.48 1.86 0 .57.54.57 1.47 0 2.01-3.75 3.48-8.61 5.43-13.77 5.58h-.57.03ZM32.045 48.41c-.57-.54-.57-1.47 0-2.01 3.87-3.57 8.79-5.43 13.77-5.55 4.98-.12 9.96 1.47 14.04 4.83.6.51.66 1.44.12 2.01-.51.51-1.32.57-1.86.12-3.51-2.88-7.8-4.29-12.09-4.2-4.29.09-8.67 1.71-12.09 4.83-.54.48-1.35.48-1.86 0l-.03-.03Z"
+ fill="var(--echoes-logos-colors-brand)"
+ />
+ <path
+ d="M90.316 63.612h5.232v1.632h-5.232V70.5h-1.632v-5.256h-5.232v-1.632h5.232v-5.256h1.632v5.256Z"
+ fill="var(--echoes-color-text-subdued)"
+ />
+ <g
+ clip-path="url(#sqciok)"
+ fill="var(--echoes-logos-colors-brand)"
+ >
+ <path
+ d="M155.268 63.54a1.54 1.54 0 0 1-1.539-1.54c0-11.433-9.301-20.733-20.734-20.733a1.54 1.54 0 0 1 0-3.079c13.131 0 23.814 10.682 23.814 23.812 0 .85-.69 1.54-1.54 1.54h-.001ZM132.994 85.812c-13.129 0-23.811-10.681-23.811-23.812a1.54 1.54 0 0 1 3.079 0c0 11.433 9.3 20.733 20.732 20.733a1.54 1.54 0 1 1 0 3.079ZM139.677 63.54c-.85 0-1.54-.69-1.54-1.54a5.147 5.147 0 0 0-5.142-5.141 1.54 1.54 0 0 1 0-3.079c4.534 0 8.222 3.688 8.222 8.22 0 .851-.69 1.54-1.539 1.54h-.001ZM132.994 70.222c-4.532 0-8.221-3.688-8.221-8.222a1.54 1.54 0 0 1 3.079 0 5.147 5.147 0 0 0 5.141 5.142 1.54 1.54 0 1 1 0 3.079h.001Z"
+ />
+ <path
+ d="M147.472 63.54a1.54 1.54 0 0 1-1.539-1.54c0-7.133-5.804-12.937-12.939-12.937a1.54 1.54 0 0 1 0-3.078c8.833 0 16.018 7.185 16.018 16.016 0 .85-.69 1.54-1.54 1.54v-.002ZM132.994 78.017c-8.831 0-16.016-7.185-16.016-16.017a1.54 1.54 0 0 1 3.079 0c0 7.134 5.804 12.938 12.936 12.938a1.54 1.54 0 1 1 0 3.08h.001Z"
+ />
+ </g>
+ <path
+ d="M110.658 4.837a2.861 2.861 0 0 1 0 4.044L87.804 31.736a2.861 2.861 0 0 1-4.044 0L72.332 20.308a2.861 2.861 0 0 1 0-4.044 2.861 2.861 0 0 1 4.044 0l9.41 9.401 20.837-20.828a2.86 2.86 0 0 1 4.044 0h-.009Z"
+ fill="var(--echoes-color-icon-success)"
+ />
+ <defs>
+ <clippath
+ id="sqciok"
+ >
+ <path
+ d="M0 0h48v48H0z"
+ fill="#fff"
+ transform="translate(108.995 38)"
+ />
+ </clippath>
+ </defs>
+ </svg>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/components/embed-docs-modal/EmbedDocsPopup.tsx b/server/sonar-web/src/main/js/components/embed-docs-modal/EmbedDocsPopup.tsx
index 5bffded6ae8..9824cec5a8f 100644
--- a/server/sonar-web/src/main/js/components/embed-docs-modal/EmbedDocsPopup.tsx
+++ b/server/sonar-web/src/main/js/components/embed-docs-modal/EmbedDocsPopup.tsx
@@ -20,41 +20,12 @@
import { DropdownMenu } from '@sonarsource/echoes-react';
import * as React from 'react';
-import { Image } from '~sonar-aligned/components/common/Image';
import { DocLink } from '../../helpers/doc-links';
import { translate } from '../../helpers/l10n';
import { SuggestionLink } from '../../types/types';
import { DocItemLink } from './DocItemLink';
import { SuggestionsContext } from './SuggestionsContext';
-function IconLink({
- icon = 'embed-doc/sq-icon.svg',
- link,
- text,
-}: {
- icon?: string;
- link: string;
- text: string;
-}) {
- return (
- <DropdownMenu.ItemLink
- prefix={
- <Image
- alt={text}
- aria-hidden
- className="sw-mr-2"
- height="18"
- src={`/images/${icon}`}
- width="18"
- />
- }
- to={link}
- >
- {text}
- </DropdownMenu.ItemLink>
- );
-}
-
function Suggestions({ suggestions }: Readonly<{ suggestions: SuggestionLink[] }>) {
return (
<>
@@ -103,21 +74,15 @@ export function EmbedDocsPopup() {
<DropdownMenu.GroupLabel>{translate('docs.stay_connected')}</DropdownMenu.GroupLabel>
- <IconLink
- link="https://www.sonarsource.com/products/sonarqube/whats-new/?referrer=sonarqube"
- text={translate('docs.news')}
- />
-
- <IconLink
- link="https://www.sonarsource.com/products/sonarqube/roadmap/?referrer=sonarqube"
- text={translate('docs.roadmap')}
- />
-
- <IconLink
- icon="embed-doc/x-icon-black.svg"
- link="https://twitter.com/SonarQube"
- text="X @SonarQube"
- />
+ <DropdownMenu.ItemLink to="https://www.sonarsource.com/products/sonarqube/whats-new/?referrer=sonarqube">
+ {translate('docs.news')}
+ </DropdownMenu.ItemLink>
+
+ <DropdownMenu.ItemLink to="https://www.sonarsource.com/products/sonarqube/roadmap/?referrer=sonarqube">
+ {translate('docs.roadmap')}
+ </DropdownMenu.ItemLink>
+
+ <DropdownMenu.ItemLink to="https://twitter.com/SonarQube">X @SonarQube</DropdownMenu.ItemLink>
</>
);
}
diff --git a/server/sonar-web/src/main/js/components/intl/TranslatedMessage.tsx b/server/sonar-web/src/main/js/components/intl/TranslatedMessage.tsx
new file mode 100644
index 00000000000..497103386cd
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/intl/TranslatedMessage.tsx
@@ -0,0 +1,28 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { ComponentProps } from 'react';
+import { FormattedMessage } from 'react-intl';
+
+type Props = ComponentProps<typeof FormattedMessage>;
+
+export function TranslatedMessage(props: Props) {
+ return <FormattedMessage {...props} />;
+}
diff --git a/server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx b/server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx
index 8b9452fe2ff..bd481c9c061 100644
--- a/server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx
+++ b/server/sonar-web/src/main/js/components/shared/AppVersionStatus.tsx
@@ -27,10 +27,11 @@ import { useDocUrl } from '../../helpers/docs';
import { getInstanceVersionNumber } from '../../helpers/strings';
import { isCurrentVersionEOLActive } from '../../helpers/system';
import { useSystemUpgrades } from '../../queries/system';
+import { EditionKey } from '../../types/editions';
export default function AppVersionStatus() {
const { data } = useSystemUpgrades();
- const { version, versionEOL } = useAppState();
+ const { edition, version, versionEOL } = useAppState();
const isActiveVersion = useMemo(() => {
if (data?.installedVersionActive !== undefined) {
@@ -47,7 +48,7 @@ export default function AppVersionStatus() {
{ id: `footer.version` },
{
version: getInstanceVersionNumber(version),
- status: (
+ status: edition && edition !== EditionKey.community && (
<LinkStandalone className="sw-ml-1" highlight={LinkHighlight.CurrentColor} to={docUrl}>
<FormattedMessage
id={`footer.version.status.${isActiveVersion ? 'active' : 'inactive'}`}
diff --git a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/RepositoryVariables.tsx b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/RepositoryVariables.tsx
index 4a08ed78697..75e3731f4cf 100644
--- a/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/RepositoryVariables.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/bitbucket-pipelines/RepositoryVariables.tsx
@@ -59,7 +59,7 @@ export default function RepositoryVariables(props: RepositoryVariablesProps) {
almBinding,
projectBinding,
)}/admin/addon/admin/pipelines/repository-variables`}
- target="_blank"
+ shouldOpenInNewTab
>
{translate('onboarding.tutorial.with.bitbucket_pipelines.variables.intro.link')}
</LinkStandalone>
diff --git a/server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx b/server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx
index eaf7bdf367d..a4606e48efd 100644
--- a/server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/components/GithubCFamilyExampleRepositories.tsx
@@ -65,7 +65,7 @@ export default function GithubCFamilyExampleRepositories(
height={20}
src="/images/alm/github.svg"
/>
- <LinkStandalone target="_blank" to={link}>
+ <LinkStandalone shouldOpenInNewTab to={link}>
sonarsource-cfamily-examples
</LinkStandalone>
</div>
diff --git a/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx b/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx
index acb6dfef1db..38f0f198ee8 100644
--- a/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx
+++ b/server/sonar-web/src/main/js/components/tutorials/github-action/SecretStep.tsx
@@ -59,7 +59,7 @@ export default function SecretStep(props: SecretStepProps) {
almBinding && projectBinding ? (
<LinkStandalone
to={`${buildGithubLink(almBinding, projectBinding)}/settings/secrets`}
- target="_blank"
+ shouldOpenInNewTab
>
{translate('onboarding.tutorial.with.github_action.secret.intro.link')}
</LinkStandalone>
diff --git a/server/sonar-web/src/main/js/components/tutorials/test-utils.ts b/server/sonar-web/src/main/js/components/tutorials/test-utils.ts
index aee023dcbeb..f46255e766c 100644
--- a/server/sonar-web/src/main/js/components/tutorials/test-utils.ts
+++ b/server/sonar-web/src/main/js/components/tutorials/test-utils.ts
@@ -71,9 +71,11 @@ export function getCommonNodes(ci: TutorialModes) {
expiresInSelect: byRole('combobox', { name: '' }),
tokenValue: byText('generatedtoken2'),
linkToRepo: byRole('link', {
- name: `onboarding.tutorial.with.${CI_TRANSLATE_MAP[ci]}.${
- ci === TutorialModes.GitHubActions ? 'secret' : 'variables'
- }.intro.link`,
+ name: new RegExp(
+ `onboarding.tutorial.with.${CI_TRANSLATE_MAP[ci]}.${
+ ci === TutorialModes.GitHubActions ? 'secret' : 'variables'
+ }.intro.link`,
+ ),
}),
allSetSentence: byText('onboarding.tutorial.ci_outro.done'),
};
diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
index f6ee51f0630..d3f89677405 100644
--- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
+++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
@@ -50,7 +50,7 @@ export default function SystemUpgradeButton(props: Readonly<Props>) {
<Link
className="sw-ml-2"
to="https://www.sonarsource.com/products/sonarqube/downloads/?referrer=sonarqube"
- target="_blank"
+ shouldOpenInNewTab
>
{translate('learn_more')}
</Link>
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/l10n-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/l10n-test.ts
index 39d66bb80f8..e1d3df12fce 100644
--- a/server/sonar-web/src/main/js/helpers/__tests__/l10n-test.ts
+++ b/server/sonar-web/src/main/js/helpers/__tests__/l10n-test.ts
@@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { IntlShape } from 'react-intl';
import { Dict } from '../../types/types';
import {
getLocalizedCategoryMetricName,
@@ -30,18 +31,30 @@ import {
translate,
translateWithParameters,
} from '../l10n';
-import { getMessages } from '../l10nBundle';
+import { getIntl, getMessages } from '../l10nBundle';
const MSG = 'my_message';
jest.unmock('../l10n');
-jest.mock('../l10nBundle', () => ({
- getMessages: jest.fn().mockReturnValue({}),
-}));
+jest.mock('../l10nBundle', () => {
+ const bundle = jest.requireActual('../l10nBundle');
+ return {
+ ...bundle,
+ getIntl: jest.fn().mockReturnValue({ formatMessage: jest.fn(({ id }) => `${id}`) }),
+ getMessages: jest.fn().mockReturnValue({}),
+ };
+});
+
+const resetMessages = (messages: Dict<string>) => {
+ jest.mocked(getMessages).mockReturnValue(messages);
-const resetMessages = (messages: Dict<string>) =>
- (getMessages as jest.Mock).mockReturnValue(messages);
+ jest.mocked(getIntl).mockReturnValue({
+ formatMessage: jest.fn(({ id }) => {
+ return id ? (messages[id] ?? id) : `${id}`;
+ }),
+ } as unknown as IntlShape);
+};
beforeEach(() => {
resetMessages({});
@@ -82,6 +95,13 @@ describe('translate', () => {
expect(translate('random', 'key')).toBe('random.key');
expect(translate('composite.random', 'key')).toBe('composite.random.key');
});
+
+ it('should fall back to the old system when intl is undefined', () => {
+ jest.mocked(getIntl).mockReturnValueOnce(undefined as unknown as IntlShape);
+ resetMessages({ exists: 'this exists' });
+
+ expect(translate('exists')).toBe('this exists');
+ });
});
describe('translateWithParameters', () => {
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/l10nBundle-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/l10nBundle-test.ts
index 5ac785375b5..930f244e8a9 100644
--- a/server/sonar-web/src/main/js/helpers/__tests__/l10nBundle-test.ts
+++ b/server/sonar-web/src/main/js/helpers/__tests__/l10nBundle-test.ts
@@ -20,6 +20,7 @@
import { fetchL10nBundle } from '../../api/l10n';
import { loadL10nBundle } from '../l10nBundle';
+import { mockAppState } from '../testMocks';
beforeEach(() => {
jest.clearAllMocks();
@@ -33,9 +34,11 @@ jest.mock('../../api/l10n', () => ({
}),
}));
+const APP_STATE = mockAppState({});
+
describe('#loadL10nBundle', () => {
it('should fetch bundle without any timestamp', async () => {
- await loadL10nBundle();
+ await loadL10nBundle(APP_STATE);
expect(fetchL10nBundle).toHaveBeenCalledWith({ locale: 'de', ts: undefined });
});
@@ -44,7 +47,7 @@ describe('#loadL10nBundle', () => {
const cachedBundle = { timestamp: 'timestamp', locale: 'fr', messages: { cache: 'cache' } };
(window as unknown as any).sonarQubeL10nBundle = cachedBundle;
- await loadL10nBundle();
+ await loadL10nBundle(APP_STATE);
expect(fetchL10nBundle).toHaveBeenCalledWith({ locale: 'de', ts: undefined });
});
@@ -53,7 +56,7 @@ describe('#loadL10nBundle', () => {
const cachedBundle = { timestamp: 'timestamp', locale: 'de', messages: { cache: 'cache' } };
(window as unknown as any).sonarQubeL10nBundle = cachedBundle;
- await loadL10nBundle();
+ await loadL10nBundle(APP_STATE);
expect(fetchL10nBundle).toHaveBeenCalledWith({ locale: 'de', ts: cachedBundle.timestamp });
});
@@ -63,7 +66,7 @@ describe('#loadL10nBundle', () => {
(fetchL10nBundle as jest.Mock).mockRejectedValueOnce({ status: 304 });
(window as unknown as any).sonarQubeL10nBundle = cachedBundle;
- const bundle = await loadL10nBundle();
+ const bundle = await loadL10nBundle(APP_STATE);
expect(bundle).toEqual(
expect.objectContaining({ locale: cachedBundle.locale, messages: cachedBundle.messages }),
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/measures-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/measures-test.ts
index 09527fbb946..4c8516d50fb 100644
--- a/server/sonar-web/src/main/js/helpers/__tests__/measures-test.ts
+++ b/server/sonar-web/src/main/js/helpers/__tests__/measures-test.ts
@@ -28,6 +28,14 @@ import {
import { mockQualityGateStatusCondition } from '../mocks/quality-gates';
import { mockMeasure, mockMeasureEnhanced, mockMetric } from '../testMocks';
+jest.mock('../l10nBundle', () => {
+ const bundle = jest.requireActual('../l10nBundle');
+ return {
+ ...bundle,
+ getIntl: () => ({ formatMessage: jest.fn(({ id }) => `${id}`) }),
+ };
+});
+
describe('enhanceConditionWithMeasure', () => {
it('should correctly map enhance conditions with measure data', () => {
const measures = [
diff --git a/server/sonar-web/src/main/js/helpers/l10n.ts b/server/sonar-web/src/main/js/helpers/l10n.ts
index 920a5a6c577..4594e4ae636 100644
--- a/server/sonar-web/src/main/js/helpers/l10n.ts
+++ b/server/sonar-web/src/main/js/helpers/l10n.ts
@@ -18,7 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { getMessages } from './l10nBundle';
+import { getIntl, getMessages } from './l10nBundle';
export function hasMessage(...keys: string[]): boolean {
const messageKey = keys.join('.');
@@ -35,9 +35,14 @@ export function translate(...keys: string[]): string {
console.error(`No message for: ${messageKey}`);
}
- return l10nMessages[messageKey] || messageKey;
+ const intl = getIntl();
+ // fallback to old if in extension
+ return intl ? intl.formatMessage({ id: messageKey }) : l10nMessages[messageKey];
}
+/**
+ * @param messageKey @deprecated Use react-intl instead
+ */
export function translateWithParameters(
messageKey: string,
...parameters: Array<string | number>
diff --git a/server/sonar-web/src/main/js/helpers/l10nBundle.ts b/server/sonar-web/src/main/js/helpers/l10nBundle.ts
index 4fc589c47f8..d34b1f479bb 100644
--- a/server/sonar-web/src/main/js/helpers/l10nBundle.ts
+++ b/server/sonar-web/src/main/js/helpers/l10nBundle.ts
@@ -20,9 +20,12 @@
import { IntlShape, createIntl, createIntlCache } from 'react-intl';
import { fetchL10nBundle } from '../api/l10n';
+import { AppState } from '../types/appstate';
+import { EditionKey } from '../types/editions';
import { L10nBundle, L10nBundleRequestParams } from '../types/l10nBundle';
import { Dict } from '../types/types';
import { toISO8601WithOffsetString } from './dates';
+import { isDefined } from './types';
const DEFAULT_LOCALE = 'en';
const DEFAULT_MESSAGES: Dict<string> = {
@@ -48,7 +51,7 @@ export function getCurrentL10nBundle() {
return getL10nBundleFromCache();
}
-export async function loadL10nBundle() {
+export async function loadL10nBundle(appState: AppState | undefined) {
const browserLocale = getPreferredLanguage();
const cachedBundle = getL10nBundleFromCache();
@@ -91,6 +94,17 @@ export async function loadL10nBundle() {
{
locale: effectiveLocale,
messages,
+
+ /*
+ * This sets a default value for translations, so devs do not need to pass the {productName}
+ * value to every instance of FormattedMessage.
+ * It is a bit of a hack, abusing this config item that is normally for tag replacement only,
+ * hence the ts-expect-error tag
+ */
+ defaultRichTextElements: {
+ // @ts-expect-error
+ productName: getProductName(appState),
+ },
},
cache,
);
@@ -109,3 +123,13 @@ function getL10nBundleFromCache(): L10nBundle {
function persistL10nBundleInCache(bundle: L10nBundle) {
(window as unknown as any).sonarQubeL10nBundle = bundle;
}
+
+function getProductName(appState?: AppState) {
+ if (isDefined(appState?.edition)) {
+ return appState?.edition === EditionKey.community
+ ? 'SonarQube Community Build'
+ : 'SonarQube Server';
+ }
+
+ return 'SonarQube';
+}
diff --git a/server/sonar-web/src/main/js/sonar-aligned/helpers/__tests__/measures-test.ts b/server/sonar-web/src/main/js/sonar-aligned/helpers/__tests__/measures-test.ts
index 223898cb1d1..f5ca0dfd212 100644
--- a/server/sonar-web/src/main/js/sonar-aligned/helpers/__tests__/measures-test.ts
+++ b/server/sonar-web/src/main/js/sonar-aligned/helpers/__tests__/measures-test.ts
@@ -18,8 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { IntlShape } from 'react-intl';
import { MetricType } from '~sonar-aligned/types/metrics';
-import { getMessages } from '../../../helpers/l10nBundle';
+import { getIntl, getMessages } from '../../../helpers/l10nBundle';
import { Dict } from '../../../types/types';
import { formatMeasure } from '../measures';
@@ -33,11 +34,19 @@ jest.unmock('../../../helpers/l10n');
jest.mock('../../../helpers/l10nBundle', () => ({
getCurrentLocale: jest.fn().mockReturnValue('us'),
getMessages: jest.fn().mockReturnValue({}),
+ getIntl: jest.fn().mockReturnValue({ formatMessage: jest.fn(({ id }) => `${id}`) }),
}));
-const resetMessages = (messages: Dict<string>) =>
+const resetMessages = (messages: Dict<string>) => {
jest.mocked(getMessages).mockReturnValue(messages);
+ jest.mocked(getIntl).mockReturnValue({
+ formatMessage: jest.fn(({ id }) => {
+ return id ? (messages[id] ?? id) : `${id}`;
+ }),
+ } as unknown as IntlShape);
+};
+
beforeAll(() => {
resetMessages({
'work_duration.x_days': '{0}d',
diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock
index 789ea5ad6ee..bf044c355c4 100644
--- a/server/sonar-web/yarn.lock
+++ b/server/sonar-web/yarn.lock
@@ -2248,7 +2248,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7":
+"@babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7":
version: 7.25.6
resolution: "@babel/runtime@npm:7.25.6"
dependencies:
@@ -2257,6 +2257,15 @@ __metadata:
languageName: node
linkType: hard
+"@babel/runtime@npm:^7.20.13":
+ version: 7.26.0
+ resolution: "@babel/runtime@npm:7.26.0"
+ dependencies:
+ regenerator-runtime: "npm:^0.14.0"
+ checksum: 10/9f4ea1c1d566c497c052d505587554e782e021e6ccd302c2ad7ae8291c8e16e3f19d4a7726fb64469e057779ea2081c28b7dbefec6d813a22f08a35712c0f699
+ languageName: node
+ linkType: hard
+
"@babel/template@npm:^7.12.13, @babel/template@npm:^7.25.7":
version: 7.25.7
resolution: "@babel/template@npm:7.25.7"
@@ -3201,7 +3210,17 @@ __metadata:
languageName: node
linkType: hard
-"@floating-ui/dom@npm:^1.0.0, @floating-ui/dom@npm:^1.0.1":
+"@floating-ui/dom@npm:^1.0.0":
+ version: 1.6.12
+ resolution: "@floating-ui/dom@npm:1.6.12"
+ dependencies:
+ "@floating-ui/core": "npm:^1.6.0"
+ "@floating-ui/utils": "npm:^0.2.8"
+ checksum: 10/5c8e5fdcd3843140a606ab6dc6c12ad740f44e66b898966ef877393faaede0bbe14586e1049e2c2f08856437da8847e884a2762e78275fefa65a5a9cd71e580d
+ languageName: node
+ linkType: hard
+
+"@floating-ui/dom@npm:^1.0.1":
version: 1.6.10
resolution: "@floating-ui/dom@npm:1.6.10"
dependencies:
@@ -3211,29 +3230,29 @@ __metadata:
languageName: node
linkType: hard
-"@floating-ui/react-dom@npm:^2.0.0, @floating-ui/react-dom@npm:^2.1.1":
- version: 2.1.1
- resolution: "@floating-ui/react-dom@npm:2.1.1"
+"@floating-ui/react-dom@npm:^2.0.0, @floating-ui/react-dom@npm:^2.1.2":
+ version: 2.1.2
+ resolution: "@floating-ui/react-dom@npm:2.1.2"
dependencies:
"@floating-ui/dom": "npm:^1.0.0"
peerDependencies:
react: ">=16.8.0"
react-dom: ">=16.8.0"
- checksum: 10/cafabfb5dd0b25547863520b3bcf6faee7f087d0c3187a8779910a6838d496bf494f237bf1fe883bbfae1a7fcc399611ae52377b696065d8118bd7c1b9c0d253
+ checksum: 10/2a67dc8499674e42ff32c7246bded185bb0fdd492150067caf9568569557ac4756a67787421d8604b0f241e5337de10762aee270d9aeef106d078a0ff13596c4
languageName: node
linkType: hard
"@floating-ui/react@npm:^0.26.9":
- version: 0.26.23
- resolution: "@floating-ui/react@npm:0.26.23"
+ version: 0.26.27
+ resolution: "@floating-ui/react@npm:0.26.27"
dependencies:
- "@floating-ui/react-dom": "npm:^2.1.1"
- "@floating-ui/utils": "npm:^0.2.7"
+ "@floating-ui/react-dom": "npm:^2.1.2"
+ "@floating-ui/utils": "npm:^0.2.8"
tabbable: "npm:^6.0.0"
peerDependencies:
react: ">=16.8.0"
react-dom: ">=16.8.0"
- checksum: 10/a2ffeb0bae72cac9e6583d9651e75e94c261a9e78ca4a5e862b7d33f2c19ae014cbe272627a0a0a5a2b526280efab17ec687d32ba02f6ce4e924bec562ae06ab
+ checksum: 10/ab6d05e5cc2c29272a6830c06dff07e8330f2b90234674f2d74ed8084659ebe3ac9472770a5f0e76007dd2ae89f9da75189d188437aea4b8ac2789afc21bb6b2
languageName: node
linkType: hard
@@ -3244,6 +3263,13 @@ __metadata:
languageName: node
linkType: hard
+"@floating-ui/utils@npm:^0.2.8":
+ version: 0.2.8
+ resolution: "@floating-ui/utils@npm:0.2.8"
+ checksum: 10/3e3ea3b2de06badc4baebdf358b3dbd77ccd9474a257a6ef237277895943db2acbae756477ec64de65a2a1436d94aea3107129a1feeef6370675bf2b161c1abc
+ languageName: node
+ linkType: hard
+
"@formatjs/ecma402-abstract@npm:2.0.0":
version: 2.0.0
resolution: "@formatjs/ecma402-abstract@npm:2.0.0"
@@ -3877,9 +3903,9 @@ __metadata:
languageName: node
linkType: hard
-"@mantine/core@patch:@mantine/core@7.12.2#./config/patches/@mantine-core-npm-7.12.2-7aad505c0f.patch::locator=%40sonarsource%2Fechoes-react%40npm%3A0.9.0":
+"@mantine/core@patch:@mantine/core@7.12.2#./config/patches/@mantine-core-npm-7.12.2-7aad505c0f.patch::locator=%40sonarsource%2Fechoes-react%40npm%3A0.10.1":
version: 7.12.2
- resolution: "@mantine/core@patch:@mantine/core@npm%3A7.12.2#./config/patches/@mantine-core-npm-7.12.2-7aad505c0f.patch::version=7.12.2&hash=cab555&locator=%40sonarsource%2Fechoes-react%40npm%3A0.9.0"
+ resolution: "@mantine/core@patch:@mantine/core@npm%3A7.12.2#./config/patches/@mantine-core-npm-7.12.2-7aad505c0f.patch::version=7.12.2&hash=cab555&locator=%40sonarsource%2Fechoes-react%40npm%3A0.10.1"
dependencies:
"@floating-ui/react": "npm:^0.26.9"
clsx: "npm:^2.1.1"
@@ -4970,9 +4996,9 @@ __metadata:
languageName: node
linkType: hard
-"@sonarsource/echoes-react@npm:0.9.0":
- version: 0.9.0
- resolution: "@sonarsource/echoes-react@npm:0.9.0"
+"@sonarsource/echoes-react@npm:0.10.1":
+ version: 0.10.1
+ resolution: "@sonarsource/echoes-react@npm:0.10.1"
dependencies:
"@mantine/core": "patch:@mantine/core@7.12.2#./config/patches/@mantine-core-npm-7.12.2-7aad505c0f.patch"
"@mantine/hooks": "npm:7.12.2"
@@ -4994,7 +5020,7 @@ __metadata:
react-dom: ^18.0.0
react-intl: ^6.0.0
react-router-dom: ^6.0.0
- checksum: 10/d67803889b7a1954fb5759954086f85b95a33b7e154863cddea6a4b134d30c66b59c864d3ec93b382035a9564d682c22452f5a5b9f40c83fd1cc642584af957a
+ checksum: 10/326e46fcf08468afbf9c6dc7b8f07d09d18d1634a63fd5f0335966e0cf6162fdee94f03e9e86cbb2eef6331afee284cc14d15aaa1e749078251fcd1733ea41e4
languageName: node
linkType: hard
@@ -6009,7 +6035,7 @@ __metadata:
"@primer/octicons-react": "npm:19.11.0"
"@react-spring/rafz": "npm:9.7.4"
"@react-spring/web": "npm:9.7.4"
- "@sonarsource/echoes-react": "npm:0.9.0"
+ "@sonarsource/echoes-react": "npm:0.10.1"
"@tanstack/react-query": "npm:5.56.2"
"@testing-library/dom": "npm:10.4.0"
"@testing-library/jest-dom": "npm:6.5.0"
@@ -15928,9 +15954,9 @@ __metadata:
linkType: hard
"tslib@npm:^2.1.0":
- version: 2.6.3
- resolution: "tslib@npm:2.6.3"
- checksum: 10/52109bb681f8133a2e58142f11a50e05476de4f075ca906d13b596ae5f7f12d30c482feb0bff167ae01cfc84c5803e575a307d47938999246f5a49d174fc558c
+ version: 2.8.1
+ resolution: "tslib@npm:2.8.1"
+ checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7
languageName: node
linkType: hard
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index 4a24430e76a..36f8eb405eb 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -528,9 +528,9 @@ qualifier.description.APP=Single-level aggregation with a technical focus and a
# Admin notification
#
#------------------------------------------------------------------------------
-admin_notification.update.new_patch=There’s an update available for your SonarQube instance. Please update to make sure you benefit from the latest security and bug fixes.
-admin_notification.update.new_version=There’s a new version of SonarQube available. Upgrade to the latest active version to access new updates and features.
-admin_notification.update.current_version_inactive=You’re running a version of SonarQube that is no longer active. Please upgrade to an active version immediately.
+admin_notification.update.new_patch=There’s an update available for your {productName} instance. Please update to make sure you benefit from the latest security and bug fixes.
+admin_notification.update.new_version=There’s a new version of {productName} available. Upgrade to the latest active version to access new updates and features.
+admin_notification.update.current_version_inactive=You’re running a version of {productName} that is no longer active. Please upgrade to an active version immediately.
#------------------------------------------------------------------------------
#
@@ -564,7 +564,7 @@ event.category.All=All
event.category.VERSION=Version
event.category.QUALITY_GATE=Quality Gate
event.category.QUALITY_PROFILE=Quality Profile
-event.category.SQ_UPGRADE=SonarQube upgrade
+event.category.SQ_UPGRADE={productName} upgrade
event.category.DEFINITION_CHANGE=Definition Change
event.category.ISSUE_DETECTION=Issue Detection
event.category.OTHER=Other
@@ -577,7 +577,7 @@ event.definition_change.branch_added={project} {branch} added
event.definition_change.branch_removed={project} {branch} removed
event.definition_change.branch_replaced={project} {oldBranch} replaced with {newBranch}
event.failed_conditions=Failed Conditions:
-event.sqUpgrade=First analysis since upgrading to SonarQube {sqVersion}
+event.sqUpgrade=First analysis since upgrading to {productName} {sqVersion}
#------------------------------------------------------------------------------
#
@@ -606,7 +606,7 @@ layout.settings.VW=Portfolio Settings
layout.settings.SVW=Portfolio Settings
layout.security_reports=Security Reports
layout.nav.home_logo_alt=Logo, link to homepage
-layout.nav.home_sonarqube_logo_alt=SonarQube logo, link to homepage
+layout.nav.home_sonarqube_logo_alt={productName} logo, link to homepage
layout.must_be_configured=This will be available once your project is configured and analyzed.
layout.all_project_must_be_accessible=You need access to all projects within this {0} to access it.
@@ -716,7 +716,7 @@ project_baseline.compliance.warning.title.project=Your project new code definiti
baseline.specific_analysis=Specific analysis
baseline.specific_analysis.description=Choose an analysis as the baseline for the new code.
-baseline.specific_analysis.compliance_warning.title=Choosing the "Specific analysis" option from the SonarQube UI is not compliant with the Clean as You Code methodology
+baseline.specific_analysis.compliance_warning.title=Choosing the "Specific analysis" option from the {productName} UI is not compliant with the Clean as You Code methodology
baseline.specific_analysis.compliance_warning.explanation=It has been deprecated. The option remains available through the Web API.
baseline.specific_analysis.compliance_warning.link=Defining New Code
baseline.reference_branch=Reference branch
@@ -729,7 +729,7 @@ baseline.last_analysis_before=Last analysis before
baseline.next_analysis_notice=Changes will take effect after the next analysis
baseline.reference_branch.choose=Choose a branch
-baseline.reference_branch.does_not_exist=Branch {0} could not be found in SonarQube.
+baseline.reference_branch.does_not_exist=Branch {branch} could not be found in {productName}.
baseline.reference_branch.cannot_be_itself=A branch cannot be used as its own reference branch
baseline.reference_branch.invalid_branch_setting=Branch {0} cannot use itself as a reference. Define a specific setting instead of using the project-level setting.
baseline.edit_branch_setting=Edit the branch's setting
@@ -764,9 +764,9 @@ regulatory_page.available_branches_info.more_info.doc_link=related documentation
#
#------------------------------------------------------------------------------
-page_title.template.default=%s - SonarQube
-page_title.template.with_category=%s - {0} - SonarQube
-page_title.template.with_instance={0} - %s - SonarQube
+page_title.template.default=%s - {productName}
+page_title.template.with_category=%s - {page} - {productName}
+page_title.template.with_instance={project} - %s - {productName}
overview.page=Overview
code.page=Code
permissions.page=Permissions
@@ -870,7 +870,7 @@ hotspots.comment.field=Comment:
hotspots.comment.open=Comment
hotspots.comment.submit=Comment
hotspots.open_in_ide.success=Success. Switch to your IDE to see the security hotspot.
-hotspots.open_in_ide.failure=Unable to connect to your IDE to open the Security Hotspot. Please make sure you're running the latest version of SonarLint.
+hotspots.open_in_ide.failure=Unable to connect to your IDE to open the Security Hotspot. Please make sure you're running the latest version of SonarQube for IDE.
hotspots.assignee.select_user=Select a user...
hotspots.assignee.change_user=Click to change assignee
@@ -944,7 +944,7 @@ issue.assign.formlink=Assign
issue.assign.to_me=to me
issue.quick_fix=Quick fix
issue.quick_fix_available_with_sonarlint=Quick fix available in {link}
-issue.quick_fix_available_with_sonarlint_no_link=Quick fix available in SonarLint
+issue.quick_fix_available_with_sonarlint_no_link=Quick fix available in SonarQube for IDE
issue.comment.add_comment=Add Comment
issue.comment.add_comment.cancel=Cancel adding comment
issue.comment.enter_comment=Enter Comment
@@ -1479,9 +1479,9 @@ settings.state.url_not_valid={0} is not a valid URL
settings._default=(default)
settings.boolean.true=True
settings.boolean.false=False
-settings.default.no_value=<no value>
-settings.default.complex_value=<complex value>
-settings.default.password=<password>
+settings.default.no_value='<no value>'
+settings.default.complex_value='<complex value>'
+settings.default.password='<password>'
settings.reset_confirm.title=Reset Setting
settings.reset_confirm.description=Are you sure that you want to reset this setting?
settings.definition.reset=Reset "{0}" to default values
@@ -1520,8 +1520,8 @@ settings.projects.change_visibility_form.warning.gitlab=This will not change the
settings.projects.change_visibility_form.submit=Change Default Visibility
settings.almintegration.title=DevOps Platform Integrations
-settings.almintegration.description=DevOps Platform integrations allow SonarQube to interact with your DevOps Platform. This enables things like authentication, or providing analysis details and a Quality Gate to your Pull Requests directly in your DevOps Platform's interface.
-settings.almintegration.empty.server_base_url=You need to set the Server Base URL in General > {serverBaseUrl} to have correct links from the DevOps Platform to your SonarQube instance.
+settings.almintegration.description=DevOps Platform integrations allow {productName} to interact with your DevOps Platform. This enables things like authentication, or providing analysis details and a Quality Gate to your Pull Requests directly in your DevOps Platform's interface.
+settings.almintegration.empty.server_base_url=You need to set the Server Base URL in General > {serverBaseUrl} to have correct links from the DevOps Platform to your {productName} instance.
settings.almintegration.empty.server_base_url.setting_link=Server Base URL
settings.almintegration.tab.github=GitHub
settings.almintegration.tab.bitbucket=Bitbucket
@@ -1529,13 +1529,13 @@ settings.almintegration.tab.azure=Azure DevOps
settings.almintegration.tab.gitlab=GitLab
settings.almintegration.github.info=You need to install a GitHub App with specific settings and permissions to enable Pull Request Decoration on your Organization or Repository. {link}
settings.almintegration.github.additional_permission=If Quality Gate status reporting fails on private projects, you might need to add an additional permission to the GitHub App. {link}
-settings.almintegration.bitbucketcloud.info=SonarQube needs you to create an {oauth} in your Bitbucket Cloud workspace settings to report the Quality Gate status on Pull Requests. It needs to be a private consumer with {permission} permission. An OAuth callback URL is required by Bitbucket Cloud but not used by SonarQube so any URL works. {doc_link}
+settings.almintegration.bitbucketcloud.info={productName} needs you to create an {oauth} in your Bitbucket Cloud workspace settings to report the Quality Gate status on Pull Requests. It needs to be a private consumer with {permission} permission. An OAuth callback URL is required by Bitbucket Cloud but not used by {productName} so any URL works. {doc_link}
settings.almintegration.bitbucketcloud.oauth=OAuth consumer
-settings.almintegration.empty.azure=Create your first Azure DevOps configuration to start analyzing your repositories on SonarQube.
-settings.almintegration.empty.bitbucket=Create your first Bitbucket configuration to start analyzing your repositories on SonarQube.
-settings.almintegration.empty.bitbucketcloud=Create your first Bitbucket Cloud configuration to start analyzing your repositories on SonarQube.
-settings.almintegration.empty.github=Create your first GitHub configuration to start analyzing your repositories on SonarQube.
-settings.almintegration.empty.gitlab=Create your first GitLab configuration to start analyzing your repositories on SonarQube.
+settings.almintegration.empty.azure=Create your first Azure DevOps configuration to start analyzing your repositories on {productName}.
+settings.almintegration.empty.bitbucket=Create your first Bitbucket configuration to start analyzing your repositories on {productName}.
+settings.almintegration.empty.bitbucketcloud=Create your first Bitbucket Cloud configuration to start analyzing your repositories on {productName}.
+settings.almintegration.empty.github=Create your first GitHub configuration to start analyzing your repositories on {productName}.
+settings.almintegration.empty.gitlab=Create your first GitLab configuration to start analyzing your repositories on {productName}.
settings.almintegration.create=Create configuration
settings.almintegration.create.tooltip=Upgrade to {link} to integrate with multiple {alm} instances.
settings.almintegration.create.tooltip.link=Enterprise Edition
@@ -1594,11 +1594,11 @@ settings.almintegration.form.private_key.github.help=Your GitHub App's private k
settings.almintegration.form.webhook_secret.github=Webhook Secret
settings.almintegration.form.webhook_secret.github.help=Leave this field empty unless you are using GitHub Code Scanning Alert feature. See documentation for more details.
settings.almintegration.form.personal_access_token=Personal Access Token
-settings.almintegration.form.personal_access_token.azure.help=SonarQube needs a {pat} to report the Quality Gate status on Pull Requests in Azure DevOps. To create this token, we recommend using a dedicated Azure DevOps account with administration permissions. The token itself needs {permission} permission. {doc_link}
+settings.almintegration.form.personal_access_token.azure.help={productName} needs a {pat} to report the Quality Gate status on Pull Requests in Azure DevOps. To create this token, we recommend using a dedicated Azure DevOps account with administration permissions. The token itself needs {permission} permission. {doc_link}
settings.almintegration.form.personal_access_token.azure.help.url=Personal Access Token
-settings.almintegration.form.personal_access_token.gitlab.help=SonarQube needs a {pat} to report the Quality Gate status on Merge Requests in GitLab. To create this token, we recommend using a dedicated GitLab account with {permission} permission to all target projects. The token itself needs the {scope} scope. {doc_link}
+settings.almintegration.form.personal_access_token.gitlab.help={productName} needs a {pat} to report the Quality Gate status on Merge Requests in GitLab. To create this token, we recommend using a dedicated GitLab account with {permission} permission to all target projects. The token itself needs the {scope} scope. {doc_link}
settings.almintegration.form.personal_access_token.gitlab.help.url=Personal Access Token
-settings.almintegration.form.personal_access_token.bitbucket.help=SonarQube needs a {pat} to report the Quality Gate status on Pull Requests in Bitbucket Server. To create this token, we recommend using a dedicated Bitbucket Server account with administration permissions. The token itself needs {permission} permission. {doc_link}
+settings.almintegration.form.personal_access_token.bitbucket.help={productName} needs a {pat} to report the Quality Gate status on Pull Requests in Bitbucket Server. To create this token, we recommend using a dedicated Bitbucket Server account with administration permissions. The token itself needs {permission} permission. {doc_link}
settings.almintegration.form.personal_access_token.bitbucket.help.url=Personal Access Token
settings.almintegration.form.save=Save configuration
settings.almintegration.form.cancel=Cancel
@@ -1614,7 +1614,7 @@ settings.almintegration.feature.pr_decoration.disabled=Disabled
settings.almintegration.feature.pr_decoration.disabled.no_branches=Upgrade to {link} to enable this feature.
settings.almintegration.feature.pr_decoration.disabled.no_branches.link=Developer Edition
settings.almintegration.feature.alm_repo_import.title=Import repositories from your DevOps Platform
-settings.almintegration.feature.alm_repo_import.description=Select repositories from your DevOps Platform, and import them into SonarQube.
+settings.almintegration.feature.alm_repo_import.description=Select repositories from your DevOps Platform, and import them into {productName}.
settings.almintegration.feature.alm_repo_import.disabled=Disabled
settings.almintegration.feature.alm_repo_import.disabled.no_url=This feature is disabled because your configured instance has no URL.
settings.almintegration.tabs.authentication_moved=You can delegate authentication to this DevOps Platform. The relevant settings are under the {link} section.
@@ -1656,7 +1656,7 @@ settings.authentication.github.appid_x=App ID: {applicationId}
settings.authentication.github.confirm.AUTO_PROVISIONING=Switch to automatic provisioning
settings.authentication.github.confirm.JIT=Switch to Just-in-Time provisioning
settings.authentication.github.confirm.insecure=Potentially insecure configuration
-settings.authentication.github.confirm.AUTO_PROVISIONING.description=Once you transition to automatic provisioning, groups, users, group memberships, and permissions on GitHub projects will be inherited from GitHub. You will no longer have the ability to edit them within SonarQube. Do you want to proceed with this change?
+settings.authentication.github.confirm.AUTO_PROVISIONING.description=Once you transition to automatic provisioning, groups, users, group memberships, and permissions on GitHub projects will be inherited from GitHub. You will no longer have the ability to edit them within {productName}. Do you want to proceed with this change?
settings.authentication.github.confirm.JIT.description=Switching to Just-in-Time provisioning removes the automatic synchronization of users, groups, and group memberships. Users are provisioned and group memberships are updated only at user login. Are you sure?
settings.authentication.github.provisioning_change.confirm_changes=Confirm change
settings.authentication.github.provisioning_change.insecure_config=Please be aware that your configuration is potentially insecure because you didn't add any organization to the allowlist. If your GitHub App is public, anyone can install it and gain access to your instance.
@@ -1666,7 +1666,7 @@ settings.authentication.github.form.legacy_configured=Compatibility with GitHub
settings.authentication.github.form.legacy_configured.link=Learn more about how to create a GitHub App configuration
settings.authentication.github.enable_first=Enable your GitHub configuration for more provisioning options.
settings.authentication.github.form.allowedOrganizations.name=Organizations
-settings.authentication.github.form.allowedOrganizations.description=Only members of these organizations will be able to authenticate to the server. ⚠ if not set, users from any organization where the GitHub App is installed will be able to login to this SonarQube instance.
+settings.authentication.github.form.allowedOrganizations.description=Only members of these organizations will be able to authenticate to the server. ⚠ if not set, users from any organization where the GitHub App is installed will be able to login to this {productName} instance.
settings.authentication.github.form.apiUrl.name=The API url for a GitHub instance.
settings.authentication.github.form.apiUrl.description=The API url for a GitHub instance. https://api.github.com/ for GitHub.com, https://github.company.com/api/v3/ when using GitHub Enterprise
settings.authentication.github.form.applicationId.name=App ID
@@ -1678,7 +1678,7 @@ settings.authentication.github.form.clientSecret.description=Client password pro
settings.authentication.github.form.privateKey.name=Private Key
settings.authentication.github.form.privateKey.description=Your GitHub App's private key. You can generate a .pem file from your GitHub App's page under Private keys. Copy and paste the whole contents of the file here.
settings.authentication.github.form.synchronizeGroups.name=Synchronize teams as groups
-settings.authentication.github.form.synchronizeGroups.description=Synchronize GitHub team with SonarQube group memberships when users log in to SonarQube. For each GitHub team they belong to, users will be associated to a group of the same name if it exists in SonarQube.
+settings.authentication.github.form.synchronizeGroups.description=Synchronize GitHub team with {productName} group memberships when users log in to {productName}. For each GitHub team they belong to, users will be associated to a group of the same name if it exists in {productName}.
settings.authentication.github.form.webUrl.name=The WEB url for a GitHub instance.
settings.authentication.github.form.webUrl.description=The WEB url for a GitHub instance. https://github.com/ for GitHub.com, https://github.company.com/ when using GitHub Enterprise.
settings.authentication.github.form.private_key.required_for_url_change=Please provide your private key again to update the API URL or the Web URL.
@@ -1687,12 +1687,12 @@ settings.authentication.github.form.provisioning_with_github_short.autoProvision
settings.authentication.github.form.provisioning_with_github_short.jit=Just-in-Time provisioning
settings.authentication.github.form.provisioning_with_github.description=Users, groups and permissions are automatically provisioned from your GitHub organizations. Once activated, users and groups can only be created and modified from your GitHub organizations/teams. Existing local users will be kept and can only be deactivated. {documentation}
settings.authentication.github.form.description.doc=For more details, see {documentation}.
-settings.authentication.github.form.provisioning_at_login.description=Users and groups are synchronized only when users log in to SonarQube. {documentation}
+settings.authentication.github.form.provisioning_at_login.description=Users and groups are synchronized only when users log in to {productName}. {documentation}
settings.authentication.github.form.provisioning.disabled=Your current edition does not support provisioning with GitHub. See the {documentation} for more information.
settings.authentication.github.form.allowUsersToSignUp.name=Allow users to sign up
settings.authentication.github.form.allowUsersToSignUp.description=Allow new users to authenticate. When set to disabled, only existing users will be able to authenticate to the server.
settings.authentication.github.form.projectVisibility.name=Provision project visibility
-settings.authentication.github.form.projectVisibility.description=Change project visibility based on GitHub repository visibility. If disabled, every provisioned project will be private in SonarQube and visible only to users with explicit GitHub permissions for the corresponding repository. Changes take effect at the next synchronization.
+settings.authentication.github.form.projectVisibility.description=Change project visibility based on GitHub repository visibility. If disabled, every provisioned project will be private in {productName} and visible only to users with explicit GitHub permissions for the corresponding repository. Changes take effect at the next synchronization.
settings.authentication.github.synchronize_now=Synchronize now
settings.authentication.github.synchronization_finish=Synchronization is done.
settings.authentication.github.configuration.validation.details=View details
@@ -1705,7 +1705,7 @@ settings.authentication.github.configuration.validation.details.title=Configurat
settings.authentication.github.configuration.validation.details.valid_label=Valid
settings.authentication.github.configuration.validation.details.invalid_label=Invalid
settings.authentication.github.configuration.validation.details.org_not_found={0} (not found or app not installed)
-settings.authentication.github.configuration.roles_mapping.description=When synchronizing users and groups, SonarQube assigns permissions based on GitHub user and team roles. You can customize the mapping of permissions. The new mapping will take effect at the next synchronization.
+settings.authentication.github.configuration.roles_mapping.description=When synchronizing users and groups, {productName} assigns permissions based on GitHub user and team roles. You can customize the mapping of permissions. The new mapping will take effect at the next synchronization.
settings.authentication.github.configuration.roles_mapping.save_success=GitHub roles mapping saved successfully.
settings.authentication.github.configuration.roles_mapping.dialog.custom_roles_description=When a custom role name added here matches an existing GitHub custom role in any of your organizations, the mapping applies to all users with this custom role. If an existing GitHub custom role has no exact match in this list, the permissions of its inherited base role are mapped.
settings.authentication.github.configuration.unsaved_changes=You have unsaved changes.
@@ -1723,9 +1723,9 @@ settings.authentication.gitlab.form.secret.name=Secret
settings.authentication.gitlab.form.secret.description=Secret provided by GitLab when registering the application.
settings.authentication.gitlab.form.secret.required_for_url_change=Please provide your secret again to update the URL.
settings.authentication.gitlab.form.synchronizeGroups.name=Synchronize user groups
-settings.authentication.gitlab.form.synchronizeGroups.description=For each GitLab group they belong to, the user will be associated to a group with the same name (if it exists) in SonarQube. If enabled, the GitLab OAuth 2 application will need to provide the api scope.
+settings.authentication.gitlab.form.synchronizeGroups.description=For each GitLab group they belong to, the user will be associated to a group with the same name (if it exists) in {productName}. If enabled, the GitLab OAuth 2 application will need to provide the api scope.
settings.authentication.gitlab.form.allowedGroups.name=Allowed groups
-settings.authentication.gitlab.form.allowedGroups.description.JIT=Only members of these groups (and sub-groups) will be allowed to authenticate. Enter the group slug as it appears in the GitLab URL, for instance `my-gitlab-group`. ⚠︎ When you turn on `Allow users to sign up`, make sure to also turn on group synchronization and provide a list of allowed groups. Otherwise, any GitLab user will be able to log in to this SonarQube instance.
+settings.authentication.gitlab.form.allowedGroups.description.JIT=Only members of these groups (and sub-groups) will be allowed to authenticate. Enter the group slug as it appears in the GitLab URL, for instance `my-gitlab-group`. ⚠︎ When you turn on `Allow users to sign up`, make sure to also turn on group synchronization and provide a list of allowed groups. Otherwise, any GitLab user will be able to log in to this {productName} instance.
settings.authentication.gitlab.form.allowedGroups.description.AUTO_PROVISIONING=Only members of these groups (and sub-groups) will be provisioned. Please enter the group slug as it appears in the GitLab URL, for instance `my-gitlab-group`.
settings.authentication.gitlab.form.allowUsersToSignUp.name=Allow users to sign up
settings.authentication.gitlab.form.allowUsersToSignUp.description=Allow new users to authenticate. When set to disabled, only existing users will be able to authenticate to the server.
@@ -1734,12 +1734,12 @@ settings.authentication.gitlab.form.provisioningToken.description=Token used for
settings.authentication.gitlab.applicationId.name=App ID: {0}
settings.authentication.gitlab.enable_first=Enable your GitLab configuration for more provisioning options.
settings.authentication.gitlab.provisioning_at_login=Just-in-Time user provisioning (default)
-settings.authentication.gitlab.provisioning_at_login.description=Users are synchronized only when users log in to SonarQube. {documentation}
+settings.authentication.gitlab.provisioning_at_login.description=Users are synchronized only when users log in to {productName}. {documentation}
settings.authentication.gitlab.description.JIT.learn_more=Learn more about Just-in-Time provisioning with GitLab
settings.authentication.gitlab.description.AUTO_PROVISIONING.learn_more=Learn more about automatic provisioning with GitLab
settings.authentication.gitlab.confirm.AUTO_PROVISIONING=Switch to automatic provisioning
settings.authentication.gitlab.confirm.JIT=Switch to Just-in-Time provisioning
-settings.authentication.gitlab.confirm.AUTO_PROVISIONING.description=Once you transition to automatic provisioning users, groups and permissions on GitLab projects will be inherited from GitLab. You will no longer have the ability to edit them within SonarQube. Do you want to proceed with this change?
+settings.authentication.gitlab.confirm.AUTO_PROVISIONING.description=Once you transition to automatic provisioning users, groups and permissions on GitLab projects will be inherited from GitLab. You will no longer have the ability to edit them within {productName}. Do you want to proceed with this change?
settings.authentication.gitlab.confirm.JIT.description=Switching to Just-in-Time provisioning removes the automatic synchronization of users, groups and permissions. Users are provisioned and updated only at user login. Are you sure?
settings.authentication.gitlab.confirm.insecure=Potentially insecure configuration
settings.authentication.gitlab.provisioning_change.confirm_changes=Confirm change
@@ -1750,7 +1750,7 @@ settings.authentication.gitlab.form.provisioning.disabled=Your current edition d
settings.authentication.gitlab.configuration.unsaved_changes=You have unsaved changes.
settings.authentication.gitlab.configuration.valid.JIT=Configuration is valid for Just-in-Time provisioning.
settings.authentication.gitlab.configuration.valid.AUTO_PROVISIONING=Configuration is valid for Automatic provisioning.
-settings.authentication.gitlab.configuration.roles_mapping.description=When synchronizing users and groups, SonarQube assigns permissions based on Gitlab user roles. You can customize the mapping of permissions. The new mapping will take effect at the next synchronization.
+settings.authentication.gitlab.configuration.roles_mapping.description=When synchronizing users and groups, {productName} assigns permissions based on Gitlab user roles. You can customize the mapping of permissions. The new mapping will take effect at the next synchronization.
settings.authentication.gitlab.configuration.roles_mapping.save_success=GitLab roles mapping saved successfully.
settings.authentication.gitlab.configuration.roles_mapping.dialog.custom_roles_description=When a custom role name added here matches an existing GitLab custom role in any of your groups or projects, the mapping applies to all users with this custom role. If an existing GitLab custom role has no exact match in this list, the permissions of its inherited base role are mapped.
@@ -1782,7 +1782,7 @@ settings.authentication.form.edit.saml=Edit SAML configuration
settings.authentication.saml.configuration=SAML Configuration
settings.authentication.saml.confirm.scim=Switch to automatic provisioning
settings.authentication.saml.confirm.jit=Switch to Just-in-Time provisioning
-settings.authentication.saml.confirm.scim.description=Once you transition to automatic provisioning, groups, users and group memberships will be managed by your identity provider. You will no longer have the ability to edit them within SonarQube. Do you want to proceed with this change?
+settings.authentication.saml.confirm.scim.description=Once you transition to automatic provisioning, groups, users and group memberships will be managed by your identity provider. You will no longer have the ability to edit them within {productName}. Do you want to proceed with this change?
settings.authentication.saml.confirm.jit.description=Switching to Just-in-Time provisioning removes all information provided while automatic provisioning through SCIM was active. These changes cannot be reverted. Are you sure?
settings.authentication.saml.form.loading=Loading SAML configuration
settings.authentication.saml.form.not_configured=SAML is not configured
@@ -1819,7 +1819,7 @@ settings.pr_decoration.binding.form.name=Configuration name
settings.pr_decoration.binding.form.name.help=Each DevOps Platform instance must be configured globally first, and given a unique name. Pick the instance your project is hosted on.
settings.pr_decoration.binding.form.monorepo=Enable monorepository support
settings.pr_decoration.binding.form.monorepo.help=Enable this setting if your project is part of a monorepository. {doc_link}
-settings.pr_decoration.binding.form.monorepo.warning=This setting must be enabled for all SonarQube projects that are part of a monorepository.
+settings.pr_decoration.binding.form.monorepo.warning=This setting must be enabled for all {productName} projects that are part of a monorepository.
settings.pr_decoration.binding.form.azure.project=Project name
settings.pr_decoration.binding.form.azure.project.help=The name of the Azure DevOps project containing your repository. You can find this name on your project's Overview page.
settings.pr_decoration.binding.form.azure.repository=Repository name
@@ -1850,7 +1850,7 @@ settings.mode.instance_conditions_from_other_mode=Some of the Quality Gates in t
settings.mode.standard.name=Standard Experience
settings.mode.mqr.name=Multi-Quality Rule (MQR) Mode
settings.mode.standard.description.line1=Encompasses the traditional use of rule types such as bugs, code smells, and vulnerabilities, with a single category and severity level for each rule.
-settings.mode.standard.description.line2=This approach focuses on assigning severity to a rule and its issues based on the single software quality (for example, security, reliability or maintainability) it has the largest impact on. This is the rule categorization used in SonarQube 9.9 LTA and earlier.
+settings.mode.standard.description.line2=This approach focuses on assigning severity to a rule and its issues based on the single software quality (for example, security, reliability or maintainability) it has the largest impact on. This is the rule categorization used in {productName} 9.9 LTA and earlier.
settings.mode.mqr.description.line1=Aims to more accurately represent the impact issues have on all software qualities. Very few issues impact only a single software quality. For instance, most vulnerabilities are also bugs. And vice versa. The MQR mode maps each rule to each of the qualities it impacts, with a separate severity rating for each quality.
settings.mode.mqr.description.line2=This approach focuses on ensuring the impact of an issue on all software qualities is clear, not just the most severe one.
settings.mode.save.warning=Save changes to see them reflected in your instance
@@ -1931,7 +1931,7 @@ property.codefix.admin.promoted_section.title=Free - early access feature
property.codefix.admin.promoted_section.content1=This no cost trial is offered to you at Sonar’s discretion. Sonar can decide to stop this trial anytime.
property.codefix.admin.promoted_section.content2=At the end of the trial, this feature will be deactivated and your choice to “enable AI CodeFix” below will be ignored. Your organisation will not be charged.
property.codefix.admin.serviceCheck.title=Test the AI CodeFix service
-property.codefix.admin.serviceCheck.description1=Make sure this SonarQube instance can communicate with the AI CodeFix service, which requires network connectivity to function.
+property.codefix.admin.serviceCheck.description1=Make sure this {productName} instance can communicate with the AI CodeFix service, which requires network connectivity to function.
property.codefix.admin.serviceCheck.description2=This test is free and should only take a few seconds.
property.codefix.admin.serviceCheck.learnMore=Read more about enabling AI CodeFix
property.codefix.admin.serviceCheck.action=Test AI CodeFix service
@@ -1939,11 +1939,11 @@ property.codefix.admin.serviceCheck.spinner.label=Waiting for AI CodeFix service
property.codefix.admin.serviceCheck.result.success=The AI CodeFix service responded successfully.
property.codefix.admin.serviceCheck.result.unresponsive.message=The AI CodeFix service does not respond or is not reachable.
property.codefix.admin.serviceCheck.result.unresponsive.causes.title=Here are some possible causes of this error:
-property.codefix.admin.serviceCheck.result.unresponsive.causes.1=The network may not be properly configured on this SonarQube instance. Please check the firewall and connectivity settings.
+property.codefix.admin.serviceCheck.result.unresponsive.causes.1=The network may not be properly configured on this {productName} instance. Please check the firewall and connectivity settings.
property.codefix.admin.serviceCheck.result.unresponsive.causes.2=The AI CodeFix service may be down.
property.codefix.admin.serviceCheck.result.requestError=Error checking the AI CodeFix service:
property.codefix.admin.serviceCheck.result.serviceError=The AI CodeFix service is reachable but returned an error. Check logs for more details.
-property.codefix.admin.serviceCheck.result.unauthorized=This SonarQube instance is not allowed to use AI CodeFix.
+property.codefix.admin.serviceCheck.result.unauthorized=This {productName} instance is not allowed to use AI CodeFix.
property.codefix.admin.serviceCheck.result.unknown=The AI CodeFix service returned an unexpected message:
#------------------------------------------------------------------------------
@@ -2272,8 +2272,8 @@ project.info.see_more_info_on_x_locs=See more information on your {0} lines of c
project.info.make_home.title=Use as homepage
project.info.make_home.label=Make this project my homepage
application.info.make_home.label=Make this application my homepage
-project.info.make_home.tooltip=This means you'll be redirected to this project whenever you log in to SonarQube or click on the top-left SonarQube logo.
-application.info.make_home.tooltip=This means you'll be redirected to this application whenever you log in to SonarQube or click on the top-left SonarQube logo.
+project.info.make_home.tooltip=This means you'll be redirected to this project whenever you log in to {productName} or click on the top-left {productName} logo.
+application.info.make_home.tooltip=This means you'll be redirected to this application whenever you log in to {productName} or click on the top-left {productName} logo.
overview.project_key.tooltip.TRK=Your project key is a unique identifier for your project. If you are using Maven, make sure the key matches the "groupId:artifactId" format.
overview.project_key.tooltip.APP=Your application key is a unique identifier for your application.
project.info.ai_code_assurance.title=AI Code Assurance
@@ -2330,7 +2330,7 @@ quality_profiles.changelog.cca_only_changed=Clean Code attribute set to {newClea
quality_profiles.changelog.impact_changed=Software impact set to {newSoftwareQuality} with severity {newSeverity}, was {oldSoftwareQuality} with severity {oldSeverity}
quality_profiles.changelog.impact_added=Software impact {newSoftwareQuality} with severity {newSeverity} was added
quality_profiles.changelog.impact_removed=Software impact {oldSoftwareQuality} with severity {oldSeverity} was removed
-quality_profiles.changelog.sq_upgrade=Instance upgraded to SonarQube {sqVersion}
+quality_profiles.changelog.sq_upgrade=Instance upgraded to {productName} {sqVersion}
quality_profiles.deleted_profile=The profile {0} doesn't exist anymore
quality_profiles.projects_for_default=Every project not specifically associated with a quality profile will be associated to this one by default.
quality_profile.x_rules={count} rule(s)
@@ -2633,7 +2633,7 @@ coding_rules.context.others.description.second=Caution: The libraries mentioned
coding_rules.context.others.description.do=Do use libraries that are compatible with the frameworks you are using.
coding_rules.context.others.description.dont=Don’t blindly copy and paste the fixups into your code.
coding_rules.context.others.title_feedback=Help us improve
-coding_rules.context.others.feedback_description_1=Let us know if the instructions we provide do not work for you. Tell us which framework you use and why our solution does not work by submitting an idea on the SonarQube productboard.
+coding_rules.context.others.feedback_description_1=Let us know if the instructions we provide do not work for you. Tell us which framework you use and why our solution does not work by submitting an idea on the {productName} productboard.
coding_rules.context.others.feedback_description_2=We will do our best to provide you with more relevant instructions in the future.
coding_rules.context.others.feedback_description.link=Submit an idea
coding_rules.create=Create
@@ -2643,7 +2643,7 @@ coding_rules.custom_rule.help=Custom rules are created by administrators from te
coding_rules.custom_rule.activation_notice=Note: parameters of a custom rule are not customizable on rule activation, only during creation/edit.
coding_rules.custom_rule.software_quality_x={quality} software quality
coding_rules.custom_rule.select_software_quality=Please select at least one software quality.
-coding_rules.custom_rule.removal=Only custom rules may be deleted. When a custom rule is deleted, it is not removed from the SonarQube instance. Instead its status is set to "REMOVED", allowing relevant issues to continue to be displayed properly.
+coding_rules.custom_rule.removal=Only custom rules may be deleted. When a custom rule is deleted, it is not removed from the {productName} instance. Instead its status is set to "REMOVED", allowing relevant issues to continue to be displayed properly.
coding_rules.custom_rules=Custom Rules
coding_rules.deactivate_in_quality_profile=Deactivate In Quality Profile
coding_rules.deactivate_in_quality_profile_x=Deactivate In Quality Profile {0}
@@ -2906,9 +2906,9 @@ email_notification.form.security_protocol.description=Security protocol used to
email_notification.form.from_address=From address
email_notification.form.from_address.description=Address emails will come from.
email_notification.form.from_name=From name
-email_notification.form.from_name.description=Name emails will come from (usually "SonarQube").
+email_notification.form.from_name.description=Name emails will come from (usually "{productName}").
email_notification.form.subject_prefix=Subject prefix
-email_notification.form.subject_prefix.description=Prefix added to email so they can be easily recognized (usually "[SonarQube]").
+email_notification.form.subject_prefix.description=Prefix added to email so they can be easily recognized (usually "[{productName}]").
email_notification.form.save_configuration=Save configuration
email_notification.form.save_configuration.create_success=Email configuration saved successfully.
email_notification.form.save_configuration.update_success=Email configuration updated successfully.
@@ -2923,7 +2923,7 @@ email_notification.test.modal_title=Test email
email_notification.test.to_address=To
email_notification.test.subject=Subject
email_notification.test.message=Message
-email_notification.test.message_text=This is a test message from SonarQube.
+email_notification.test.message_text=This is a test message from {productName}.
email_notification.test.create_test_email=Create test email
email_notification.test.submit=Send test email
email_notification.test.success=Your email was sent successfully
@@ -2992,7 +2992,7 @@ user.x_deleted={0} (deleted)
user.confirm_password.no_match=Passwords do not match
login.page=Log in
-login.login_to_sonarqube=Log in to SonarQube
+login.login_to_sonarqube=Log in to {productName}
login.login_with_x=Log in with {0}
login.more_options=Log in with credentials
login.unauthorized_access_alert=You are not authorized to access this page. Please log in with more privileges and try again.
@@ -3875,8 +3875,8 @@ permission_templates.show_actions_for_x=Show actions for template {0}
#
#------------------------------------------------------------------------------
-promotion.sonarlint.title=Get the most out of SonarQube!
-promotion.sonarlint.content=Take advantage of the whole ecosystem by using SonarLint, a free IDE plugin that helps you find and fix issues earlier in your workflow. Connect SonarLint to SonarQube to sync rule sets and issue states.
+promotion.sonarlint.title=Get the most out of {productName}!
+promotion.sonarlint.content=Take advantage of the whole ecosystem by using SonarQube for IDE, a free IDE plugin that helps you find and fix issues earlier in your workflow. Connect SonarQube for IDE to {productName} to sync rule sets and issue states.
#------------------------------------------------------------------------------
#
@@ -3884,14 +3884,14 @@ promotion.sonarlint.content=Take advantage of the whole ecosystem by using Sonar
#
#------------------------------------------------------------------------------
-sonarlint-connection.request.title=Allow SonarLint connection?
-sonarlint-connection.request.description=SonarLint for {0} is requesting access to SonarQube.
-sonarlint-connection.request.description2=Do you allow SonarLint to connect? This will create a token and share it with SonarLint.
+sonarlint-connection.request.title=Allow SonarQube for IDE connection?
+sonarlint-connection.request.description=SonarQube for IDE for {ideName} is requesting access to {productName}.
+sonarlint-connection.request.description2=Do you allow SonarQube for IDE to connect? This will create a token and share it with SonarQube for IDE.
sonarlint-connection.request.action=Allow connection
sonarlint-connection.token-error.title=Token generation failed
-sonarlint-connection.token-error.description=SonarQube was not able to generate a token.
-sonarlint-connection.token-error.description2=Go back to your IDE and start again, or go to the {link} of your SonarQube account to create a new user token manually.
+sonarlint-connection.token-error.description={productName} was not able to generate a token.
+sonarlint-connection.token-error.description2=Go back to your IDE and start again, or go to the {link} of your {productName} account to create a new user token manually.
sonarlint-connection.token-error.description2.link=Security section
sonarlint-connection.connection-error.title=Token created
@@ -3900,16 +3900,16 @@ sonarlint-connection.connection-error.token-name=Token name
sonarlint-connection.connection-error.token-value=Token value
sonarlint-connection.connection-error.next-steps=Next steps
sonarlint-connection.connection-error.step1=Copy the above token.
-sonarlint-connection.connection-error.step2=Go back to your IDE and paste the token in SonarLint.
+sonarlint-connection.connection-error.step2=Go back to your IDE and paste the token in SonarQube for IDE.
-sonarlint-connection.success.title=SonarLint connection is almost ready!
-sonarlint-connection.success.description=A new '{0}' token was created and sent to SonarLint in your IDE.
+sonarlint-connection.success.title=SonarQube for IDE connection is almost ready!
+sonarlint-connection.success.description=A new '{0}' token was created and sent to SonarQube for IDE in your IDE.
sonarlint-connection.success.last-step=Last step
sonarlint-connection.success.step=Go back to your IDE to complete the setup.
sonarlint-connection.unspecified-ide=an unspecified IDE
-sonarlint-connected-mode-doc=documentation about SonarLint Connected Mode
+sonarlint-connected-mode-doc=documentation about SonarQube for IDE Connected Mode
#------------------------------------------------------------------------------
#
# HELP
@@ -3927,7 +3927,6 @@ formatting.example.link.example=[link label](https://www.domain.com)
#------------------------------------------------------------------------------
keyboard_shortcuts_modal.title=Keyboard Shortcuts
keyboard_shortcuts_modal.disable_link=Disable shortcuts
-keyboard_shortcuts_modal.description= You can use the following shortcuts when navigating within SonarCloud
keyboard_shortcuts_modal.global= Global
keyboard_shortcuts_modal.global.open_search_bar= Open search bar
keyboard_shortcuts_modal.global.open_keyboard_shortcuts_modal= Open keyboard shortcuts modal
@@ -4059,8 +4058,8 @@ workspace.no_rule=The rule has been removed or never existed.
#
#------------------------------------------------------------------------------
marketplace.page=Marketplace
-marketplace.page.description=Discover the additional benefits offered in SonarQube Commercial Editions
-marketplace.page.description_best_edition=This edition includes access to all the SonarQube-SonarLint Ecosystem features!
+marketplace.page.description=Discover the additional benefits offered in SonarQube Server Commercial Editions
+marketplace.page.description_best_edition=This edition includes access to all the SonarQube Server-SonarQube for IDE Ecosystem features!
marketplace.page.you_are_running.community=You are currently running a Community Edition.
marketplace.page.you_are_running.developer=You are currently running a Developer Edition.
marketplace.page.you_are_running.enterprise=You are currently running an Enterprise Edition.
@@ -4159,7 +4158,7 @@ component_navigation.status.in_progress.admin.help=A background task is in progr
component_navigation.status.in_progress_X.admin.help=The {type} is in progress.
component_navigation.status.last_blocked_due_to_bad_license_X=Last analysis blocked due to an invalid license, which has since been corrected. Please reanalyze this {0}.
-component_navigation.pr_deco.error_detected_X=We've detected an issue with your configuration. Your SonarQube instance won't be able to perform any pull request decoration. {action}
+component_navigation.pr_deco.error_detected_X=We've detected an issue with your configuration. Your {productName} instance won't be able to perform any pull request decoration. {action}
component_navigation.pr_deco.action.check_project_settings=Please check your project settings.
component_navigation.pr_deco.action.contact_project_admin=Please contact your project administrator.
@@ -4243,8 +4242,8 @@ background_tasks.failing_count=Count of projects where processing of most recent
#
#------------------------------------------------------------------------------
project_dump.page=Import / Export
-project_dump.page.description1=Moving a project from one SonarQube instance to another is a 3 step operation:
-project_dump.page.description2=Export the project, copy the generated dump on the target server, and finally import that dump from this page on the target SonarQube instance.
+project_dump.page.description1=Moving a project from one {productName} instance to another is a 3 step operation:
+project_dump.page.description2=Export the project, copy the generated dump on the target server, and finally import that dump from this page on the target {productName} instance.
project_dump.page.description_without_import1=Export project issues, measures and measure history for import into an Enterprise Edition or higher instance of the same version and similar configuration. The export file will be generated to the file system. It must then be copied to the target file system for import.
project_dump.page.description_without_import2=The export file will be generated to the file system. It must then be copied to the target file system for import.
project_dump.refresh=Refresh
@@ -4296,7 +4295,7 @@ system.log_level.warning.short=Current logs level has performance impacts, get b
system.log_level.info=Your selection does not affect the Search Engine.
system.logs_level=Logs level
system.logs_level.change=Change logs level
-system.new_version_available=A new version of SonarQube is available.
+system.new_version_available=A new version of {productName} is available.
system.restart_does_not_reload_sonar_properties=Also note that a restart will not reload the sonar.properties file.
system.see_whats_new=See what's new!
system.release_notes=Release Notes
@@ -4305,7 +4304,7 @@ system.restart_in_progress=Restart in progress
system.restart_server=Restart Server
system.instance_restarting={instance} restart is in progress. Ongoing {link} are completing.
system.search_nodes_title=Search Nodes
-system.see_sonarqube_downloads=See All SonarQube Downloads
+system.see_sonarqube_downloads=See All {productName} Downloads
system.not_production_database_warning=This server ID is valid only for the embedded database, which should be considered disposable. Consider configuring an external database for long-term use prior to requesting your license.
system.server_id=Server ID
system.set_log_level=Set logs level
@@ -4427,11 +4426,11 @@ overview.project.main_branch_no_lines_of_code=The main branch has no lines of co
overview.project.main_branch_empty=The main branch of this project is empty.
overview.project.branch_needs_new_analysis=The branch data is incomplete. Run a new analysis to update it.
overview.project.last_analysis.date_time=Last analysis started on {0}
-overview.project.next_steps.set_up_pr_deco_and_ci.admin=To benefit from more of SonarQube's features, {link_ci} and set up DevOps platform integration in your {link_project_settings}.
-overview.project.next_steps.set_up_pr_deco_and_ci=To benefit from more of SonarQube's features, {link_ci} and ask a project administrator to set up DevOps platform integration.
-overview.project.next_steps.set_up_pr_deco.admin=To benefit from more of SonarQube's features, set up DevOps platform integration in your {link_project_settings}.
-overview.project.next_steps.set_up_pr_deco=To benefit from more of SonarQube's features, ask a project administrator to set up DevOps platform integration.
-overview.project.next_steps.set_up_ci=To benefit from more of SonarQube's features, {link}.
+overview.project.next_steps.set_up_pr_deco_and_ci.admin=To benefit from more of {productName}'s features, {link_ci} and set up DevOps platform integration in your {link_project_settings}.
+overview.project.next_steps.set_up_pr_deco_and_ci=To benefit from more of {productName}'s features, {link_ci} and ask a project administrator to set up DevOps platform integration.
+overview.project.next_steps.set_up_pr_deco.admin=To benefit from more of {productName}'s features, set up DevOps platform integration in your {link_project_settings}.
+overview.project.next_steps.set_up_pr_deco=To benefit from more of {productName}'s features, ask a project administrator to set up DevOps platform integration.
+overview.project.next_steps.set_up_ci=To benefit from more of {productName}'s features, {link}.
overview.project.next_steps.links.project_settings=project settings
overview.project.next_steps.links.set_up_ci=set up analysis in your favorite CI
overview.project.software_impact.has_rating=Software Quality {softwareQuality} has rating {rating}
@@ -4481,14 +4480,14 @@ overview.deprecated_profile=This Quality Profile uses {0} deprecated rules and s
overview.deleted_profile={0} has been deleted since the last analysis.
overview.link_to_x_profile_y=Go to {0} profile "{1}"
-overview.sonarlint_ad.header=Catch issues before they fail your Quality Gate with our IDE extension, SonarLint
+overview.sonarlint_ad.header=Catch issues before they fail your Quality Gate with our IDE extension, SonarQube for IDE
overview.sonarlint_ad.details_1=The power of Sonar analyzers directly as you type
overview.sonarlint_ad.details_2=No need to wait for your PR to pass all checks
overview.sonarlint_ad.details_3=Repair flagged issues in real-time with quick fixes
overview.sonarlint_ad.details_4=12 major IDE's supported (including key JetBrains and Microsoft IDE's
overview.sonarlint_ad.details_5=Free forever
-overview.sonarlint_ad.learn_more=Learn more about SonarLint
-overview.sonarlint_ad.close_promotion=Close SonarLint promotion
+overview.sonarlint_ad.learn_more=Learn more about SonarQube for IDE
+overview.sonarlint_ad.close_promotion=Close SonarQube for IDE promotion
overview.badges.get_badge=Badges
overview.badges.title=Get project badges
@@ -4521,7 +4520,7 @@ overview.badges.renew=Renew Token
overview.badges.renew.description=If your project badge security token has leaked to an unsafe environment, you can renew it:
overview.badges.deprecated_badge_x_y=Badges displaying {0} are deprecated and will be removed in a future version. Please choose another badge for your {1}.
-overview.quality_profiles_update_after_sq_upgrade.message=Upgrade to SonarQube {sqVersion} has updated your Quality Profiles. Issues on your project may have been affected. {link}
+overview.quality_profiles_update_after_sq_upgrade.message=Upgrade to {productName} {sqVersion} has updated your Quality Profiles. Issues on your project may have been affected. {link}
overview.quality_profiles_update_after_sq_upgrade.link=See more details
overview.activity.variations.new_analysis=New analysis:
@@ -4544,7 +4543,7 @@ guiding.cayc_promotion.2.content.1=Your team or organization decides when a new
guiding.cayc_promotion.3.title=Green is clean
guiding.cayc_promotion.3.content.1=Quality Gate Status tells you if your new code is clean or not. Keep it green as often as possible, and your project will be production-ready.
guiding.cayc_promotion.4.title=Clean at all levels
-guiding.cayc_promotion.4.content.1=With SonarLint, clean code as you write it in your {value}.
+guiding.cayc_promotion.4.content.1=With SonarQube for IDE, clean code as you write it in your {value}.
guiding.cayc_promotion.4.content.2=When a feature is ready, analyze your {value} (commercial edition only) and make sure no issue is missed.
guiding.cayc_promotion.4.content.3=Finally, rely on a thorough {value} analysis to ensure the new code is clean.
guiding.replay_tour_button.1.title=Replay tour
@@ -4767,11 +4766,11 @@ new_code_definition.reference_branch.description=Choose a branch as the baseline
new_code_definition.reference_branch.usecase=Recommended for projects using feature branches.
new_code_definition.reference_branch.notice=The main branch will be set as the reference branch when the project is created. You will be able to choose another branch as the reference branch when your project will have more branches.
-new_code_definition.auto_update.branch.message=The new code definition of the following branch(es) was automatically changed on {date}, following a SonarQube upgrade, as it was exceeding the maximum value: {branchesList} {link}
+new_code_definition.auto_update.branch.message=The new code definition of the following branch(es) was automatically changed on {date}, following a {productName} upgrade, as it was exceeding the maximum value: {branchesList} {link}
new_code_definition.auto_update.branch.list_item={branchName}: Number of days was changed from {previousDays} to {days}.
-new_code_definition.auto_update.global.message=The global new code definition was automatically changed from {previousDays} to {days} days on {date}, following a SonarQube upgrade, as it was exceeding the maximum value. {link}
-new_code_definition.auto_update.ncd_page.message=The number of days was automatically changed from {previousDays} to {days} on {date}, following a SonarQube upgrade, as it was exceeding the maximum value. {link}
-new_code_definition.auto_update.project.message=This project's new code definition was automatically changed from {previousDays} to {days} days on {date}, following a SonarQube upgrade, as it was exceeding the maximum value. {link}
+new_code_definition.auto_update.global.message=The global new code definition was automatically changed from {previousDays} to {days} days on {date}, following a {productName} upgrade, as it was exceeding the maximum value. {link}
+new_code_definition.auto_update.ncd_page.message=The number of days was automatically changed from {previousDays} to {days} on {date}, following a {productName} upgrade, as it was exceeding the maximum value. {link}
+new_code_definition.auto_update.project.message=This project's new code definition was automatically changed from {previousDays} to {days} days on {date}, following a {productName} upgrade, as it was exceeding the maximum value. {link}
new_code_definition.auto_update.review_link=Review new code definition
#------------------------------------------------------------------------------
@@ -4785,7 +4784,7 @@ onboarding.alm.bitbucketcloud=Bitbucket Cloud
onboarding.alm.gitlab=GitLab
onboarding.project_analysis.header=Analyze your project
-onboarding.project_analysis.description=We initialized your project on SonarQube, now it's up to you to launch analyses!
+onboarding.project_analysis.description=We initialized your project on {productName}, now it's up to you to launch analyses!
onboarding.project_analysis.guide_to_integrate_pipelines=follow the guide to integrating with Pipelines
onboarding.create_project.manual.step1=1 of 2
@@ -4793,7 +4792,7 @@ onboarding.create_project.manual.step2=2 of 2
onboarding.create_project.manual.title=Create a local project
onboarding.create_project.select_method=How do you want to create your project?
onboarding.create_project.select_method.manually=Are you just testing or have an advanced use-case? Create a local project.
-onboarding.create_project.select_method.devops_platform=Do you want to benefit from all of SonarQube's features (like repository import and Pull Request decoration)?
+onboarding.create_project.select_method.devops_platform=Do you want to benefit from all of {productName}'s features (like repository import and Pull Request decoration)?
onboarding.create_project.select_method.devops_platform_second=Create your project from your favorite DevOps platform.
onboarding.create_project.select_method.no_alm_yet.admin=First, you need to set up a DevOps platform configuration.
@@ -4837,10 +4836,10 @@ onboarding.create_project.select_all_repositories=Select all available repositor
onboarding.create_application.key.description=If specified, this value is used as the key instead of generating it from the name of the Application. Only letters, digits, dashes and underscores can be used.
onboarding.create_project.pat_form.title=Grant access to your repositories
-onboarding.create_project.pat_form.help.azure=SonarQube needs a personal access token to access and list your repositories from Azure DevOps.
-onboarding.create_project.pat_form.help.bitbucket=SonarQube needs a personal access token to access and list your repositories from Bitbucket Server.
-onboarding.create_project.pat_form.help.bitbucket_cloud=SonarQube needs an app password to access and list your repositories from Bitbucket Cloud.
-onboarding.create_project.pat_form.help.gitlab=SonarQube needs a personal access token to access and list your projects from GitLab.
+onboarding.create_project.pat_form.help.azure={productName} needs a personal access token to access and list your repositories from Azure DevOps.
+onboarding.create_project.pat_form.help.bitbucket={productName} needs a personal access token to access and list your repositories from Bitbucket Server.
+onboarding.create_project.pat_form.help.bitbucket_cloud={productName} needs an app password to access and list your repositories from Bitbucket Cloud.
+onboarding.create_project.pat_form.help.gitlab={productName} needs a personal access token to access and list your projects from GitLab.
onboarding.create_project.pat_form.pat_required=Please enter a personal access token
onboarding.create_project.wrong_binding_count=You must have at least 1 {alm} instance configured in order to use this method, but none were found. Either create a local project, or contact your system administrator.
onboarding.create_project.wrong_binding_count.admin=You must have at least 1 {alm} instance configured in order to use this method. You can configure instances under {url}.
@@ -4859,22 +4858,22 @@ onboarding.create_project.pat.expired.info_message_contact=If this does not fix
onboarding.create_project.pat_help.instructions.azure=To create a Personal Access Token on Azure, {link} and make sure to select the “Code (Read & Write)” scope.
onboarding.create_project.pat_help.instructions.link.azure=generate a token
-onboarding.create_project.pat_help.instructions.gitlab=To create a Personal Access Token on GitLab, {link} by setting a name, for example “SonarQube” and selecting the “read_api” scope.
+onboarding.create_project.pat_help.instructions.gitlab=To create a Personal Access Token on GitLab, {link} by setting a name, for example “{productName}” and selecting the “read_api” scope.
onboarding.create_project.pat_help.instructions.gitlab.link=generate a token
onboarding.enter_username.instructions.bitbucket_cloud=You can find your username in your {link}
onboarding.enter_username.instructions.bitbucket_cloud.link=BitBucket profile settings
-onboarding.create_project.enter_password.instructions.bitbucket_cloud=To create an app password on BitBucket, {link} by setting a name, for example “SonarQube” and selecting the “Repositories: Read” permissions.
+onboarding.create_project.enter_password.instructions.bitbucket_cloud=To create an app password on BitBucket, {link} by setting a name, for example “{productName}” and selecting the “Repositories: Read” permissions.
onboarding.create_project.enter_password.instructions.bitbucket_cloud.link=add an app password
-onboarding.create_project.pat_help.instructions.bitbucket_server=To create a Personal Access Token on BitBucket Server, go to {link} and click on “Create token”. Set a name, for example “SonarQube” and select the following permissions “Projects: Read” “Repositories: Read”.
+onboarding.create_project.pat_help.instructions.bitbucket_server=To create a Personal Access Token on BitBucket Server, go to {link} and click on “Create token”. Set a name, for example “{productName}” and select the following permissions “Projects: Read” “Repositories: Read”.
onboarding.create_project.pat_help.instructions.bitbucket_server.link=HTTP access tokens
-onboarding.create_project.pat_help.instructions2.bitbucket=Set a name, for example "SonarQube", and select the following permissions:
-onboarding.create_project.pat_help.instructions2.bitbucketcloud=Set a name, for example "SonarQube", and select the following permissions:
+onboarding.create_project.pat_help.instructions2.bitbucket=Set a name, for example "{productName}", and select the following permissions:
+onboarding.create_project.pat_help.instructions2.bitbucketcloud=Set a name, for example "{productName}", and select the following permissions:
-onboarding.create_project.pat_help.instructions2.gitlab=Set a name, for example "SonarQube", and select the following scope:
+onboarding.create_project.pat_help.instructions2.gitlab=Set a name, for example "{productName}", and select the following scope:
onboarding.create_project.pat_help.gitlab.read_api_permission=read_api
onboarding.create_project.no_bbs_projects=No projects could be fetched from Bitbucket Server. Contact your system administrator, or {link}.
@@ -4919,12 +4918,12 @@ onboarding.create_project.bitbucket.title=Bitbucket Server project onboarding
onboarding.create_project.bitbucket.subtitle=Import projects from one of your Bitbucket server workspaces
onboarding.create_project.bitbucket.subtitle.with_monorepo=Import projects from one of your Bitbucket server workspaces or {monorepoSetupLink}.
onboarding.create_project.x_repositories_selected={count} {count, plural, one {repository} other {repositories}} selected
-onboarding.create_project.x_repository_created={count} {count, plural, one {repository} other {repositories}} will be created as {count, plural, one {a project} other {projects}} in SonarQube
+onboarding.create_project.x_repository_created={count} {count, plural, one {repository} other {repositories}} will be created as {count, plural, one {a project} other {projects}} in {productName}
onboarding.create_project.please_dont_leave=If you leave the page the import could fail. Are you sure you want to leave?
onboarding.create_project.import_in_progress={count} of {total} projects imported. Please do not close this page until the import is complete.
onboarding.create_project.monorepo.title={almName} monorepo project onboarding
-onboarding.create_project.monorepo.subtitle=Create multiple SonarQube projects corresponding to the same monorepo and bound to the same repository.
+onboarding.create_project.monorepo.subtitle=Create multiple {productName} projects corresponding to the same monorepo and bound to the same repository.
onboarding.create_project.monorepo.doc_link=Learn more and get help setting up your monorepo
onboarding.create_project.monorepo.choose_organization_and_repository=Choose the organization and the repository
onboarding.create_project.monorepo.choose_dop_setting=Choose the {almKey} configuration
@@ -4932,8 +4931,8 @@ onboarding.create_project.monorepo.choose_organization=Choose the organization
onboarding.create_project.monorepo.choose_organization.placeholder=List of organizations
onboarding.create_project.monorepo.choose_repository=Choose the repository
onboarding.create_project.monorepo.choose_repository.placeholder=List of repositories
-onboarding.create_project.monorepo.choose_repository.no_already_bound_projects=This repository has no imported projects in SonarQube
-onboarding.create_project.monorepo.choose_repository.existing_already_bound_projects=This repository has already been imported, and it's linked to these projects in SonarQube:
+onboarding.create_project.monorepo.choose_repository.no_already_bound_projects=This repository has no imported projects in {productName}
+onboarding.create_project.monorepo.choose_repository.existing_already_bound_projects=This repository has already been imported, and it's linked to these projects in {productName}:
onboarding.create_project.monorepo.no_orgs=We couldn't load any organizations with your key. Contact an administrator.
onboarding.create_project.monorepo.no_orgs_admin=We couldn't load any organizations. Make sure the {almKey} App is installed in at least one organization and check the {almKey} instance configuration in the {link}.
onboarding.create_project.monorepo.no_projects=No projects could be fetch from {almKey}. Contact your system administrator.
@@ -5073,12 +5072,12 @@ onboarding.tutorial.ci_outro.all_set.title=You're all set!
onboarding.tutorial.ci_outro.done=And you are done!
onboarding.tutorial.ci_outro.refresh_text=If everything is running successfully, once the analysis is complete you'll be redirected to the Overview page of your project where the new analysis results will be displayed. This can take a few minutes.
onboarding.tutorial.ci_outro.commit=Commit and push your code to start the analysis.
-onboarding.tutorial.ci_outro.commit.why.gitlab=Each new push you make on your branches or merge requests will trigger a new analysis in SonarQube. We will decorate merge requests directly on GitLab for you.
-onboarding.tutorial.ci_outro.commit.why.github=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. We will decorate pull requests directly on GitHub for you.
-onboarding.tutorial.ci_outro.commit.why.bitbucket=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. We will decorate pull requests directly on Bitbucket for you.
-onboarding.tutorial.ci_outro.commit.why.bitbucketcloud=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. We will decorate pull requests directly on Bitbucket for you.
-onboarding.tutorial.ci_outro.commit.why.azure=Each new push you make on your branches or pull requests will trigger a new analysis in SonarQube. We will decorate pull requests directly on Azure for you.
-onboarding.tutorial.ci_outro.commit.why.no_branches=Each new push you make on your main branch will trigger a new analysis in SonarQube.
+onboarding.tutorial.ci_outro.commit.why.gitlab=Each new push you make on your branches or merge requests will trigger a new analysis in {productName}. We will decorate merge requests directly on GitLab for you.
+onboarding.tutorial.ci_outro.commit.why.github=Each new push you make on your branches or pull requests will trigger a new analysis in {productName}. We will decorate pull requests directly on GitHub for you.
+onboarding.tutorial.ci_outro.commit.why.bitbucket=Each new push you make on your branches or pull requests will trigger a new analysis in {productName}. We will decorate pull requests directly on Bitbucket for you.
+onboarding.tutorial.ci_outro.commit.why.bitbucketcloud=Each new push you make on your branches or pull requests will trigger a new analysis in {productName}. We will decorate pull requests directly on Bitbucket for you.
+onboarding.tutorial.ci_outro.commit.why.azure=Each new push you make on your branches or pull requests will trigger a new analysis in {productName}. We will decorate pull requests directly on Azure for you.
+onboarding.tutorial.ci_outro.commit.why.no_branches=Each new push you make on your main branch will trigger a new analysis in {productName}.
onboarding.tutorial.ci_outro.refresh=This page will then refresh with your analysis results.
onboarding.tutorial.ci_outro.refresh.why=If the page doesn't refresh after a while, please double-check the analysis configuration, and check your logs.
onboarding.tutorial.other.project_key.sentence=Create a {file} file in your repository and paste the following code:
@@ -5086,7 +5085,7 @@ onboarding.tutorial.other.project_key.monorepo.sentence=Create a {file} file at
onboarding.tutorial.other.project_key.monorepo.info=Repeat this step for all the projects in your monorepo
onboarding.tutorial.cfamilly.compilation_database_info=If you have trouble using the Build Wrapper, you can try one of the alternative methods to {link}.
onboarding.tutorial.cfamilly.compilation_database_info.link=generate a compilation database
-onboarding.tutorial.cfamily.examples_repositories_description=Check out our C and C++ sample projects with SonarQube analysis configured
+onboarding.tutorial.cfamily.examples_repositories_description=Check out our C and C++ sample projects with {productName} analysis configured
onboarding.tutorial.page.title=Analysis Method
@@ -5110,7 +5109,7 @@ onboarding.tutorial.breadcrumbs.gitlab-ci=GitLab CI
onboarding.tutorial.breadcrumbs.azure-pipelines=Azure Pipelines
onboarding.tutorial.breadcrumbs.bitbucket-pipelines=Bitbucket Pipelines
onboarding.mode.help.manual=Use this for testing or advanced use-case. Other modes are recommended to help you set up your CI environment.
-onboarding.mode.help.otherci=SonarQube integrates with your workflow no matter which CI tool you're using.
+onboarding.mode.help.otherci={productName} integrates with your workflow no matter which CI tool you're using.
onboarding.tutorial.with.yaml.gradle=Update your {groovy} or {kotlin} file with the {sq} plugin and its configuration:
@@ -5158,7 +5157,7 @@ onboarding.tutorial.with.gitlab_ci.project_key.objectivec.step2=Create a {file}
onboarding.tutorial.with.gitlab_ci.variables.title=Add environment variables
onboarding.tutorial.with.gitlab_ci.variables.description.link=Settings > CI/CD > Variables
-onboarding.tutorial.with.gitlab_ci.variables.section.title=Define the SonarQube Token environment variable.
+onboarding.tutorial.with.gitlab_ci.variables.section.title=Define the {productName} Token environment variable.
onboarding.tutorial.with.gitlab_ci.variables.section.description=In GitLab, go to {link} to add the following variable and make sure it is available for your project:
onboarding.tutorial.with.gitlab_ci.variables.edit.token.tooltip=Use an existing token or generate a new one.
onboarding.tutorial.with.gitlab_ci.variables.step1=Key {value} {extra}
@@ -5167,13 +5166,13 @@ onboarding.tutorial.with.gitlab_ci.variables.step3=Uncheck the {value} checkbox.
onboarding.tutorial.with.gitlab_ci.variables.step3.value=Protect Variable
onboarding.tutorial.with.gitlab_ci.variables.section.step4=Check the {value} checkbox.
onboarding.tutorial.with.gitlab_ci.variables.section.step4.value=Mask Variable
-onboarding.tutorial.with.gitlab_ci.variables.section2.title=Define the SonarQube URL environment variable.
+onboarding.tutorial.with.gitlab_ci.variables.section2.title=Define the {productName} URL environment variable.
onboarding.tutorial.with.gitlab_ci.variables.section2.description=Still in {link} add a new variable and make sure it is available for your project:
onboarding.tutorial.with.gitlab_ci.variables.section2.step4=Leave the {value} checkbox unchecked.
onboarding.tutorial.with.gitlab_ci.yaml.title=Create or update the configuration file
onboarding.tutorial.with.gitlab_ci.yaml.description=Create or update your {filename} file with the following content.
onboarding.tutorial.with.gitlab_ci.yaml.filename=.gitlab-ci.yml
-onboarding.tutorial.with.gitlab_ci.yaml.baseconfig=Note that this is a minimal base configuration to run a SonarQube analysis on your main branch and merge requests, and fetch the vulnerability report (if applicable).
+onboarding.tutorial.with.gitlab_ci.yaml.baseconfig=Note that this is a minimal base configuration to run a {productName} analysis on your main branch and merge requests, and fetch the vulnerability report (if applicable).
onboarding.tutorial.with.gitlab_ci.yaml.existing=If you already have a pipeline configured and running, you might want to add the example above to your existing yml file.
onboarding.tutorial.with.gitlab_ci.yaml.premium=GitLab vulnerability report is only available with GitLab Ultimate. You may safely remove the sonarqube-vulnerability-report stage if you have not subscribed to this service.
@@ -5188,7 +5187,7 @@ onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source.bitbucketcloud=Bi
onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source.github=GitHub Branch Source plugin for Jenkins - version 2.7.1 or later
onboarding.tutorial.with.jenkins.prereqs.plugins.branch_source.gitlab=GitLab Branch Source plugin for Jenkins - version 1.5.3 or later
onboarding.tutorial.with.jenkins.prereqs.plugins.gitlab_plugin=GitLab plugin for Jenkins - version 1.5.13 or later
-onboarding.tutorial.with.jenkins.prereqs.plugins.sonar_scanner=SonarQube Scanner plugin for Jenkins - version 2.11 or later
+onboarding.tutorial.with.jenkins.prereqs.plugins.sonar_scanner={productName} Scanner plugin for Jenkins - version 2.11 or later
onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide=For a step by step guide on installing and configuring those plugins in Jenkins, visit the {link} documentation page.
onboarding.tutorial.with.jenkins.prereqs.step_by_step_guide.link=Analysis Prerequisites
onboarding.tutorial.with.jenkins.prereqs.following_are_recommendations=We recommend using the configuration in the following steps for the best results, but you can customize it as needed.
@@ -5406,13 +5405,13 @@ onboarding.tutorial.with.jenkins.dotnet.scanner.prereqs.step5.sentence=Under {in
onboarding.tutorial.with.jenkins.dotnet.scanner.prereqs.step5.sentence.install_from=Install from GitHub
onboarding.tutorial.with.jenkins.dotnet.scanner.prereqs.step5.sentence.install_auto=Install automatically
-onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence=Make sure to replace {default} with the name you gave to your SonarQube Scanner tool {in_jenkins}.
+onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence=Make sure to replace {default} with the name you gave to your {productName} Scanner tool {in_jenkins}.
onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence.default=SonarScanner
onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.replace.sentence.in_jenkins=in Jenkins
-onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help1.sentence=To get the name of your SonarQube Scanner tool in Jenkins, navigate to {path}.
+onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help1.sentence=To get the name of your {productName} Scanner tool in Jenkins, navigate to {path}.
onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help1.sentence.path=Manage Jenkins > Global Tool Configuration
onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence=The name is located under the {path} section, in the {name} field.
-onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence.path=SonarQube Scanner > SonarQube Scanner installations
+onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence.path={productName} Scanner > {productName} Scanner installations
onboarding.tutorial.with.jenkins.jenkinsfile.other.step3.help2.sentence.name=Name
onboarding.tutorial.with.jenkins.jenkinsfile.cfamilly.agent_setup=We assume the Jenkins agent has the necessary tools to build your project.
@@ -5420,16 +5419,16 @@ onboarding.tutorial.with.jenkins.jenkinsfile.cfamilly.agent_setup=We assume the
onboarding.tutorial.with.azure_pipelines.os=What is your agent host?
onboarding.tutorial.with.azure_pipelines.architecture=What is your agent architecture?
onboarding.tutorial.with.azure_pipelines.title=Analyze your project with Azure DevOps Pipelines
-onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title=Install SonarQube extension for Azure DevOps
+onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.title=Install {productName} extension for Azure DevOps
onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence=From your Azure DevOps instance, navigate to the Visual Studio Marketplace and install the {link} by clicking the {button} button.
-onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link=SonarQube extension
+onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.link={productName} extension
onboarding.tutorial.with.azure_pipelines.ExtensionInstallation.sentence.button=Get it free
-onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title=Add a new SonarQube Service Endpoint in your project
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.title=Add a new {productName} Service Endpoint in your project
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1.sentence=In Azure DevOps, go to {menu}.
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step1.sentence.menu=Project settings > Service connections
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2.sentence=Add a new service connection of type {type}.
-onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2.sentence.type=SonarQube
-onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence=Enter your SonarQube server url: {url} {button}
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step2.sentence.type={productName}
+onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step3.sentence=Enter your {productName} server url: {url} {button}
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step4.sentence=Enter an existing token, or a newly generated one
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step5.sentence=Enter a memorable connection name.
onboarding.tutorial.with.azure_pipelines.ServiceEndpoint.step6.sentence=Create the service connection.
@@ -5457,7 +5456,7 @@ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.pipelin
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.task=Prepare Analysis Configuration
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.sentence.before=before
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence=Select the {endpoint} you created in Step 2.
-onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint=SonarQube server endpoint
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.endpoint.sentence.endpoint={productName} server endpoint
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis=Under {section}, select {run_analysis_value}.
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.section=Choose the way to run the analysis
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.prepare.run_analysis.values.dotnet=Integrate with MSBuild
@@ -5486,11 +5485,11 @@ onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java_installer.pre-insta
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java=Edit or add a new {0} task:
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence=Under {section}, check {option}
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.section=Code Analysis
-onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.option=Run SonarQube or SonarCloud Analysis
-onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence=Add a new {task} task to publish SonarQube's Quality Gate results on your build pipeline summary.
-onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1=This task may increase your build time as your pipeline will have to wait for SonarQube to process the analysis report. It is highly recommended but optional.
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.java.settings.sentence.option=Run {productName} Analysis
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence=Add a new {task} task to publish {productName}'s Quality Gate results on your build pipeline summary.
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.info.sentence1=This task may increase your build time as your pipeline will have to wait for {productName} to process the analysis report. It is highly recommended but optional.
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.publish_qg.sentence.task=Publish Quality Gate Result
-onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence=Under the {tab} tab of your pipeline, check {continuous_integration} and select all the branches for which you want the SonarQube analysis to run automatically.
+onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence=Under the {tab} tab of your pipeline, check {continuous_integration} and select all the branches for which you want the {productName} analysis to run automatically.
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.tab=Triggers
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.sentence.continuous_integration=Enable continuous integration
onboarding.tutorial.with.azure_pipelines.BranchAnalysis.continous_integration.no_branches.sentence=Under the {tab} tab of your pipeline, check {continuous_integration} and select the main branch
@@ -5524,13 +5523,13 @@ branch_like_navigation.orphan_pull_requests=Orphan Pull Requests
branch_like_navigation.orphan_pull_requests.tooltip=When the base of a Pull Request is deleted, this Pull Request becomes orphan.
branch_like_navigation.for_merge_into_x_from_y=for merge into {target} from {branch}
branch_like_navigation.for_merge_into_x_from_y.title=for merge into {0} from {1}
-branch_like_navigation.no_branch_support.title=Get the most out of SonarQube with branch and PR/MR analysis
-branch_like_navigation.no_branch_support.title.pr=Get the most out of SonarQube with branch and PR analysis
-branch_like_navigation.no_branch_support.title.mr=Get the most out of SonarQube with branch and MR analysis
-branch_like_navigation.no_branch_support.content=With Developer Edition you can analyze every pull/merge request. You can also watch the quality of your release branches by analyzing each one individually in SonarQube.
-branch_like_navigation.no_branch_support.content_x.pr=With Developer Edition you can analyze every pull request and get the results in {0}. You can also watch the quality of your release branches by analyzing each one individually in SonarQube.
-branch_like_navigation.no_branch_support.content_x.mr=With Developer Edition you can analyze every merge request and get the results in {0}. You can also watch the quality of your release branches by analyzing each one individually in SonarQube.
-branch_like_navigation.only_one_branch.title=Learn how to analyze branches in SonarQube
+branch_like_navigation.no_branch_support.title=Get the most out of {productName} with branch and PR/MR analysis
+branch_like_navigation.no_branch_support.title.pr=Get the most out of {productName} with branch and PR analysis
+branch_like_navigation.no_branch_support.title.mr=Get the most out of {productName} with branch and MR analysis
+branch_like_navigation.no_branch_support.content=With Developer Edition you can analyze every pull/merge request. You can also watch the quality of your release branches by analyzing each one individually in {productName}.
+branch_like_navigation.no_branch_support.content_x.pr=With Developer Edition you can analyze every pull request and get the results in {alm}. You can also watch the quality of your release branches by analyzing each one individually in {productName}.
+branch_like_navigation.no_branch_support.content_x.mr=With Developer Edition you can analyze every merge request and get the results in {alm}. You can also watch the quality of your release branches by analyzing each one individually in {productName}.
+branch_like_navigation.only_one_branch.title=Learn how to analyze branches in {productName}
branch_like_navigation.only_one_branch.content=Quickly set up branch analysis and get separate insights for each of your branches and Pull Requests.
branch_like_navigation.only_one_branch.documentation=Branches documentation
branch_like_navigation.only_one_branch.pr_analysis=Pull Request analysis
@@ -5638,8 +5637,8 @@ users.login_start_with_letter_or_number=Login should start with _ or alphanumeri
users.email=Email
users.email.invalid=Invalid email
users.last_connection=Last connection
-users.last_sonarlint_connection=Last SonarLint connection
-users.last_sonarlint_connection.help_text=The time of the last connection from SonarLint indicates that the user used SonarLint in connected mode.
+users.last_sonarlint_connection=Last SonarQube for IDE connection
+users.last_sonarlint_connection.help_text=The time of the last connection from SonarQube for IDE indicates that the user used SonarQube for IDE in connected mode.
users.update_users_groups=Update {0}'s group membership
users.view_users_groups=View {0}'s group membership
users.update_groups=Update Groups
@@ -5654,11 +5653,11 @@ users.remove=Remove user
users.search_description=Search users by login or name
users.activity_filter.label=Filter users by activity
users.activity_filter.placeholder=All users
-users.activity_filter.helptext.sonarqube=Users are considered active if they connected to SonarQube at least once in the past 30 days.
-users.activity_filter.helptext.sonarlint=Users are considered active with SonarLint if they used SonarLint in connected mode at least once in the past 30 days.
+users.activity_filter.helptext.sonarqube=Users are considered active if they connected to {productName} at least once in the past 30 days.
+users.activity_filter.helptext.sonarlint=Users are considered active with SonarQube for IDE if they used SonarQube for IDE in connected mode at least once in the past 30 days.
users.activity_filter.all_users=All users
-users.activity_filter.active_sonarlint_users=Active users with SonarLint
-users.activity_filter.active_sonarqube_users=Active users without SonarLint
+users.activity_filter.active_sonarlint_users=Active users with SonarQube for IDE
+users.activity_filter.active_sonarqube_users=Active users without SonarQube for IDE
users.activity_filter.inactive_users=Inactive users
users.tokens=Tokens
users.user_X_tokens=Tokens of {user}
@@ -5691,12 +5690,12 @@ users.tokens.new_token_created=New token "{0}" has been created. Make sure you c
users.generate_new_token=Generate New Token
users.new_token=New token value
users.change_admin_password.page=Change password
-users.change_admin_password.instance_is_at_risk=Secure your SonarQube instance
+users.change_admin_password.instance_is_at_risk=Secure your {productName} instance
users.change_admin_password.header=Default Administrator credentials are still used
-users.change_admin_password.description=Your SonarQube instance is still using default administrator credentials. You must change the password for the 'admin' account to secure your SonarQube instance.
+users.change_admin_password.description=Your {productName} instance is still using default administrator credentials. You must change the password for the 'admin' account to secure your {productName} instance.
users.change_admin_password.form.header=Change the password for user 'admin'
users.change_admin_password.form.success=The admin user's password was successfully changed.
-users.change_admin_password.form.continue_to_app=Continue to SonarQube
+users.change_admin_password.form.continue_to_app=Continue to {productName}
users.filter.by=Filter by
#------------------------------------------------------------------------------
@@ -5734,7 +5733,7 @@ maintenance.migration_not_supported.text=Migration is not supported on embedded
maintenance.upgrade_database=Upgrade Database
maintenance.upgrade_database.1=The database upgrade can take several minutes.
maintenance.upgrade_database.2=It is mandatory to back up database before upgrading.
-maintenance.upgrade_database.3=Make sure you have followed the steps from the SonarQube Upgrade guide.
+maintenance.upgrade_database.3=Make sure you have followed the steps from the {productName} Upgrade guide.
maintenance.upgrade=Upgrade
maintenance.database_migration=Database Migration
maintenance.database_is_up_to_date=Database is up-to-date
@@ -5743,14 +5742,14 @@ maintenance.sonarqube_is_down.text=Something went wrong. Please contact your sys
maintenance.try_again=Try Again
maintenance.is_under_maintenance={instance} is under maintenance
maintenance.sonarqube_is_under_maintenance.1=While waiting, install {link} in your IDE!
-maintenance.sonarqube_is_under_maintenance_link.1=SonarLint
+maintenance.sonarqube_is_under_maintenance_link.1=SonarQube for IDE
maintenance.sonarqube_is_under_maintenance.2=If you are an administrator and have no idea why this message is being shown, you should read the {link}.
maintenance.sonarqube_is_under_maintenance_link.2=upgrade guide
maintenance.is_starting={instance} is starting
maintenance.is_up={instance} is up
maintenance.all_systems_opetational=All systems operational.
maintenance.is_offline={instance} is offline
-maintenance.sonarqube_is_offline.text=The connection to SonarQube is lost. Please contact your system administrator.
+maintenance.sonarqube_is_offline.text=The connection to {productName} is lost. Please contact your system administrator.
maintenance.running.progress={completed} migrations completed out of {total}
maintenance.running.estimate=Estimated completion time: {date}
@@ -5767,13 +5766,13 @@ indexation.progression={0} out of {1} projects reindexed.
indexation.progression_with_error={count} out of {total} projects reindexed with some {link}.
indexation.progression_with_error.link=tasks failing
indexation.completed=All project data has been reloaded.
-indexation.completed_with_error=SonarQube completed the reload of project data. Some {link} causing some projects to remain unavailable.
+indexation.completed_with_error={productName} completed the reload of project data. Some {link} causing some projects to remain unavailable.
indexation.completed_with_error.link=tasks failed or canceled
indexation.admin_link=See {link} for more information.
indexation.page_unavailable.title.issues=Issues page is temporarily unavailable
indexation.page_unavailable.title.portfolios=Portfolios page is temporarily unavailable
indexation.page_unavailable.title={componentQualifier} {componentName} is temporarily unavailable
-indexation.page_unavailable.description=SonarQube is reindexing project data.
+indexation.page_unavailable.description={productName} is reindexing project data.
indexation.page_unavailable.description.additional_information=This page is unavailable until this process is complete. {link}
indexation.filter_unavailable.description=This filter is unavailable until this process is complete.
indexation.learn_more=Learn more: