diff --git a/.idea/modules.xml b/.idea/modules.xml
index 7043736145a7..a48076ea4f3f 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -894,6 +894,7 @@
+
diff --git a/platform/syntax/syntax-scripts/api-dump-experimental.txt b/platform/syntax/syntax-scripts/api-dump-experimental.txt
new file mode 100644
index 000000000000..75e04b903bbf
--- /dev/null
+++ b/platform/syntax/syntax-scripts/api-dump-experimental.txt
@@ -0,0 +1,3 @@
+*f:com.intellij.platform.syntax.scripts.GenerateBundleMappingKt
+- sf:generateMapping(java.lang.String,java.lang.String,java.lang.String):V
+- sf:main(java.lang.String[]):V
diff --git a/platform/syntax/syntax-scripts/api-dump.txt b/platform/syntax/syntax-scripts/api-dump.txt
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/platform/syntax/syntax-scripts/intellij.platform.syntax.scripts.iml b/platform/syntax/syntax-scripts/intellij.platform.syntax.scripts.iml
new file mode 100644
index 000000000000..122f4d43124d
--- /dev/null
+++ b/platform/syntax/syntax-scripts/intellij.platform.syntax.scripts.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/syntax/syntax-scripts/src/com/intellij/platform/syntax/scripts/generateBundleMapping.kt b/platform/syntax/syntax-scripts/src/com/intellij/platform/syntax/scripts/generateBundleMapping.kt
new file mode 100644
index 000000000000..08b77a5db67a
--- /dev/null
+++ b/platform/syntax/syntax-scripts/src/com/intellij/platform/syntax/scripts/generateBundleMapping.kt
@@ -0,0 +1,88 @@
+// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+@file: ApiStatus.Experimental
+
+package com.intellij.platform.syntax.scripts
+
+import org.jetbrains.annotations.ApiStatus
+import java.io.OutputStreamWriter
+import java.nio.file.Path
+import java.util.*
+import kotlin.io.path.*
+import kotlin.system.exitProcess
+
+/**
+ * Generates a Kotlin class from your .properties file. This file is necessary for non-JVM targets where Java PropertyResourceBundles are not available.
+ */
+fun main(args: Array) {
+ if (args.size != 3) {
+ println("""
+ |Please pass 3 arguments:
+ | 1. the path to .properties file
+ | 2. the path to the output file as arguments
+ | 3. the package name to use
+ |""".trimMargin())
+ exitProcess(-1)
+ }
+
+ generateMapping(args[0], args[1], args[2])
+}
+
+fun generateMapping(
+ properties: String,
+ output: String,
+ packageName: String,
+) {
+ val propertiesPath = Path(properties)
+ if (!propertiesPath.exists()) {
+ println(".properties file does not exist! Path: $propertiesPath")
+ exitProcess(-1)
+ }
+
+ val outputPath = Path(output)
+ if (!outputPath.exists()) {
+ outputPath.createFile()
+ }
+
+ val mappings = loadMappings(propertiesPath)
+
+ val fileName = outputPath.nameWithoutExtension
+ outputPath.writer().use { writer ->
+ writer.printClass(fileName, packageName, mappings, "$properties $output $packageName")
+ }
+}
+
+private fun OutputStreamWriter.printClass(
+ className: String,
+ packageName: String,
+ mappings: Map,
+ parametersString: String,
+) {
+ appendLine("package $packageName")
+ appendLine()
+ appendLine("// Generated by generateBundleMapping.kt")
+ appendLine("// To regenerate the file, please run `GenerateBundleMapping` run configuration with the following parameters: ")
+ appendLine("// $parametersString ")
+ appendLine("internal object $className {")
+ appendLine(" val mappings: Map = mapOf(")
+ for ((key, value) in mappings.entries) {
+ val escapedValue = value
+ .replace("\\", "\\\\")
+ .replace("\n", "\\n")
+ .replace("\r", "\\r")
+ .replace("\t", "\\t")
+ appendLine(" \"$key\" to \"$escapedValue\",")
+ }
+ appendLine(" )")
+ appendLine('}')
+}
+
+private fun loadMappings(propertiesPath: Path): Map {
+ val properties = Properties()
+ propertiesPath.inputStream().use { stream ->
+ properties.load(stream)
+ }
+
+ return properties.entries.associate { (key, value) ->
+ (key as String) to (value as String)
+ }
+}