diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.200.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.200.md
index b647952d09..a5b9fe2ed7 100644
--- a/docs/release-notes/.FSharp.Compiler.Service/10.0.200.md
+++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.200.md
@@ -1,6 +1,7 @@
### Fixed
* Type relations cache: optimize key generation ([Issue #19116](https://github.com/dotnet/fsharp/issues/18767)) ([PR #19120](https://github.com/dotnet/fsharp/pull/19120))
+* Fix `--preferreduilang` switch leaking into `fsi.CommandLineArgs` when positioned after script file ([PR #19151](https://github.com/dotnet/fsharp/pull/19151))
### Added
diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs
index 6d3350f0bf..e6b2775961 100644
--- a/src/Compiler/Interactive/fsi.fs
+++ b/src/Compiler/Interactive/fsi.fs
@@ -1068,8 +1068,41 @@ type internal FsiCommandLineOptions(fsi: FsiEvaluationSessionHostConfig, argv: s
(fun args ->
let scriptFile = args[0]
let scriptArgs = List.tail args
+
+ // Filter out and process preferreduilang from script args
+ let isPreferredUiLangArg (arg: string) =
+ arg.StartsWith("--preferreduilang:", StringComparison.OrdinalIgnoreCase)
+ || arg.StartsWith("/preferreduilang:", StringComparison.OrdinalIgnoreCase)
+
+ let rec filterScriptArgs (args: string list) =
+ match args with
+ | [] -> []
+ | (arg: string) :: rest when isPreferredUiLangArg arg ->
+ // Extract culture and set it
+ let colonIndex = arg.IndexOf(':')
+
+ if colonIndex >= 0 && colonIndex < arg.Length - 1 then
+ let culture = arg.Substring(colonIndex + 1)
+
+ try
+ // Validate culture first by creating CultureInfo
+ let cultureInfo = CultureInfo(culture)
+ // Only set if valid
+ tcConfigB.preferredUiLang <- Some culture
+ Thread.CurrentThread.CurrentUICulture <- cultureInfo
+ with
+ | :? CultureNotFoundException
+ | :? ArgumentException ->
+ // Ignore invalid culture, just don't set it
+ ()
+
+ filterScriptArgs rest
+ | arg :: rest -> arg :: filterScriptArgs rest
+
+ let filteredScriptArgs = filterScriptArgs scriptArgs
+
inputFilesAcc <- inputFilesAcc @ [ (scriptFile, true) ] (* record script.fsx for evaluation *)
- List.iter recordExplicitArg scriptArgs (* record rest of line as explicit arguments *)
+ List.iter recordExplicitArg filteredScriptArgs (* record rest of line as explicit arguments *)
tcConfigB.noFeedback <- true (* "quiet", no banners responses etc *)
interact <- false (* --exec, exit after eval *)
[] (* no arguments passed on, all consumed here *)
diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
index 0bc7df3c9f..c7c7127c07 100644
--- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
+++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj
@@ -278,6 +278,7 @@
+
diff --git a/tests/FSharp.Compiler.ComponentTests/Scripting/PreferredUiLangTests.fs b/tests/FSharp.Compiler.ComponentTests/Scripting/PreferredUiLangTests.fs
new file mode 100644
index 0000000000..63fbfb3df7
--- /dev/null
+++ b/tests/FSharp.Compiler.ComponentTests/Scripting/PreferredUiLangTests.fs
@@ -0,0 +1,147 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace Scripting
+
+open Xunit
+open System
+open System.IO
+open FSharp.Test
+open FSharp.Test.CompilerAssertHelpers
+open FSharp.Compiler.Interactive.Shell
+open FSharp.Compiler.Diagnostics
+
+module ``PreferredUiLang tests`` =
+
+ []
+ let ``preferreduilang switch before script is consumed from CommandLineArgs``() =
+ let scriptContent = """
+printfn "Args: %A" fsi.CommandLineArgs
+if fsi.CommandLineArgs.Length <> 2 then exit 1
+if fsi.CommandLineArgs.[0] <> __SOURCE_FILE__ then exit 1
+if fsi.CommandLineArgs.[1] <> "arg1" then exit 1
+exit 0
+"""
+ let tmpFile = Path.GetTempFileName() + ".fsx"
+ try
+ File.WriteAllText(tmpFile, scriptContent)
+ let errors, _, _ =
+ CompilerAssert.RunScriptWithOptionsAndReturnResult
+ [| "--preferreduilang:es-ES"; tmpFile; "arg1" |]
+ ""
+
+ // Should succeed (exit 0)
+ Assert.True((errors: ResizeArray).Count = 0, sprintf "Expected no errors, got: %A" errors)
+ finally
+ if File.Exists(tmpFile) then File.Delete(tmpFile)
+
+ []
+ let ``preferreduilang switch after script is consumed from CommandLineArgs``() =
+ let scriptContent = """
+printfn "Args: %A" fsi.CommandLineArgs
+if fsi.CommandLineArgs.Length <> 2 then exit 1
+if fsi.CommandLineArgs.[0] <> __SOURCE_FILE__ then exit 1
+if fsi.CommandLineArgs.[1] <> "arg1" then exit 1
+exit 0
+"""
+ let tmpFile = Path.GetTempFileName() + ".fsx"
+ try
+ File.WriteAllText(tmpFile, scriptContent)
+ let errors, _, _ =
+ CompilerAssert.RunScriptWithOptionsAndReturnResult
+ [| tmpFile; "--preferreduilang:es-ES"; "arg1" |]
+ ""
+
+ // Should succeed (exit 0)
+ Assert.True((errors: ResizeArray).Count = 0, sprintf "Expected no errors, got: %A" errors)
+ finally
+ if File.Exists(tmpFile) then File.Delete(tmpFile)
+
+ []
+ let ``preferreduilang with slash form is consumed from CommandLineArgs``() =
+ let scriptContent = """
+printfn "Args: %A" fsi.CommandLineArgs
+if fsi.CommandLineArgs.Length <> 2 then exit 1
+if fsi.CommandLineArgs.[0] <> __SOURCE_FILE__ then exit 1
+if fsi.CommandLineArgs.[1] <> "arg1" then exit 1
+exit 0
+"""
+ let tmpFile = Path.GetTempFileName() + ".fsx"
+ try
+ File.WriteAllText(tmpFile, scriptContent)
+ let errors, _, _ =
+ CompilerAssert.RunScriptWithOptionsAndReturnResult
+ [| tmpFile; "/preferreduilang:de-DE"; "arg1" |]
+ ""
+
+ // Should succeed (exit 0)
+ Assert.True((errors: ResizeArray).Count = 0, sprintf "Expected no errors, got: %A" errors)
+ finally
+ if File.Exists(tmpFile) then File.Delete(tmpFile)
+
+ []
+ let ``preferreduilang sets CurrentUICulture correctly``() =
+ let scriptContent = """
+let culture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name
+printfn "Culture: %s" culture
+if not (culture.StartsWith("fr-FR")) then
+ printfn "Expected culture starting with fr-FR, got: %s" culture
+ exit 1
+exit 0
+"""
+ let tmpFile = Path.GetTempFileName() + ".fsx"
+ try
+ File.WriteAllText(tmpFile, scriptContent)
+ let errors, _, _ =
+ CompilerAssert.RunScriptWithOptionsAndReturnResult
+ [| "--preferreduilang:fr-FR"; tmpFile |]
+ ""
+
+ // Should succeed (exit 0)
+ Assert.True((errors: ResizeArray).Count = 0, sprintf "Expected no errors, got: %A" errors)
+ finally
+ if File.Exists(tmpFile) then File.Delete(tmpFile)
+
+ []
+ let ``preferreduilang after script also sets culture``() =
+ let scriptContent = """
+let culture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name
+printfn "Culture: %s" culture
+if not (culture.StartsWith("ja-JP")) then
+ printfn "Expected culture starting with ja-JP, got: %s" culture
+ exit 1
+exit 0
+"""
+ let tmpFile = Path.GetTempFileName() + ".fsx"
+ try
+ File.WriteAllText(tmpFile, scriptContent)
+ let errors, _, _ =
+ CompilerAssert.RunScriptWithOptionsAndReturnResult
+ [| tmpFile; "--preferreduilang:ja-JP" |]
+ ""
+
+ // Should succeed (exit 0)
+ Assert.True((errors: ResizeArray).Count = 0, sprintf "Expected no errors, got: %A" errors)
+ finally
+ if File.Exists(tmpFile) then File.Delete(tmpFile)
+
+ []
+ let ``invalid culture in preferreduilang is ignored gracefully``() =
+ let scriptContent = """
+printfn "Args: %A" fsi.CommandLineArgs
+if fsi.CommandLineArgs.Length <> 2 then exit 1
+if fsi.CommandLineArgs.[0] <> __SOURCE_FILE__ then exit 1
+if fsi.CommandLineArgs.[1] <> "arg1" then exit 1
+exit 0
+"""
+ let tmpFile = Path.GetTempFileName() + ".fsx"
+ try
+ File.WriteAllText(tmpFile, scriptContent)
+ let errors, _, _ =
+ CompilerAssert.RunScriptWithOptionsAndReturnResult
+ [| tmpFile; "--preferreduilang:invalid-culture-xyz"; "arg1" |]
+ ""
+
+ // Should succeed - invalid culture is ignored, but switch is still consumed
+ Assert.True((errors: ResizeArray).Count = 0, sprintf "Expected no errors, got: %A" errors)
+ finally
+ if File.Exists(tmpFile) then File.Delete(tmpFile)