diff --git a/idea/customization/base/src/IntelliJIdeaExternalResourceUrls.kt b/idea/customization/base/src/IntelliJIdeaExternalResourceUrls.kt index ae1ffeafcf7f..7a5c43b66661 100644 --- a/idea/customization/base/src/IntelliJIdeaExternalResourceUrls.kt +++ b/idea/customization/base/src/IntelliJIdeaExternalResourceUrls.kt @@ -9,10 +9,10 @@ import com.intellij.util.Urls class IntelliJIdeaExternalResourceUrls : BaseJetBrainsExternalProductResourceUrls() { override val basePatchDownloadUrl: Url get() = Urls.newFromEncoded("https://download.jetbrains.com/idea/") - + override val productPageUrl: Url - get() = Urls.newFromEncoded("https://www.jetbrains.com/idea/") - + get() = baseWebSiteUrl.resolve("idea/") + override val youtrackProjectId: String get() = "IDEA" @@ -31,12 +31,12 @@ class IntelliJIdeaExternalResourceUrls : BaseJetBrainsExternalProductResourceUrl override val keyboardShortcutsPdfUrl: Url get() { val suffix = if (SystemInfoRt.isMac) "_Mac" else "" - return Urls.newFromEncoded("https://www.jetbrains.com/idea/docs/IntelliJIDEA_ReferenceCard$suffix.pdf") + return baseWebSiteUrl.resolve("idea/docs/IntelliJIDEA_ReferenceCard$suffix.pdf") } override val gettingStartedPageUrl: Url - get() = Urls.newFromEncoded("https://www.jetbrains.com/idea/resources/") - + get() = baseWebSiteUrl.resolve("idea/resources/") + override val baseWebHelpUrl: Url - get() = Urls.newFromEncoded("https://www.jetbrains.com/help/idea/") + get() = baseWebSiteUrl.resolve("help/idea/") } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/platform/ide/impl/customization/BaseJetBrainsExternalProductResourceUrls.kt b/platform/platform-impl/src/com/intellij/platform/ide/impl/customization/BaseJetBrainsExternalProductResourceUrls.kt index 841aaaf960d5..618c8bbf7cfe 100644 --- a/platform/platform-impl/src/com/intellij/platform/ide/impl/customization/BaseJetBrainsExternalProductResourceUrls.kt +++ b/platform/platform-impl/src/com/intellij/platform/ide/impl/customization/BaseJetBrainsExternalProductResourceUrls.kt @@ -1,6 +1,9 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.platform.ide.impl.customization +import com.intellij.ide.Region +import com.intellij.ide.RegionSettings +import com.intellij.l10n.LocalizationStateService import com.intellij.openapi.application.ApplicationInfo import com.intellij.openapi.updateSettings.impl.PatchInfo import com.intellij.openapi.updateSettings.impl.UpdateRequestParametersProvider @@ -21,30 +24,73 @@ abstract class BaseJetBrainsExternalProductResourceUrls : ExternalProductResourc abstract val basePatchDownloadUrl: Url /** - * Returns ID of YouTrack project which will be used by the "Submit a Bug Report" action. + * Returns ID of a YouTrack project which will be used by the "Submit a Bug Report" action. */ abstract val youtrackProjectId: String /** - * Returns the product name in the form shown at "intellij-support.jetbrains.com" site and "jetbrains.com/feedback/feedback.jsp" page. + * Returns the product name in the form shown at the "intellij-support.jetbrains.com" site and "jetbrains.com/feedback/feedback.jsp" page. */ abstract val shortProductNameUsedInForms: String? /** * Returns URL of the product page on the "jetbrains.com" site. - * It's used to compute URLs of the following resources: + * It's used to compute URLs of the following resources: * * [productPageUrl]/download to get the address of the download page; - * * [productPageUrl]/whatsnew to get the address of "What's New" page. + * * [productPageUrl]/whatsnew to get the address of "What's New" page. */ abstract val productPageUrl: Url /** - * Returns base URL of context help pages. + * Returns base URL of context help pages. * The current IDE version number and ID of the requested topic are added to it to get the actual URL: * [baseWebHelpUrl]`//?`. */ abstract val baseWebHelpUrl: Url? + /** + * Returns base URL for JetBrains website. Takes into account the user-selected region and locale. + * When localized pages are not present on the website, it takes care of redirecting to their English equivalents, + * so the IDE doesn't need to worry about that + */ + val baseWebSiteUrl: Url + get() { + + //China mainland has its own website, on which the default locale is zh; other regions use jetbrains.com with default en + val defaultLocales = mapOf(Region.CHINA to "zh") + + //All language URLs currently supported by the website + val langToUrl = mapOf( + "en" to "en-us", + "zh" to "zh-cn", + "ja" to "ja-jp", + "ko" to "ko-kr", + "es" to "es-es", + "pt" to "pt-br", + "fr" to "fr-fr", + "de" to "de-de" + ) + + val currentRegion = RegionSettings.getRegion() + val selectedLocale = LocalizationStateService.getInstance()?.selectedLocale + + return Urls.newFromEncoded( + buildString { + append("https://www.jetbrains.com") + if (currentRegion == Region.CHINA) + append(".cn") + if (selectedLocale != defaultLocales.getOrDefault(currentRegion, "en")) { + val locale = langToUrl[selectedLocale] + /** + * In case the locale is not supported by the website, direct to its default language. + * For jetbrains.com it will be English, and for jetbrains.com.cn — Chinese. + **/ + if (locale != null) + append("/$locale") + } + }) + } + /** * Returns ID of the form used to contact support at the "intellij-support.jetbrains.com" site. */ @@ -81,9 +127,9 @@ abstract class BaseJetBrainsExternalProductResourceUrls : ExternalProductResourc "c" to "Affected versions: ${ApplicationInfo.getInstance().fullVersion} (${ApplicationInfo.getInstance().getBuild().withoutProductCode()})" )) } - + override val technicalSupportUrl: ((description: String) -> Url)? - get() = { _ -> + get() = { _ -> Urls.newFromEncoded("https://intellij-support.jetbrains.com/hc/en-us/requests/new").addParameters( mapOf( "ticket_form_id" to intellijSupportFormId.toString(), @@ -111,7 +157,7 @@ abstract class BaseJetBrainsExternalProductResourceUrls : ExternalProductResourc baseUrl.resolve("${ApplicationInfo.getInstance().shortVersion}/").addParameters(mapOf( topicId to "" )) - } + } } } @@ -145,4 +191,4 @@ internal fun computePatchFileName(from: BuildNumber, to: BuildNumber): String { internal fun computeCustomPatchDownloadUrl(from: BuildNumber, to: BuildNumber): Url? { val customPatchesUrl = System.getProperty("idea.patches.url") ?: return null return Urls.newFromEncoded(customPatchesUrl).resolve(computePatchFileName(from, to)) -} +} \ No newline at end of file diff --git a/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt b/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt index c6b0a94e7ad3..23d7def4fe29 100644 --- a/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt +++ b/plugins/built-in-help/src/com/jetbrains/builtInHelp/BuiltInHelpManager.kt @@ -4,87 +4,71 @@ package com.jetbrains.builtInHelp import com.intellij.ide.BrowserUtil import com.intellij.ide.browsers.BrowserLauncher import com.intellij.ide.browsers.WebBrowserManager -import com.intellij.openapi.application.ApplicationInfo -import com.intellij.openapi.application.ApplicationNamesInfo +import com.intellij.openapi.application.IdeUrlTrackingParametersProvider import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.help.HelpManager import com.intellij.openapi.keymap.Keymap import com.intellij.openapi.keymap.ex.KeymapManagerEx -import com.intellij.openapi.util.text.StringUtil -import com.intellij.util.PlatformUtils +import com.intellij.platform.ide.customization.ExternalProductResourceUrls import com.jetbrains.builtInHelp.settings.SettingsPage import org.jetbrains.builtInWebServer.BuiltInServerOptions import java.io.IOException -import java.lang.String.* -import java.net.InetAddress -import java.net.URI -import java.net.URISyntaxException -import java.net.URLEncoder +import java.lang.String.valueOf +import java.net.* import java.nio.charset.StandardCharsets private val LOG = Logger.getInstance(BuiltInHelpManager::class.java) class BuiltInHelpManager : HelpManager() { + private fun isOnline(): Boolean { + return try { + val connection = URL("https://www.jetbrains.com/") + .openConnection() as HttpURLConnection + connection.connectTimeout = 1500 + connection.readTimeout = 1500 + connection.requestMethod = "GET" + connection.connect() + println("Response code: ${connection.responseCode}") + connection.responseCode < 400 + } + catch (e: IOException) { + e.printStackTrace() + false + } + } + override fun invokeHelp(helpId: String?) { val helpIdToUse = URLEncoder.encode(helpId, StandardCharsets.UTF_8) ?: "top" logWillOpenHelpId(helpIdToUse) + var activeKeymap: Keymap? = KeymapManagerEx.getInstanceEx().getActiveKeymap() + if (true == activeKeymap?.canModify()) + activeKeymap = activeKeymap.parent + try { - var activeKeymap: Keymap? = KeymapManagerEx.getInstanceEx().getActiveKeymap() - if (true == activeKeymap?.canModify()) - activeKeymap = activeKeymap.parent - - val activeKeymapParam = if (activeKeymap == null) "" else "&keymap=${URLEncoder.encode(activeKeymap.presentableName, StandardCharsets.UTF_8)}" - - var url = "http://127.0.0.1:${BuiltInServerOptions.getInstance().effectiveBuiltInServerPort}/help/?${ - helpIdToUse - }$activeKeymapParam" - val tryOpenWebSite = java.lang.Boolean.valueOf(Utils.getStoredValue( SettingsPage.OPEN_HELP_FROM_WEB, "true")) - var online = false - if (tryOpenWebSite) { - online = try { - InetAddress.getByName("www.jetbrains.com").isReachable(100) - } - catch (e: Exception) { - false - } + val url = if (tryOpenWebSite && isOnline()) { + val helpUrl = + ExternalProductResourceUrls.getInstance().helpPageUrl?.let { it(helpIdToUse) } + + if (activeKeymap != null) + helpUrl?.addParameters( + mapOf(Pair("keymap", activeKeymap.presentableName)) + ) + IdeUrlTrackingParametersProvider.getInstance().augmentUrl(helpUrl.toString()) + } + else { - if (online) { + val activeKeymapParam = if (activeKeymap == null) "" else "&keymap=${URLEncoder.encode(activeKeymap.presentableName, StandardCharsets.UTF_8)}" - val nameInfo = ApplicationNamesInfo.getInstance() - val editionName = nameInfo.editionName - - val productWebPath = when (val productName = StringUtil.toLowerCase(nameInfo.productName)) { - "rubymine", "ruby" -> "ruby" - "intellij idea", "idea" -> "idea" - "goland" -> "go" - "appcode" -> "objc" - "pycharm" -> if (editionName != null && StringUtil.toLowerCase(editionName) == "edu") "pycharm-edu" else "pycharm" - else -> productName - } - - val info = ApplicationInfo.getInstance() - val productVersion = info.shortVersion - - var baseUrl = Utils.getStoredValue(SettingsPage.OPEN_HELP_BASE_URL, - Utils.BASE_HELP_URL) - - if (!baseUrl.endsWith("/")) baseUrl += "/" - - url = "${baseUrl}help/$productWebPath/$productVersion/?${helpIdToUse}" - - if (PlatformUtils.isJetBrainsProduct() && baseUrl == Utils.BASE_HELP_URL) { - val productCode = info.build.productCode - if (!StringUtil.isEmpty(productCode)) { - url += "&utm_source=from_product&utm_medium=help_link&utm_campaign=$productCode&utm_content=$productVersion$activeKeymapParam" - } - } + "http://127.0.0.1:${BuiltInServerOptions.getInstance().effectiveBuiltInServerPort}/help/?${ + helpIdToUse + }$activeKeymapParam" } val browserName = valueOf( diff --git a/python/ide-common/src/customization/PyCharmExternalResourceUrls.kt b/python/ide-common/src/customization/PyCharmExternalResourceUrls.kt index 316451750f58..80fa19a35630 100644 --- a/python/ide-common/src/customization/PyCharmExternalResourceUrls.kt +++ b/python/ide-common/src/customization/PyCharmExternalResourceUrls.kt @@ -8,21 +8,21 @@ import com.intellij.util.Urls class PyCharmExternalResourceUrls : BaseJetBrainsExternalProductResourceUrls() { override val productPageUrl: Url - get() = Urls.newFromEncoded("https://www.jetbrains.com/pycharm/") + get() = baseWebSiteUrl.resolve("pycharm/") override val basePatchDownloadUrl: Url get() = Urls.newFromEncoded("https://download.jetbrains.com/python/") override val baseWebHelpUrl: Url - get() = Urls.newFromEncoded("https://www.jetbrains.com/pycharm/webhelp/") + get() = baseWebSiteUrl.resolve("help/pycharm/") override val gettingStartedPageUrl: Url - get() = Urls.newFromEncoded("https://www.jetbrains.com/pycharm/learn/") + get() = baseWebSiteUrl.resolve("pycharm/learn/") override val keyboardShortcutsPdfUrl: Url get() { val suffix = if (SystemInfo.isMac) "_Mac" else "" - return Urls.newFromEncoded("https://www.jetbrains.com/pycharm/docs/PyCharm_ReferenceCard$suffix.pdf") + return baseWebSiteUrl.resolve("pycharm/docs/PyCharm_ReferenceCard$suffix.pdf") } override val shortProductNameUsedInForms: String