You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by ni...@apache.org on 2021/05/04 12:16:23 UTC

[lucenenet] 04/04: Lucene.Net.QueryParsers.Flexible.Core.Messages: Redesigned QueryParserMessages.cs so that it is just a facade around a IResourceProvider implementation that provides the actual fallback logic. Added a QueryParserResourceProvider implementation that can be passed zero to many ResourceProvider instances to override and optionally localize the default resource messages.

This is an automated email from the ASF dual-hosted git repository.

nightowl888 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucenenet.git

commit c15328f68ef46503d96f3f011f9ab2a3001acc24
Author: Shad Storhaug <sh...@shadstorhaug.com>
AuthorDate: Tue May 4 06:44:36 2021 +0700

    Lucene.Net.QueryParsers.Flexible.Core.Messages: Redesigned QueryParserMessages.cs so that it is just a facade around a IResourceProvider implementation that provides the actual fallback logic. Added a QueryParserResourceProvider implementation that can be passed zero to many ResourceProvider instances to override and optionally localize the default resource messages.
---
 .../Core/Messages/QueryParserMessages.Designer.cs  | 243 -------------------
 .../Flexible/Core/Messages/QueryParserMessages.cs  | 263 ++++++++++++++++-----
 .../Core/Messages/QueryParserResourceProvider.cs   | 191 +++++++++++++++
 .../Lucene.Net.QueryParser.csproj                  |  13 +-
 .../Lucene.Net.Tests.QueryParser.csproj            |  29 ++-
 .../Resources/LUCENE_NET_ICON_32x32.png            | Bin 0 -> 233 bytes
 .../Core/Messages/MessagesTest.Designer.cs         | 100 ++++++++
 .../Flexible/Core/Messages/MessagesTest.ja.resx    | 126 ++++++++++
 .../Flexible/Core/Messages/MessagesTest.resx       | 133 +++++++++++
 .../Messages/TestQueryParserMessagesDefault.cs     | 105 ++++++++
 .../Messages/TestQueryParserMessagesOverridden.cs  | 123 ++++++++++
 .../Messages/TestQueryParserResourceProvider.cs    | 113 +++++++++
 .../Core/Messages/lucene-net-icon-32x32.png        | Bin 0 -> 1136 bytes
 src/Lucene.Net/Support/Util/IResourceProvider.cs   |  94 ++++++++
 14 files changed, 1214 insertions(+), 319 deletions(-)

diff --git a/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserMessages.Designer.cs b/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserMessages.Designer.cs
deleted file mode 100644
index 1275ba0..0000000
--- a/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserMessages.Designer.cs
+++ /dev/null
@@ -1,243 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-//     This code was generated by a tool.
-//     Runtime Version:4.0.30319.42000
-//
-//     Changes to this file may cause incorrect behavior and will be lost if
-//     the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Lucene.Net.QueryParsers.Flexible.Core.Messages {
-    using System;
-    
-    
-    /// <summary>
-    ///   A strongly-typed resource class, for looking up localized strings, etc.
-    /// </summary>
-    // This class was auto-generated by the StronglyTypedResourceBuilder
-    // class via a tool like ResGen or Visual Studio.
-    // To add or remove a member, edit your .ResX file then rerun ResGen
-    // with the /str option, or rebuild your VS project.
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
-    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    internal class QueryParserMessages {
-        
-        private static global::System.Resources.ResourceManager resourceMan;
-        
-        private static global::System.Globalization.CultureInfo resourceCulture;
-        
-        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
-        internal QueryParserMessages() {
-        }
-        
-        /// <summary>
-        ///   Returns the cached ResourceManager instance used by this class.
-        /// </summary>
-        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
-        internal static global::System.Resources.ResourceManager ResourceManager {
-            get {
-                if (object.ReferenceEquals(resourceMan, null)) {
-                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Lucene.Net.QueryParsers.Flexible.Core.Messages.QueryParserMessages", typeof(QueryParserMessages).Assembly);
-                    resourceMan = temp;
-                }
-                return resourceMan;
-            }
-        }
-        
-        /// <summary>
-        ///   Overrides the current thread's CurrentUICulture property for all
-        ///   resource lookups using this strongly typed resource class.
-        /// </summary>
-        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
-        internal static global::System.Globalization.CultureInfo Culture {
-            get {
-                return resourceCulture;
-            }
-            set {
-                resourceCulture = value;
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Value cannot be null..
-        /// </summary>
-        internal static string ARGUMENT_CANNOT_BE_NULL {
-            get {
-                return ResourceManager.GetString("ARGUMENT_CANNOT_BE_NULL", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Could not parse text &quot;{0}&quot; using {1}.
-        /// </summary>
-        internal static string COULD_NOT_PARSE_NUMBER {
-            get {
-                return ResourceManager.GetString("COULD_NOT_PARSE_NUMBER", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to .
-        /// </summary>
-        internal static string EMPTY_MESSAGE {
-            get {
-                return ResourceManager.GetString("EMPTY_MESSAGE", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Syntax Error: {0}.
-        /// </summary>
-        internal static string INVALID_SYNTAX {
-            get {
-                return ResourceManager.GetString("INVALID_SYNTAX", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Syntax Error, cannot parse {0}: {1}.
-        /// </summary>
-        internal static string INVALID_SYNTAX_CANNOT_PARSE {
-            get {
-                return ResourceManager.GetString("INVALID_SYNTAX_CANNOT_PARSE", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Term can not end with escape character..
-        /// </summary>
-        internal static string INVALID_SYNTAX_ESCAPE_CHARACTER {
-            get {
-                return ResourceManager.GetString("INVALID_SYNTAX_ESCAPE_CHARACTER", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Non-hex character in Unicode escape sequence: {0}.
-        /// </summary>
-        internal static string INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE {
-            get {
-                return ResourceManager.GetString("INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Truncated unicode escape sequence..
-        /// </summary>
-        internal static string INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION {
-            get {
-                return ResourceManager.GetString("INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Fractional edit distances are not allowed..
-        /// </summary>
-        internal static string INVALID_SYNTAX_FUZZY_EDITS {
-            get {
-                return ResourceManager.GetString("INVALID_SYNTAX_FUZZY_EDITS", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to The similarity value for a fuzzy search must be between 0.0 and 1.0..
-        /// </summary>
-        internal static string INVALID_SYNTAX_FUZZY_LIMITS {
-            get {
-                return ResourceManager.GetString("INVALID_SYNTAX_FUZZY_LIMITS", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Leading wildcard is not allowed: {0}.
-        /// </summary>
-        internal static string LEADING_WILDCARD_NOT_ALLOWED {
-            get {
-                return ResourceManager.GetString("LEADING_WILDCARD_NOT_ALLOWED", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Cannot convert query to lucene syntax: {0} error: {1}.
-        /// </summary>
-        internal static string LUCENE_QUERY_CONVERSION_ERROR {
-            get {
-                return ResourceManager.GetString("LUCENE_QUERY_CONVERSION_ERROR", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to This node does not support this action..
-        /// </summary>
-        internal static string NODE_ACTION_NOT_SUPPORTED {
-            get {
-                return ResourceManager.GetString("NODE_ACTION_NOT_SUPPORTED", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to The parameter must be greater than or equal to zero..
-        /// </summary>
-        internal static string NUMBER_CANNOT_BE_NEGATIVE {
-            get {
-                return ResourceManager.GetString("NUMBER_CANNOT_BE_NEGATIVE", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Number class not supported by NumericRangeQueryNode: {0}.
-        /// </summary>
-        internal static string NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY {
-            get {
-                return ResourceManager.GetString("NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Field &quot;{0}&quot; is numeric and cannot have an empty value..
-        /// </summary>
-        internal static string NUMERIC_CANNOT_BE_EMPTY {
-            get {
-                return ResourceManager.GetString("NUMERIC_CANNOT_BE_EMPTY", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Parameter {1} with value {0} not supported..
-        /// </summary>
-        internal static string PARAMETER_VALUE_NOT_SUPPORTED {
-            get {
-                return ResourceManager.GetString("PARAMETER_VALUE_NOT_SUPPORTED", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Too many boolean clauses, the maximum supported is {0}: {1}.
-        /// </summary>
-        internal static string TOO_MANY_BOOLEAN_CLAUSES {
-            get {
-                return ResourceManager.GetString("TOO_MANY_BOOLEAN_CLAUSES", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Unsupported NumericField.DataType: {0}.
-        /// </summary>
-        internal static string UNSUPPORTED_NUMERIC_DATA_TYPE {
-            get {
-                return ResourceManager.GetString("UNSUPPORTED_NUMERIC_DATA_TYPE", resourceCulture);
-            }
-        }
-        
-        /// <summary>
-        ///   Looks up a localized string similar to Wildcard is not supported for query: {0}.
-        /// </summary>
-        internal static string WILDCARD_NOT_SUPPORTED {
-            get {
-                return ResourceManager.GetString("WILDCARD_NOT_SUPPORTED", resourceCulture);
-            }
-        }
-    }
-}
diff --git a/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserMessages.cs b/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserMessages.cs
index 103b981..02f38f7 100644
--- a/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserMessages.cs
+++ b/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserMessages.cs
@@ -1,64 +1,199 @@
-// LUCENENET specific - factored out this class to optionally use the built in .NET localization rather than
-// forcing the use of the oddly designed Message class with NLS.
-
-//using Lucene.Net.QueryParsers.Flexible.Messages;
-
-//namespace Lucene.Net.QueryParsers.Flexible.Core.Messages
-//{
-//    /*
-//     * Licensed to the Apache Software Foundation (ASF) under one or more
-//     * contributor license agreements.  See the NOTICE file distributed with
-//     * this work for additional information regarding copyright ownership.
-//     * The ASF licenses this file to You under the Apache License, Version 2.0
-//     * (the "License"); you may not use this file except in compliance with
-//     * the License.  You may obtain a copy of the License at
-//     *
-//     *     http://www.apache.org/licenses/LICENSE-2.0
-//     *
-//     * Unless required by applicable law or agreed to in writing, software
-//     * distributed under the License is distributed on an "AS IS" BASIS,
-//     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-//     * See the License for the specific language governing permissions and
-//     * limitations under the License.
-//     */
-
-//    /// <summary>
-//    /// Flexible Query Parser message bundle class
-//    /// </summary>
-//    public class QueryParserMessages : NLS
-//    {
-//        private static readonly string BUNDLE_NAME = typeof(QueryParserMessages).Name;
-
-//        private QueryParserMessages()
-//        {
-//            // Do not instantiate
-//        }
-
-//        static QueryParserMessages()
-//        {
-//            // register all string ids with NLS class and initialize static string
-//            // values
-//            NLS.InitializeMessages(BUNDLE_NAME, typeof(QueryParserMessages));
-//        }
-
-//        // static string must match the strings in the property files.
-//        public static string INVALID_SYNTAX;
-//        public static string INVALID_SYNTAX_CANNOT_PARSE;
-//        public static string INVALID_SYNTAX_FUZZY_LIMITS;
-//        public static string INVALID_SYNTAX_FUZZY_EDITS;
-//        public static string INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION;
-//        public static string INVALID_SYNTAX_ESCAPE_CHARACTER;
-//        public static string INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE;
-//        public static string NODE_ACTION_NOT_SUPPORTED;
-//        public static string PARAMETER_VALUE_NOT_SUPPORTED;
-//        public static string LUCENE_QUERY_CONVERSION_ERROR;
-//        public static string EMPTY_MESSAGE;
-//        public static string WILDCARD_NOT_SUPPORTED;
-//        public static string TOO_MANY_BOOLEAN_CLAUSES;
-//        public static string LEADING_WILDCARD_NOT_ALLOWED;
-//        public static string COULD_NOT_PARSE_NUMBER;
-//        public static string NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY;
-//        public static string UNSUPPORTED_NUMERIC_DATA_TYPE;
-//        public static string NUMERIC_CANNOT_BE_EMPTY;
-//    }
-//}
+// LUCENENET specific - This is the content of QueryParserMessages.Designer that was generated, with customizations.
+// The NLS derived class was replaced with this one to make interop with .NET resources easier.
+//
+// We turned off auto-generation of this file and added a SetResourceProvider() to supply a class that can supply string/object localized resources.
+// This allows end users to supply localized messages.
+//
+// 1. Add a Resource (.resx) to the project.
+// 2. Add any of the below properties as the resource names, and supply messages as desired.
+// 3. Call QueryParserMessages.SetResourceProvider(new QueryParserResourceProvider(SomeResource.ResourceManager)) at application startup and supply the ResourceManager of the custom Resource's designer.
+
+
+using Lucene.Net.Util;
+using System;
+using System.ComponentModel;
+using System.Globalization;
+#nullable enable
+
+namespace Lucene.Net.QueryParsers.Flexible.Core.Messages
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// A strongly-typed resource class, for looking up localized strings, etc.
+    /// <para/>
+    /// The localized resources can be replaced by end users by calling the <see cref="SetResourceProvider(IResourceProvider)"/>
+    /// method and supplying an instance of <see cref="QueryParserResourceProvider"/> with custom
+    /// <see cref="System.Resources.ResourceManager"/> instances provided in its constructor.
+    /// <code>
+    /// QueryParserMessages.SetResourceProvider(new QueryParserResourceProvider(SomeResource.ResourceManager, SomeOtherResource.ResourceManager));
+    /// </code>
+    /// <c>SomeResource</c> and <c>SomeOtherResource</c> are auto-generated designer classes which can be generated by Visual Studio when adding
+    /// resource files (.resx) to a project. These resources can optionally be localized in different cultures and optionally be distributed as satellite
+    /// assemblies. See the <see cref="System.Resources.ResourceManager"/> documentation for tips on how to get started, but do note this is a broad topic.
+    /// <para/>
+    /// <see cref="QueryParserResourceProvider"/> makes no assumptions on the method used to generate or deploy resources, the only requirement is that you provide
+    /// a <see cref="System.Resources.ResourceManager"/> that can find them. Note that it is also possible to subclass <see cref="QueryParserResourceProvider"/>
+    /// or implement <see cref="IResourceProvider"/> directly to provide resources from any source.
+    /// </summary>
+    // LUCENENET NOTE: Since we are not using a custom tool to generate this file, any additions/deletions to the
+    // QueryParserMessages.resx file must be done to this class manually.
+    public static class QueryParserMessages
+    {
+        private static CultureInfo? resourceCulture;
+        private static IResourceProvider resourceProvider = new QueryParserResourceProvider();
+
+        /// <summary>
+        /// Gets the associated resource provider.
+        /// </summary>
+        /// <returns>The current resource provider.</returns>
+        /// <seealso cref="SetResourceProvider(IResourceProvider)"/>
+        public static IResourceProvider GetResourceProvider()
+        {
+            return resourceProvider;
+        }
+
+        /// <summary>
+        /// Sets the <see cref="IResourceProvider"/> instance used to provide
+        /// localized <see cref="string"/>s and <see cref="object"/>s.
+        /// </summary>
+        /// <param name="provider">The new <see cref="IResourceProvider"/>.</param>
+        /// <exception cref="ArgumentNullException">The <paramref name="provider"/> parameter is <c>null</c>.</exception>
+        /// <seealso cref="GetResourceProvider()"/>
+        public static void SetResourceProvider(IResourceProvider provider)
+        {
+            resourceProvider = provider ?? throw new ArgumentNullException(nameof(provider));
+        }
+
+        private static string? GetString(string name, CultureInfo? culture)
+        {
+            return resourceProvider.GetString(name, culture);
+        }
+
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
+        public static CultureInfo? Culture
+        {
+            get => resourceCulture;
+            set => resourceCulture = value;
+        }
+
+        /// <summary>
+        ///   Looks up a localized string similar to Value cannot be null..
+        /// </summary>
+        public static string? ARGUMENT_CANNOT_BE_NULL => GetString("ARGUMENT_CANNOT_BE_NULL", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Could not parse text &quot;{0}&quot; using {1}.
+        /// </summary>
+        public static string? COULD_NOT_PARSE_NUMBER => GetString("COULD_NOT_PARSE_NUMBER", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to .
+        /// </summary>
+        public static string? EMPTY_MESSAGE => GetString("EMPTY_MESSAGE", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Syntax Error: {0}.
+        /// </summary>
+        public static string? INVALID_SYNTAX => GetString("INVALID_SYNTAX", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Syntax Error, cannot parse {0}: {1}.
+        /// </summary>
+        public static string? INVALID_SYNTAX_CANNOT_PARSE => GetString("INVALID_SYNTAX_CANNOT_PARSE", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Term can not end with escape character..
+        /// </summary>
+        public static string? INVALID_SYNTAX_ESCAPE_CHARACTER => GetString("INVALID_SYNTAX_ESCAPE_CHARACTER", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Non-hex character in Unicode escape sequence: {0}.
+        /// </summary>
+        public static string? INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE => GetString("INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Truncated unicode escape sequence..
+        /// </summary>
+        public static string? INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION => GetString("INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Fractional edit distances are not allowed..
+        /// </summary>
+        public static string? INVALID_SYNTAX_FUZZY_EDITS => GetString("INVALID_SYNTAX_FUZZY_EDITS", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to The similarity value for a fuzzy search must be between 0.0 and 1.0..
+        /// </summary>
+        public static string? INVALID_SYNTAX_FUZZY_LIMITS => GetString("INVALID_SYNTAX_FUZZY_LIMITS", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Leading wildcard is not allowed: {0}.
+        /// </summary>
+        public static string? LEADING_WILDCARD_NOT_ALLOWED => GetString("LEADING_WILDCARD_NOT_ALLOWED", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Cannot convert query to lucene syntax: {0} error: {1}.
+        /// </summary>
+        public static string? LUCENE_QUERY_CONVERSION_ERROR => GetString("LUCENE_QUERY_CONVERSION_ERROR", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to This node does not support this action..
+        /// </summary>
+        public static string? NODE_ACTION_NOT_SUPPORTED => GetString("NODE_ACTION_NOT_SUPPORTED", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to The parameter must be greater than or equal to zero..
+        /// </summary>
+        public static string? NUMBER_CANNOT_BE_NEGATIVE => GetString("NUMBER_CANNOT_BE_NEGATIVE", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Number class not supported by NumericRangeQueryNode: {0}.
+        /// </summary>
+        public static string? NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY => GetString("NUMBER_CLASS_NOT_SUPPORTED_BY_NUMERIC_RANGE_QUERY", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Field &quot;{0}&quot; is numeric and cannot have an empty value..
+        /// </summary>
+        public static string? NUMERIC_CANNOT_BE_EMPTY => GetString("NUMERIC_CANNOT_BE_EMPTY", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Parameter {1} with value {0} not supported..
+        /// </summary>
+        public static string? PARAMETER_VALUE_NOT_SUPPORTED => GetString("PARAMETER_VALUE_NOT_SUPPORTED", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Too many boolean clauses, the maximum supported is {0}: {1}.
+        /// </summary>
+        public static string? TOO_MANY_BOOLEAN_CLAUSES => GetString("TOO_MANY_BOOLEAN_CLAUSES", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Unsupported NumericField.DataType: {0}.
+        /// </summary>
+        public static string? UNSUPPORTED_NUMERIC_DATA_TYPE => GetString("UNSUPPORTED_NUMERIC_DATA_TYPE", resourceCulture);
+
+        /// <summary>
+        ///   Looks up a localized string similar to Wildcard is not supported for query: {0}.
+        /// </summary>
+        public static string? WILDCARD_NOT_SUPPORTED => GetString("WILDCARD_NOT_SUPPORTED", resourceCulture);
+    }
+}
diff --git a/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserResourceProvider.cs b/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserResourceProvider.cs
new file mode 100644
index 0000000..0a9633f
--- /dev/null
+++ b/src/Lucene.Net.QueryParser/Flexible/Core/Messages/QueryParserResourceProvider.cs
@@ -0,0 +1,191 @@
+using Lucene.Net.Support;
+using Lucene.Net.Util;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.IO;
+using System.Resources;
+using System.Threading;
+#nullable enable
+
+namespace Lucene.Net.QueryParsers.Flexible.Core.Messages
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// The default <see cref="IResourceProvider"/> implementation for the <see cref="QueryParserMessages"/> class.
+    /// This class can be set in the <see cref="QueryParserMessages.SetResourceProvider(IResourceProvider)"/> method
+    /// and supplied with one or more <see cref="ResourceManager"/> instances that can override the default query parser
+    /// messages (generally, they are exception messages).
+    /// <para/>
+    /// Alternatively, this class may be overridden to provide either a custom <see cref="FallbackResourceManager"/> or
+    /// to alter the fallback logic in either <see cref="GetString(string, CultureInfo?)"/> or <see cref="GetObject(string, CultureInfo?)"/>.
+    /// The performance of this class may be improved significantly by specifying the <see cref="ResourceManager"/> that a specific resource
+    /// can be found in rather than attempting all of them.
+    /// </summary>
+    public class QueryParserResourceProvider : IResourceProvider
+    {
+        private ResourceManager? fallbackResourceManager;
+        private readonly IList<ResourceManager> resourceManagers;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="QueryParserResourceProvider"/> class with default values.
+        /// </summary>
+        public QueryParserResourceProvider()
+            : this((IList<ResourceManager>)Arrays.Empty<ResourceManager>())
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="QueryParserResourceProvider"/> class with the specified
+        /// <paramref name="resourceManagers"/>. The <paramref name="resourceManagers"/> may override resources
+        /// in the <see cref="FallbackResourceManager"/>, provided they have the same names.
+        /// <para/>
+        /// Note that not all of the resources are required to be provided and if the name doesn't exist it will
+        /// fall back to the next <see cref="ResourceManager"/> that is provided and ultimately will try the
+        /// <see cref="FallbackResourceManager"/> if the resource is not found.
+        /// </summary>
+        /// <param name="resourceManagers">One or more <see cref="ResourceManager"/> instances that provide
+        /// localized resources. The <paramref name="resourceManagers"/> are used in the order they are specified, and the first one
+        /// that provides a non-<c>null</c> value for a given resource name wins.</param>
+        /// <exception cref="ArgumentNullException">If <paramref name="resourceManagers"/> is <c>null</c>.</exception>
+        public QueryParserResourceProvider(params ResourceManager[] resourceManagers)
+            : this((IList<ResourceManager>)resourceManagers)
+        {
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="QueryParserResourceProvider"/> class with the specified
+        /// <paramref name="resourceManagers"/>. The <paramref name="resourceManagers"/> may override resources
+        /// in the <see cref="FallbackResourceManager"/>, provided they have the same names.
+        /// <para/>
+        /// Note that not all of the resources are required to be provided and if the name doesn't exist it will
+        /// fall back to the next <see cref="ResourceManager"/> that is provided and ultimately will try the
+        /// <see cref="FallbackResourceManager"/> if the resource is not found.
+        /// </summary>
+        /// <param name="resourceManagers">One or more <see cref="ResourceManager"/> instances that provide
+        /// localized resources. The <paramref name="resourceManagers"/> are used in the order they are specified, and the first one
+        /// that provides a non-<c>null</c> value for a given resource name wins.</param>
+        /// <exception cref="ArgumentNullException">If <paramref name="resourceManagers"/> is <c>null</c>.</exception>
+        public QueryParserResourceProvider(IList<ResourceManager> resourceManagers)
+        {
+            this.resourceManagers = resourceManagers ?? throw new ArgumentNullException(nameof(resourceManagers));
+        }
+
+        /// <summary>
+        /// Gets the cached <see cref="ResourceManager"/> instance used as the fallback by this class.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Advanced)]
+        public virtual ResourceManager FallbackResourceManager
+        {
+            get
+            {
+                return LazyInitializer.EnsureInitialized(ref fallbackResourceManager,
+                    () => new ResourceManager("Lucene.Net.QueryParsers.Flexible.Core.Messages.QueryParserMessages", typeof(QueryParserMessages).Assembly))!;
+            }
+        }
+
+        /// <summary>Returns the value of the string resource localized for the specified <paramref name="culture"/>.
+        /// <para/>
+        /// The resource is searched for
+        /// first in the <see cref="ResourceManager"/> instances passed in the <see cref="QueryParserResourceProvider(IList{ResourceManager})"/>
+        /// or <see cref="QueryParserResourceProvider(ResourceManager[])"/> constructor, in the order they are specified. If not found, the
+        /// <see cref="FallbackResourceManager"/> is used. This method may return <c>null</c> if the resource with the given name is not found.</summary>
+        /// <inheritdoc/>
+        public virtual string? GetString(string name, CultureInfo? culture)
+        {
+            if (resourceManagers.Count > 0)
+            {
+                foreach (var resourceManager in resourceManagers)
+                {
+                    // LUCENENET NOTE: MissingManifestResourceException or MissingSatelliteAssemblyException
+                    // can be thrown here, but these indicate that there is a misconfigured setup (missing resource file,
+                    // missing assembly, etc.) We intentionally let these errors propagate to ensure the developer who
+                    // is creating resources is aware of these problems before the app is deployed (provided they test it).
+                    // However, if the resource simply doesn't have an entry, we get a null return value instead and handle
+                    // the fallback accordingly.
+                    var result = resourceManager.GetString(name, culture);
+                    if (result is null)
+                        continue;
+                    return result;
+                }
+            }
+
+            return FallbackResourceManager.GetString(name, culture);
+        }
+
+        /// <summary>Gets the value of the specified non-string resource localized for the specified <paramref name="culture"/>.
+        /// <para/>
+        /// The resource is searched for
+        /// first in the <see cref="ResourceManager"/> instances passed in the <see cref="QueryParserResourceProvider(IList{ResourceManager})"/>
+        /// or <see cref="QueryParserResourceProvider(ResourceManager[])"/> constructor, in the order they are specified. If not found, the
+        /// <see cref="FallbackResourceManager"/> is used. This method may return <c>null</c> if the resource with the given name is not found.</summary>
+        /// <inheritdoc/>
+        public virtual object? GetObject(string name, CultureInfo? culture)
+        {
+            if (resourceManagers.Count > 0)
+            {
+                foreach (var resourceManager in resourceManagers)
+                {
+                    // LUCENENET NOTE: MissingManifestResourceException or MissingSatelliteAssemblyException
+                    // can be thrown here, but these indicate that there is a misconfigured setup (missing resource file,
+                    // missing assembly, etc.) We intentionally let these errors propagate to ensure the developer who
+                    // is creating resources is aware of these problems before the app is deployed (provided they test it).
+                    // However, if the resource simply doesn't have an entry, we get a null return value instead and handle
+                    // the fallback accordingly.
+                    var result = resourceManager.GetObject(name, culture);
+                    if (result is null)
+                        continue;
+                    return result;
+                }
+            }
+
+            return FallbackResourceManager.GetObject(name, culture);
+        }
+
+        /// <summary>Returns an unmanaged memory stream object from the specified resource, using the specified <paramref name="culture"/>.
+        /// <para/>
+        /// The resource is searched for
+        /// first in the <see cref="ResourceManager"/> instances passed in the <see cref="QueryParserResourceProvider(IList{ResourceManager})"/>
+        /// or <see cref="QueryParserResourceProvider(ResourceManager[])"/> constructor, in the order they are specified. If not found, the
+        /// <see cref="FallbackResourceManager"/> is used. This method may return <c>null</c> if the resource with the given name is not found.</summary>
+        /// <inheritdoc/>
+        public Stream? GetStream(string name, CultureInfo? culture)
+        {
+            if (resourceManagers.Count > 0)
+            {
+                foreach (var resourceManager in resourceManagers)
+                {
+                    // LUCENENET NOTE: MissingManifestResourceException or MissingSatelliteAssemblyException
+                    // can be thrown here, but these indicate that there is a misconfigured setup (missing resource file,
+                    // missing assembly, etc.) We intentionally let these errors propagate to ensure the developer who
+                    // is creating resources is aware of these problems before the app is deployed (provided they test it).
+                    // However, if the resource simply doesn't have an entry, we get a null return value instead and handle
+                    // the fallback accordingly.
+                    var result = resourceManager.GetStream(name, culture);
+                    if (result is null)
+                        continue;
+                    return result;
+                }
+            }
+
+            return FallbackResourceManager.GetStream(name, culture);
+        }
+    }
+}
diff --git a/src/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj b/src/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
index 20f51fe..4d287a7 100644
--- a/src/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
+++ b/src/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
@@ -53,17 +53,8 @@
   </ItemGroup>
 
   <ItemGroup>
-    <Compile Update="Flexible\Core\Messages\QueryParserMessages.Designer.cs">
-      <DesignTime>True</DesignTime>
-      <AutoGen>True</AutoGen>
-      <DependentUpon>QueryParserMessages.resx</DependentUpon>
-    </Compile>
-  </ItemGroup>
-
-  <ItemGroup>
-    <EmbeddedResource Update="Flexible\Core\Messages\QueryParserMessages.resx">
-      <Generator>ResXFileCodeGenerator</Generator>
-      <LastGenOutput>QueryParserMessages.Designer.cs</LastGenOutput>
+    <EmbeddedResource Update="Flexible\Core\Messages\QueryParserMessages.resx" >
+      <Generator Label="No generator used - we update manually."></Generator>
     </EmbeddedResource>
   </ItemGroup>
 
diff --git a/src/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj b/src/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
index 603bbf2..52d4966 100644
--- a/src/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
+++ b/src/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
@@ -25,9 +25,17 @@
 
   <PropertyGroup>
     <AssemblyTitle>Lucene.Net.Tests.QueryParser</AssemblyTitle>
+
+    <RootNamespace>Lucene.Net.QueryParsers</RootNamespace>
   </PropertyGroup>
 
   <ItemGroup>
+    <None Remove="Support\Flexible\Core\Messages\lucene-net-icon-32x32.png" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <!-- For testing whether we can get an image from a localized resource -->
+    <EmbeddedResource Include="Support\Flexible\Core\Messages\lucene-net-icon-32x32.png" />
     <EmbeddedResource Include="Xml\albumBooleanQuery.xsl;Xml\albumFilteredQuery.xsl;Xml\albumLuceneClassicQuery.xsl;Xml\BooleanFilter.xml;Xml\BooleanQuery.xml;Xml\BoostingQuery.xml;Xml\BoostingTermQuery.xml;Xml\CachedFilter.xml;Xml\ConstantScoreQuery.xml;Xml\DisjunctionMaxQuery.xml;Xml\DuplicateFilterQuery.xml;Xml\FuzzyLikeThisQuery.xml;Xml\LikeThisQuery.xml;Xml\MatchAllDocsQuery.xml;Xml\NestedBooleanQuery.xml;Xml\NumericRangeFilterQuery.xml;Xml\NumericRangeQueryQuery.xml;Xml\RangeFilter [...]
   </ItemGroup>
 
@@ -40,7 +48,26 @@
   <Import Project="$(SolutionDir)build/TestReferences.Common.targets" />
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
-    <PackageReference Include="System.Net.Primitives" Version="$(SystemNetPrimitivesPackageVersion)"/>
+    <PackageReference Include="System.Net.Primitives" Version="$(SystemNetPrimitivesPackageVersion)" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Update="Support\Flexible\Core\Messages\MessagesTest.Designer.cs">
+      <DesignTime>True</DesignTime>
+      <AutoGen>True</AutoGen>
+      <DependentUpon>MessagesTest.resx</DependentUpon>
+    </Compile>
+  </ItemGroup>
+
+  <ItemGroup>
+    <EmbeddedResource Update="Support\Flexible\Core\Messages\MessagesTest.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>MessagesTest.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+  </ItemGroup>
+
+  <ItemGroup>
+    <Folder Include="Resources\" />
   </ItemGroup>
 
 </Project>
diff --git a/src/Lucene.Net.Tests.QueryParser/Resources/LUCENE_NET_ICON_32x32.png b/src/Lucene.Net.Tests.QueryParser/Resources/LUCENE_NET_ICON_32x32.png
new file mode 100644
index 0000000..3e9d6ed
Binary files /dev/null and b/src/Lucene.Net.Tests.QueryParser/Resources/LUCENE_NET_ICON_32x32.png differ
diff --git a/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/MessagesTest.Designer.cs b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/MessagesTest.Designer.cs
new file mode 100644
index 0000000..67dc83d
--- /dev/null
+++ b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/MessagesTest.Designer.cs
@@ -0,0 +1,100 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.42000
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Lucene.Net.QueryParsers.Support.Flexible.Core.Messages {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class MessagesTest {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal MessagesTest() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Lucene.Net.QueryParsers.Support.Flexible.Core.Messages.MessagesTest", typeof(MessagesTest).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Syntax Error: {0} (TEST).
+        /// </summary>
+        internal static string INVALID_SYNTAX {
+            get {
+                return ResourceManager.GetString("INVALID_SYNTAX", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Non-hex character in Unicode escape sequence: {0} (TEST).
+        /// </summary>
+        internal static string INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE {
+            get {
+                return ResourceManager.GetString("INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Truncated unicode escape sequence. (TEST) .
+        /// </summary>
+        internal static string INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION {
+            get {
+                return ResourceManager.GetString("INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Byte[].
+        /// </summary>
+        internal static byte[] LUCENE_NET_ICON_32x32 {
+            get {
+                object obj = ResourceManager.GetObject("LUCENE_NET_ICON_32x32", resourceCulture);
+                return ((byte[])(obj));
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/MessagesTest.ja.resx b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/MessagesTest.ja.resx
new file mode 100644
index 0000000..230b8a6
--- /dev/null
+++ b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/MessagesTest.ja.resx
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="INVALID_SYNTAX" xml:space="preserve">
+    <value>構文エラー: {0}</value>
+  </data>
+  <data name="INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION" xml:space="preserve">
+    <value>切り捨てられたユニコード・エスケープ・シーケンス。</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/MessagesTest.resx b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/MessagesTest.resx
new file mode 100644
index 0000000..549d008
--- /dev/null
+++ b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/MessagesTest.resx
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="INVALID_SYNTAX" xml:space="preserve">
+    <value>Syntax Error: {0} (TEST)</value>
+  </data>
+  <data name="INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE" xml:space="preserve">
+    <value>Non-hex character in Unicode escape sequence: {0} (TEST)</value>
+  </data>
+  <data name="INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION" xml:space="preserve">
+    <value>Truncated unicode escape sequence. (TEST) </value>
+  </data>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="LUCENE_NET_ICON_32x32" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>lucene-net-icon-32x32.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/TestQueryParserMessagesDefault.cs b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/TestQueryParserMessagesDefault.cs
new file mode 100644
index 0000000..361ae08
--- /dev/null
+++ b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/TestQueryParserMessagesDefault.cs
@@ -0,0 +1,105 @@
+using Lucene.Net.QueryParsers.Flexible.Core.Messages;
+using NUnit.Framework;
+using System.Globalization;
+using Assert = Lucene.Net.TestFramework.Assert;
+
+namespace Lucene.Net.QueryParsers.Support.Flexible.Core.Messages
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    public class TestQueryParserMessagesDefault
+    {
+        [Test]
+        public void TestOverrideResourceStrings()
+        {
+            QueryParserMessages.Culture = null;
+
+            var actual = QueryParserMessages.INVALID_SYNTAX;
+
+            Assert.AreEqual("Syntax Error: {0}", actual);
+
+            actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION;
+
+            Assert.AreEqual("Truncated unicode escape sequence.", actual);
+
+            actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE;
+
+            Assert.AreEqual("Non-hex character in Unicode escape sequence: {0}", actual);
+
+            actual = QueryParserMessages.INVALID_SYNTAX_FUZZY_LIMITS;
+
+            Assert.AreEqual("The similarity value for a fuzzy search must be between 0.0 and 1.0.", actual);
+        }
+
+        [Test]
+        public void TestOverrideResourceStrings_ja()
+        {
+            QueryParserMessages.Culture = new CultureInfo("ja");
+            try
+            {
+                var actual = QueryParserMessages.INVALID_SYNTAX;
+
+                Assert.AreEqual("Syntax Error: {0}", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION;
+
+                Assert.AreEqual("Truncated unicode escape sequence.", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE;
+
+                Assert.AreEqual("Non-hex character in Unicode escape sequence: {0}", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_FUZZY_LIMITS;
+
+                Assert.AreEqual("The similarity value for a fuzzy search must be between 0.0 and 1.0.", actual);
+            }
+            finally
+            {
+                QueryParserMessages.Culture = null;
+            }
+        }
+
+        [Test]
+        public void TestOverrideResourceStrings_ja_JP()
+        {
+            QueryParserMessages.Culture = new CultureInfo("ja-JP");
+            try
+            {
+                var actual = QueryParserMessages.INVALID_SYNTAX;
+
+                Assert.AreEqual("Syntax Error: {0}", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION;
+
+                Assert.AreEqual("Truncated unicode escape sequence.", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE;
+
+                Assert.AreEqual("Non-hex character in Unicode escape sequence: {0}", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_FUZZY_LIMITS;
+
+                Assert.AreEqual("The similarity value for a fuzzy search must be between 0.0 and 1.0.", actual);
+            }
+            finally
+            {
+                QueryParserMessages.Culture = null;
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/TestQueryParserMessagesOverridden.cs b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/TestQueryParserMessagesOverridden.cs
new file mode 100644
index 0000000..f80a8e8
--- /dev/null
+++ b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/TestQueryParserMessagesOverridden.cs
@@ -0,0 +1,123 @@
+using Lucene.Net.QueryParsers.Flexible.Core.Messages;
+using Lucene.Net.Util;
+using NUnit.Framework;
+using System.Globalization;
+using Assert = Lucene.Net.TestFramework.Assert;
+
+namespace Lucene.Net.QueryParsers.Support.Flexible.Core.Messages
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    public class TestQueryParserMessagesOverridden : LuceneTestCase
+    {
+        public override void BeforeClass()
+        {
+            base.BeforeClass();
+
+            var provider = new QueryParserResourceProvider(MessagesTest.ResourceManager);
+            QueryParserMessages.SetResourceProvider(provider);
+        }
+
+        public override void AfterClass()
+        {
+            // Return to the default
+            var provider = new QueryParserResourceProvider();
+            QueryParserMessages.SetResourceProvider(provider);
+
+            base.AfterClass();
+        }
+
+        [Test]
+        public void TestOverrideResourceStrings()
+        {
+            QueryParserMessages.Culture = null;
+
+            var actual = QueryParserMessages.INVALID_SYNTAX;
+
+            Assert.IsTrue(actual.Contains("(TEST)"));
+
+            actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION;
+
+            Assert.IsTrue(actual.Contains("(TEST)"));
+
+            actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE;
+
+            Assert.IsTrue(actual.Contains("(TEST)"));
+
+            actual = QueryParserMessages.INVALID_SYNTAX_FUZZY_LIMITS;
+
+            Assert.IsFalse(actual.Contains("(TEST)")); // Fallback to default ResourceManager
+        }
+
+        [Test]
+        public void TestOverrideResourceStrings_ja()
+        {
+            QueryParserMessages.Culture = new CultureInfo("ja");
+            try
+            {
+                var actual = QueryParserMessages.INVALID_SYNTAX;
+
+                Assert.AreEqual("構文エラー: {0}", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION;
+
+                Assert.AreEqual("切り捨てられたユニコード・エスケープ・シーケンス。", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE;
+
+                Assert.AreEqual("Non-hex character in Unicode escape sequence: {0} (TEST)", actual); // Fallback to non-localized test
+
+                actual = QueryParserMessages.INVALID_SYNTAX_FUZZY_LIMITS;
+
+                Assert.AreEqual("The similarity value for a fuzzy search must be between 0.0 and 1.0.", actual); // Fallback to default ResourceManager
+            }
+            finally
+            {
+                QueryParserMessages.Culture = null;
+            }
+        }
+
+        [Test]
+        public void TestOverrideResourceStrings_ja_JP()
+        {
+            QueryParserMessages.Culture = new CultureInfo("ja-JP");
+            try
+            {
+                var actual = QueryParserMessages.INVALID_SYNTAX;
+
+                Assert.AreEqual("構文エラー: {0}", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION;
+
+                Assert.AreEqual("切り捨てられたユニコード・エスケープ・シーケンス。", actual);
+
+                actual = QueryParserMessages.INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE;
+
+                Assert.AreEqual("Non-hex character in Unicode escape sequence: {0} (TEST)", actual); // Fallback to non-localized test
+
+                actual = QueryParserMessages.INVALID_SYNTAX_FUZZY_LIMITS;
+
+                Assert.AreEqual("The similarity value for a fuzzy search must be between 0.0 and 1.0.", actual); // Fallback to default ResourceManager
+            }
+            finally
+            {
+                QueryParserMessages.Culture = null;
+            }
+        }
+    }
+}
diff --git a/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/TestQueryParserResourceProvider.cs b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/TestQueryParserResourceProvider.cs
new file mode 100644
index 0000000..6979df4
--- /dev/null
+++ b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/TestQueryParserResourceProvider.cs
@@ -0,0 +1,113 @@
+using Lucene.Net.QueryParsers.Flexible.Core.Messages;
+using Lucene.Net.Util;
+using NUnit.Framework;
+using System.Globalization;
+using Assert = Lucene.Net.TestFramework.Assert;
+
+namespace Lucene.Net.QueryParsers.Support.Flexible.Core.Messages // LUCENENET: There is no control over the namespace for code generation other than the folder, so we must use this namespace
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    public class TestQueryParserResourceProvider : LuceneTestCase
+    {
+        [Test]
+        public void TestOverrideResourceStrings()
+        {
+            var provider = new QueryParserResourceProvider(MessagesTest.ResourceManager);
+
+            var actual = provider.GetString("INVALID_SYNTAX", null);
+
+            Assert.IsTrue(actual.Contains("(TEST)"));
+
+            actual = provider.GetString("INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION", null);
+
+            Assert.IsTrue(actual.Contains("(TEST)"));
+
+            actual = provider.GetString("INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE", null);
+
+            Assert.IsTrue(actual.Contains("(TEST)"));
+
+            actual = provider.GetString("INVALID_SYNTAX_FUZZY_LIMITS", null);
+
+            Assert.AreEqual("The similarity value for a fuzzy search must be between 0.0 and 1.0.", actual); // Fallback to default ResourceManager
+        }
+
+        [Test]
+        public void TestOverrideResourceStrings_ja()
+        {
+            var provider = new QueryParserResourceProvider(MessagesTest.ResourceManager);
+
+            var actual = provider.GetString("INVALID_SYNTAX", new CultureInfo("ja"));
+
+            Assert.AreEqual("構文エラー: {0}", actual);
+
+            actual = provider.GetString("INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION", new CultureInfo("ja"));
+
+            Assert.AreEqual("切り捨てられたユニコード・エスケープ・シーケンス。", actual);
+
+            actual = provider.GetString("INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE", new CultureInfo("ja"));
+
+            Assert.AreEqual("Non-hex character in Unicode escape sequence: {0} (TEST)", actual); // Fallback to non-localized test
+
+            actual = provider.GetString("INVALID_SYNTAX_FUZZY_LIMITS", new CultureInfo("ja"));
+
+            Assert.AreEqual("The similarity value for a fuzzy search must be between 0.0 and 1.0.", actual); // Fallback to default ResourceManager
+        }
+
+        [Test]
+        public void TestOverrideResourceStrings_ja_JP()
+        {
+            var provider = new QueryParserResourceProvider(MessagesTest.ResourceManager);
+
+            var actual = provider.GetString("INVALID_SYNTAX", new CultureInfo("ja-JP"));
+
+            Assert.AreEqual("構文エラー: {0}", actual);
+
+            actual = provider.GetString("INVALID_SYNTAX_ESCAPE_UNICODE_TRUNCATION", new CultureInfo("ja-JP"));
+
+            Assert.AreEqual("切り捨てられたユニコード・エスケープ・シーケンス。", actual);
+
+            actual = provider.GetString("INVALID_SYNTAX_ESCAPE_NONE_HEX_UNICODE", new CultureInfo("ja-JP"));
+
+            Assert.AreEqual("Non-hex character in Unicode escape sequence: {0} (TEST)", actual); // Fallback to non-localized test
+
+            actual = provider.GetString("INVALID_SYNTAX_FUZZY_LIMITS", new CultureInfo("ja-JP"));
+
+            Assert.AreEqual("The similarity value for a fuzzy search must be between 0.0 and 1.0.", actual); // Fallback to default ResourceManager
+        }
+
+        [Test]
+        public void TestGetImageAsObject()
+        {
+            // Get the expected bytes
+            using var expectedStream = GetType().Assembly.GetManifestResourceStream("Lucene.Net.QueryParsers.Support.Flexible.Core.Messages.lucene-net-icon-32x32.png");
+            byte[] expectedBytes = new byte[expectedStream.Length];
+            expectedStream.Read(expectedBytes, 0, (int)expectedStream.Length);
+
+            // Check the wrapper to ensure we can read the bytes
+            Assert.AreEqual(expectedBytes, MessagesTest.LUCENE_NET_ICON_32x32);
+
+
+            var provider = new QueryParserResourceProvider(MessagesTest.ResourceManager);
+
+            byte[] actualBytes = (byte[])provider.GetObject("LUCENE_NET_ICON_32x32", null);
+
+            Assert.AreEqual(expectedBytes, actualBytes);
+        }
+    }
+}
diff --git a/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/lucene-net-icon-32x32.png b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/lucene-net-icon-32x32.png
new file mode 100644
index 0000000..227b7ce
Binary files /dev/null and b/src/Lucene.Net.Tests.QueryParser/Support/Flexible/Core/Messages/lucene-net-icon-32x32.png differ
diff --git a/src/Lucene.Net/Support/Util/IResourceProvider.cs b/src/Lucene.Net/Support/Util/IResourceProvider.cs
new file mode 100644
index 0000000..034da27
--- /dev/null
+++ b/src/Lucene.Net/Support/Util/IResourceProvider.cs
@@ -0,0 +1,94 @@
+using System;
+using System.IO;
+using System.Globalization;
+using System.Resources;
+#nullable enable
+
+namespace Lucene.Net.Util
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Contract for a set of localized resources. Generally, this is an abstraction over one or
+    /// more <see cref="ResourceManager"/> instances.
+    /// </summary>
+    public interface IResourceProvider
+    {
+        /// <summary>
+        /// Returns the value of the string resource localized for the specified <paramref name="culture"/>.
+        /// </summary>
+        /// <param name="name">The name of the resource to retrieve.</param>
+        /// <param name="culture">An object that represents the culture for which the resource is localized.</param>
+        /// <returns>The value of the resource localized for the specified <paramref name="culture"/>, or <c>null</c>
+        /// if <paramref name="name"/> cannot be found in a resource set.</returns>
+        /// <exception cref="ArgumentNullException">The <paramref name="name"/> parameter is <c>null</c>.</exception>
+        /// <exception cref="InvalidOperationException">The value of the specified resource is not a string.</exception>
+        /// <exception cref="MissingManifestResourceException">No usable set of resources has been found, and there are
+        /// no resources for a default culture. For information about how to handle this exception, see the
+        /// "Handling MissingManifestResourceException and MissingSatelliteAssemblyException Exceptions" section
+        /// in the <see cref="ResourceManager"/> class topic.</exception>
+        /// <exception cref="MissingSatelliteAssemblyException">The default culture's resources reside in a satellite
+        /// assembly that could not be found. For information about how to handle this exception, see the
+        /// "Handling MissingManifestResourceException and MissingSatelliteAssemblyException Exceptions" section
+        /// in the <see cref="ResourceManager"/> class topic.</exception>
+        string? GetString(string name, CultureInfo? culture);
+
+        /// <summary>
+        /// Gets the value of the specified non-string resource localized for the specified <paramref name="culture"/>.
+        /// </summary>
+        /// <param name="name">The name of the resource to get.</param>
+        /// <param name="culture">The culture for which the resource is localized. If the resource is not
+        /// localized for this culture, the resource manager uses fallback rules to locate an appropriate resource.
+        /// <para/>
+        /// If this value is <c>null</c>, the <see cref="CultureInfo"/> object is obtained by using the
+        /// <see cref="CultureInfo.CurrentUICulture"/> property.
+        /// </param>
+        /// <returns>The value of the resource, localized for the specified culture. If an appropriate resource
+        /// set exists but <paramref name="name"/> cannot be found, the method returns <c>null</c>.</returns>
+        /// <exception cref="ArgumentNullException">The <paramref name="name"/> parameter is <c>null</c>.</exception>
+        /// <exception cref="MissingManifestResourceException">No usable set of resources has been found, and there are
+        /// no resources for a default culture. For information about how to handle this exception, see the
+        /// "Handling MissingManifestResourceException and MissingSatelliteAssemblyException Exceptions" section
+        /// in the <see cref="ResourceManager"/> class topic.</exception>
+        /// <exception cref="MissingSatelliteAssemblyException">The default culture's resources reside in a satellite
+        /// assembly that could not be found. For information about how to handle this exception, see the
+        /// "Handling MissingManifestResourceException and MissingSatelliteAssemblyException Exceptions" section
+        /// in the <see cref="ResourceManager"/> class topic.</exception>
+        object? GetObject(string name, CultureInfo? culture);
+
+
+        /// <summary>
+        /// Returns an unmanaged memory stream object from the specified resource, using the specified <paramref name="culture"/>.
+        /// </summary>
+        /// <param name="name">The name of a resource.</param>
+        /// <param name="culture">An object that specifies the culture to use for the resource lookup. If <paramref name="culture"/>
+        /// is <c>null</c>, the culture for the current thread is used.</param>
+        /// <returns>An unmanaged memory stream object that represents a resource.</returns>
+        /// <exception cref="ArgumentNullException">The <paramref name="name"/> parameter is <c>null</c>.</exception>
+        /// <exception cref="InvalidOperationException">The value of the specified resource is not a <see cref="MemoryStream"/> object.</exception>
+        /// <exception cref="MissingManifestResourceException">No usable set of resources has been found, and there are
+        /// no resources for a default culture. For information about how to handle this exception, see the
+        /// "Handling MissingManifestResourceException and MissingSatelliteAssemblyException Exceptions" section
+        /// in the <see cref="ResourceManager"/> class topic.</exception>
+        /// <exception cref="MissingSatelliteAssemblyException">The default culture's resources reside in a satellite
+        /// assembly that could not be found. For information about how to handle this exception, see the
+        /// "Handling MissingManifestResourceException and MissingSatelliteAssemblyException Exceptions" section
+        /// in the <see cref="ResourceManager"/> class topic.</exception>
+        Stream? GetStream(string name, CultureInfo? culture);
+    }
+}