Skip to content

Commit e47487c

Browse files
timilirisclaude
andcommitted
v1.3.7 - Multi-Language Support & MCP Multi-Assistant
### Added - Multi-Language Support: Plugin UI now supports 8 languages - English (default), Français, Deutsch, Español, Italiano, Português (Brasil), Polski, Русский - Language selector in Settings > Tools > HytaleDocs - Restart prompt when changing language - MCP Multi-Assistant Support: Install MCP server for multiple AI tools - Claude Code, Claude Desktop, Cursor, Windsurf, GitHub Copilot, Continue ### Changed - English is now the default language regardless of system locale - MCP configuration is now saved immediately to XML Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent fe0d214 commit e47487c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+4062
-818
lines changed

CHANGELOG.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,33 @@
22

33
## [Unreleased]
44

5+
## [1.3.7] - 2026-01-25
6+
7+
### Added
8+
9+
- **Multi-Language Support**: Plugin UI now supports 8 languages
10+
- English (default), Français, Deutsch, Español, Italiano, Português (Brasil), Polski, Русский
11+
- Language selector in Settings > Tools > HytaleDocs
12+
- Restart prompt when changing language
13+
- **MCP Multi-Assistant Support**: Install MCP server for multiple AI tools
14+
- Claude Code (project .mcp.json)
15+
- Claude Desktop
16+
- Cursor (~/.cursor/mcp.json)
17+
- Windsurf (~/.codeium/windsurf/mcp_config.json)
18+
- GitHub Copilot (.vscode/mcp.json)
19+
- Continue (~/.continue/config.json)
20+
21+
### Changed
22+
23+
- English is now the default language regardless of system locale
24+
- MCP configuration is now saved immediately to XML
25+
26+
## [1.3.6] - 2026-01-24
27+
28+
### Added
29+
30+
- **Hytale Installation Path Settings**: Configure custom Hytale installation path in Settings > Tools > HytaleDocs
31+
532
## [1.3.5] - 2026-01-22
633

734
### Fixed
@@ -118,7 +145,9 @@
118145
- Supports IntelliJ IDEA 2024.3+
119146
- Requires Java 25 for Hytale development
120147

121-
[Unreleased]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.5...HEAD
148+
[Unreleased]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.7...HEAD
149+
[1.3.7]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.6...v1.3.7
150+
[1.3.6]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.5...v1.3.6
122151
[1.3.5]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.4...v1.3.5
123152
[1.3.4]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.1...v1.3.4
124153
[1.3.1]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.0...v1.3.1

build.gradle.kts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ intellijPlatform {
9494

9595
ideaVersion {
9696
sinceBuild = providers.gradleProperty("pluginSinceBuild")
97+
untilBuild = providers.gradleProperty("pluginUntilBuild")
9798
}
9899
}
99100

@@ -143,6 +144,17 @@ tasks {
143144
publishPlugin {
144145
dependsOn(patchChangelog)
145146
}
147+
148+
// Force French locale for testing i18n (comment out for production)
149+
runIde {
150+
jvmArgumentProviders += CommandLineArgumentProvider {
151+
listOf(
152+
"-Duser.language=fr",
153+
"-Duser.country=FR",
154+
"-Dide.locale=fr_FR"
155+
)
156+
}
157+
}
146158
}
147159

148160
intellijPlatformTesting {

gradle.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ pluginGroup = com.hytaledocs.hytale
44
pluginName = Hytale Development Tools
55
pluginRepositoryUrl = https://github.com/HytaleDocs/hytale-intellij-plugin
66
# SemVer format -> https://semver.org
7-
pluginVersion = 1.3.6
7+
pluginVersion = 1.3.7
88

99
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
1010
pluginSinceBuild = 253
11+
pluginUntilBuild = 253.*
1112

1213
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
1314
platformVersion = 2025.3.1.1
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.hytaledocs.intellij
2+
3+
import com.hytaledocs.intellij.settings.HytaleAppSettings
4+
import org.jetbrains.annotations.NonNls
5+
import org.jetbrains.annotations.PropertyKey
6+
import java.util.*
7+
8+
@NonNls
9+
private const val BUNDLE = "messages.HytaleBundle"
10+
11+
/**
12+
* Custom bundle that uses the plugin's language setting instead of system locale.
13+
* Default language is English, regardless of system locale.
14+
*/
15+
object HytaleBundle {
16+
private var cachedBundle: ResourceBundle? = null
17+
private var cachedLanguage: String? = null
18+
19+
/**
20+
* Get a message from the bundle with the configured language.
21+
*/
22+
@JvmStatic
23+
fun message(@PropertyKey(resourceBundle = BUNDLE) key: String, vararg params: Any): String {
24+
val bundle = getBundle()
25+
return try {
26+
val pattern = bundle.getString(key)
27+
if (params.isEmpty()) {
28+
pattern
29+
} else {
30+
java.text.MessageFormat.format(pattern, *params)
31+
}
32+
} catch (e: MissingResourceException) {
33+
key // Return key if not found
34+
}
35+
}
36+
37+
/**
38+
* Get the ResourceBundle for the currently configured language.
39+
* Caches the bundle to avoid repeated loading.
40+
*/
41+
private fun getBundle(): ResourceBundle {
42+
val currentLanguage = try {
43+
HytaleAppSettings.getInstance().language
44+
} catch (e: Exception) {
45+
// Settings may not be available during early initialization
46+
"en"
47+
}
48+
49+
// Return cached bundle if language hasn't changed
50+
if (cachedBundle != null && cachedLanguage == currentLanguage) {
51+
return cachedBundle!!
52+
}
53+
54+
// Load the appropriate bundle
55+
// Use Locale.ROOT for English to load base HytaleBundle.properties
56+
// (Locale.ENGLISH would look for HytaleBundle_en.properties which doesn't exist)
57+
val locale = when (currentLanguage) {
58+
"fr" -> Locale.FRENCH
59+
"de" -> Locale.GERMAN
60+
"es" -> Locale("es")
61+
"it" -> Locale.ITALIAN
62+
"pt_BR" -> Locale("pt", "BR")
63+
"pl" -> Locale("pl")
64+
"ru" -> Locale("ru")
65+
else -> Locale.ROOT // English = base bundle
66+
}
67+
68+
cachedBundle = try {
69+
// Try to load the specific locale bundle with NO fallback to system locale
70+
ResourceBundle.getBundle(BUNDLE, locale, HytaleBundle::class.java.classLoader, NoFallbackControl())
71+
} catch (e: MissingResourceException) {
72+
// Fallback to base bundle (English)
73+
ResourceBundle.getBundle(BUNDLE, Locale.ROOT, HytaleBundle::class.java.classLoader, NoFallbackControl())
74+
}
75+
cachedLanguage = currentLanguage
76+
77+
return cachedBundle!!
78+
}
79+
80+
/**
81+
* Clear the cached bundle to force reload on next access.
82+
* Call this when the language setting changes.
83+
*/
84+
@JvmStatic
85+
fun clearCache() {
86+
cachedBundle = null
87+
cachedLanguage = null
88+
}
89+
90+
/**
91+
* Custom ResourceBundle.Control that:
92+
* 1. Loads properties files as UTF-8
93+
* 2. Does NOT fall back to system locale (only uses requested locale or base)
94+
*/
95+
private class NoFallbackControl : ResourceBundle.Control() {
96+
97+
override fun getCandidateLocales(baseName: String, locale: Locale): List<Locale> {
98+
// Only return the requested locale and ROOT (base bundle)
99+
// This prevents falling back to system locale
100+
return if (locale == Locale.ROOT) {
101+
listOf(Locale.ROOT)
102+
} else {
103+
listOf(locale, Locale.ROOT)
104+
}
105+
}
106+
107+
override fun getFallbackLocale(baseName: String, locale: Locale): Locale? {
108+
// No fallback to system locale
109+
return null
110+
}
111+
112+
override fun newBundle(
113+
baseName: String,
114+
locale: Locale,
115+
format: String,
116+
loader: ClassLoader,
117+
reload: Boolean
118+
): ResourceBundle? {
119+
val bundleName = toBundleName(baseName, locale)
120+
val resourceName = toResourceName(bundleName, "properties")
121+
122+
val stream = if (reload) {
123+
loader.getResource(resourceName)?.openConnection()?.apply {
124+
useCaches = false
125+
}?.getInputStream()
126+
} else {
127+
loader.getResourceAsStream(resourceName)
128+
}
129+
130+
return stream?.use { inputStream ->
131+
PropertyResourceBundle(inputStream.reader(Charsets.UTF_8))
132+
}
133+
}
134+
}
135+
}

src/main/kotlin/com/hytaledocs/intellij/actions/DownloadServerJarAction.kt

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
package com.hytaledocs.intellij.actions
22

3+
import com.hytaledocs.intellij.services.MavenMetadataService
34
import com.hytaledocs.intellij.services.ServerDownloadService
5+
import com.hytaledocs.intellij.services.ServerVersionCacheService
6+
import com.hytaledocs.intellij.settings.HytaleAppSettings
47
import com.intellij.notification.NotificationGroupManager
58
import com.intellij.notification.NotificationType
69
import com.intellij.openapi.actionSystem.AnAction
710
import com.intellij.openapi.actionSystem.AnActionEvent
811
import com.intellij.openapi.progress.ProgressIndicator
912
import com.intellij.openapi.progress.ProgressManager
1013
import com.intellij.openapi.progress.Task
14+
import com.intellij.openapi.ui.Messages
15+
import com.intellij.openapi.ui.popup.JBPopupFactory
16+
import com.intellij.openapi.ui.popup.PopupStep
17+
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
1118
import java.nio.file.Paths
1219
import java.util.concurrent.TimeUnit
1320

21+
/**
22+
* Action to download the Hytale server JAR from Maven repository.
23+
* Allows selecting a specific version or using the latest.
24+
*/
1425
class DownloadServerJarAction : AnAction() {
1526

1627
override fun actionPerformed(e: AnActionEvent) {
@@ -19,10 +30,79 @@ class DownloadServerJarAction : AnAction() {
1930

2031
val serverPath = Paths.get(basePath, "server")
2132
val downloadService = ServerDownloadService.getInstance(project)
33+
val mavenService = MavenMetadataService.getInstance()
34+
val settings = HytaleAppSettings.getInstance()
2235

36+
// Show version selection popup
2337
ProgressManager.getInstance().run(object : Task.Backgroundable(
2438
project,
25-
"Downloading Hytale Server JAR",
39+
"Fetching Available Versions",
40+
true
41+
) {
42+
override fun run(indicator: ProgressIndicator) {
43+
indicator.isIndeterminate = true
44+
indicator.text = "Fetching versions from Maven repository..."
45+
46+
try {
47+
val versions = mavenService.getAvailableVersions().get(30, TimeUnit.SECONDS)
48+
49+
if (versions.isEmpty()) {
50+
showError(project, "No versions available from Maven repository")
51+
return
52+
}
53+
54+
// Build popup items
55+
val items = mutableListOf<VersionItem>()
56+
items.add(VersionItem("Latest (${versions.first().version})", versions.first(), isLatest = true))
57+
58+
versions.forEach { version ->
59+
val cached = ServerVersionCacheService.getInstance().isVersionCached(version.version)
60+
val label = if (cached) {
61+
"${version.getDisplayName()} [cached]"
62+
} else {
63+
version.getDisplayName()
64+
}
65+
items.add(VersionItem(label, version, isLatest = false))
66+
}
67+
68+
// Show popup on EDT
69+
com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater {
70+
val popup = JBPopupFactory.getInstance().createListPopup(
71+
object : BaseListPopupStep<VersionItem>("Select Server Version", items) {
72+
override fun onChosen(selectedValue: VersionItem, finalChoice: Boolean): PopupStep<*>? {
73+
if (finalChoice) {
74+
downloadVersion(project, serverPath, downloadService, selectedValue.version)
75+
}
76+
return PopupStep.FINAL_CHOICE
77+
}
78+
79+
override fun getTextFor(value: VersionItem): String = value.label
80+
}
81+
)
82+
popup.showCenteredInCurrentWindow(project)
83+
}
84+
} catch (ex: Exception) {
85+
showError(project, "Failed to fetch versions: ${ex.message}")
86+
}
87+
}
88+
})
89+
}
90+
91+
private data class VersionItem(
92+
val label: String,
93+
val version: MavenMetadataService.ServerVersion,
94+
val isLatest: Boolean
95+
)
96+
97+
private fun downloadVersion(
98+
project: com.intellij.openapi.project.Project,
99+
serverPath: java.nio.file.Path,
100+
downloadService: ServerDownloadService,
101+
version: MavenMetadataService.ServerVersion
102+
) {
103+
ProgressManager.getInstance().run(object : Task.Backgroundable(
104+
project,
105+
"Downloading Hytale Server ${version.version}",
26106
true
27107
) {
28108
override fun run(indicator: ProgressIndicator) {
@@ -31,18 +111,21 @@ class DownloadServerJarAction : AnAction() {
31111
try {
32112
downloadService.createServerDirectories(serverPath)
33113

34-
downloadService.downloadServerJar(serverPath) { progress ->
114+
downloadService.downloadServerJarFromMaven(
115+
serverPath,
116+
version.version
117+
) { progress ->
35118
indicator.fraction = progress.progress / 100.0
36119
indicator.text = progress.message
37120
}.exceptionally { e ->
38121
throw RuntimeException("Download failed: ${e.message}", e)
39-
}.get(60, TimeUnit.SECONDS)
122+
}.get(300, TimeUnit.SECONDS)
40123

41124
NotificationGroupManager.getInstance()
42125
.getNotificationGroup("Hytale Plugin")
43126
.createNotification(
44-
"Hytale Server JAR Downloaded",
45-
"Successfully downloaded to $serverPath",
127+
"Hytale Server Downloaded",
128+
"Version ${version.version} downloaded to $serverPath",
46129
NotificationType.INFORMATION
47130
)
48131
.notify(project)
@@ -60,7 +143,22 @@ class DownloadServerJarAction : AnAction() {
60143
})
61144
}
62145

146+
private fun showError(project: com.intellij.openapi.project.Project, message: String) {
147+
com.intellij.openapi.application.ApplicationManager.getApplication().invokeLater {
148+
NotificationGroupManager.getInstance()
149+
.getNotificationGroup("Hytale Plugin")
150+
.createNotification(
151+
"Maven Error",
152+
message,
153+
NotificationType.ERROR
154+
)
155+
.notify(project)
156+
}
157+
}
158+
63159
override fun update(e: AnActionEvent) {
64160
e.presentation.isEnabledAndVisible = e.project != null
161+
e.presentation.text = "Download Server JAR (Maven)"
162+
e.presentation.description = "Download the Hytale server JAR from Maven repository with version selection"
65163
}
66164
}

0 commit comments

Comments
 (0)