Index: common-build.xml
===================================================================
--- common-build.xml	(revision 477094)
+++ common-build.xml	(working copy)
@@ -119,6 +119,11 @@
       destdir="${build.dir}/classes/java">
       <classpath refid="classpath"/>
     </compile>
+    <copy todir="${build.dir}/classes/java">
+      <fileset dir="src/java">
+        <include name="**/*.properties"/>
+      </fileset>
+    </copy>
   </target>
 
   <target name="compile" depends="compile-core">
Index: src/test/org/apache/lucene/queryParser/TestQueryParserLocaleOperators.java
===================================================================
--- src/test/org/apache/lucene/queryParser/TestQueryParserLocaleOperators.java	(revision 0)
+++ src/test/org/apache/lucene/queryParser/TestQueryParserLocaleOperators.java	(revision 0)
@@ -0,0 +1,119 @@
+package org.apache.lucene.queryParser;
+
+/**
+ * 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.
+ */
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.SimpleAnalyzer;
+import org.apache.lucene.search.Query;
+
+public class TestQueryParserLocaleOperators extends TestCase
+{
+  public void testEmptyValueLocaleEnglish() throws Exception{
+    QueryParser qp = getParser(null);
+    qp.setBundleBaseName("org.apache.lucene.queryParser.TestQueryParserLocaleOperators");
+    qp.setLocale(Locale.ENGLISH);
+    assertQueryEquals(qp, true, "a OR b", "a or b");
+    assertQueryEquals(qp, false, "a OR b", "a b");
+  }
+
+  public void testSplitValueLocaleEnglish() throws Exception{
+    QueryParser qp = getParser(null);
+    qp.setBundleBaseName("org.apache.lucene.queryParser.TestQueryParserLocaleOperatorsSplit");
+    qp.setLocale(Locale.ENGLISH);
+    assertQueryEquals(qp, true, "a OR b", "a b");
+    assertQueryEquals(qp, true, "a or b", "a b");
+    assertQueryEquals(qp, true, "a AND b", "+a +b");
+    assertQueryEquals(qp, true, "a and b", "+a +b");
+    assertQueryEquals(qp, true, "a NOT b", "a -b");
+    assertQueryEquals(qp, true, "a not b", "a -b");
+  }
+  
+  public void testEmptyValueLocaleFrench() throws Exception{
+    QueryParser qp = getParser(null);
+    qp.setBundleBaseName("org.apache.lucene.queryParser.TestQueryParserLocaleOperators");
+    qp.setLocale(Locale.CANADA_FRENCH);
+    assertQueryEquals(qp, true, "a ET b", "a et b");
+    assertQueryEquals(qp, false, "a ET b", "a et b");
+  }
+
+  public void testOR() throws Exception {
+    verify(Locale.ENGLISH, "a OR b", "a b", "a b");
+    verify(Locale.US, "a OR b", "a b", "a b");
+    verify(Locale.UK, "a OR b", "a b", "a b");
+    verify(Locale.CANADA_FRENCH, "a OU b", "a b", "a ou b");
+    verify(Locale.CANADA_FRENCH, "a OR b", "a or b", "a b");
+    verify(Locale.FRENCH, "a OU b", "a b", "a ou b");
+    verify(Locale.FRENCH, "a OR b", "a or b", "a b");
+  }
+
+  public void testAND() throws Exception {
+    verify(Locale.ENGLISH, "a AND b", "+a +b", "+a +b");
+    verify(Locale.US, "a AND b", "+a +b", "+a +b");
+    verify(Locale.UK, "a AND b", "+a +b", "+a +b");
+    verify(Locale.CANADA_FRENCH, "a ET b", "+a +b", "a et b");
+    verify(Locale.CANADA_FRENCH, "a AND b", "a and b", "+a +b");
+    verify(Locale.FRENCH, "a ET b", "+a +b", "a et b");
+    verify(Locale.FRENCH, "a AND b", "a and b", "+a +b");
+  }
+
+  public void testNOT() throws Exception {
+    verify(Locale.ENGLISH, "a NOT b", "a -b", "a -b");
+    verify(Locale.US, "a NOT b", "a -b", "a -b");
+    verify(Locale.UK, "a NOT b", "a -b", "a -b");
+    verify(Locale.CANADA_FRENCH, "a SAUF b", "a -b", "a sauf b");
+    verify(Locale.CANADA_FRENCH, "a NOT b", "a not b", "a -b");
+    verify(Locale.FRENCH, "a SAUF b", "a -b", "a sauf b");
+    verify(Locale.FRENCH, "a NOT b", "a not b", "a -b");
+  }
+
+  public void verify(Locale locale, String userString, String actif, String inactif) 
+    throws Exception {
+    QueryParser qp = getParser(null);
+    qp.setLocale(locale);
+
+    assertQueryEquals(qp, false, userString, inactif);
+    assertQueryEquals(qp, true, userString, actif);
+  }
+
+  public void assertQueryEquals(QueryParser qp, boolean useLocalizedOperator, String query, String result)
+    throws ParseException {
+    qp.setUseLocalizedOperators(useLocalizedOperator);
+
+    Query q = qp.parse(query);
+    String s = q.toString("field");
+
+    if (!s.equals(result))
+      {
+        fail("Query (usingLocalizedOperator == " + useLocalizedOperator + ") /" + query + "/ yielded /" + s
+             + "/, expecting /" + result + "/ " + qp.getLocale());
+      }
+  }
+
+  public QueryParser getParser(Analyzer a) throws Exception {
+    if (a == null)
+      a = new SimpleAnalyzer();
+    QueryParser qp = new QueryParser("field", a);
+    qp.setDefaultOperator(QueryParser.OR_OPERATOR);
+    return qp;
+  }
+
+}
Index: src/test/org/apache/lucene/queryParser/TestQueryParserLocaleOperatorsSplit.properties
===================================================================
--- src/test/org/apache/lucene/queryParser/TestQueryParserLocaleOperatorsSplit.properties	(revision 0)
+++ src/test/org/apache/lucene/queryParser/TestQueryParserLocaleOperatorsSplit.properties	(revision 0)
@@ -0,0 +1,3 @@
+AND=AND;and
+OR=OR;or
+NOT=NOT;not
Index: src/test/org/apache/lucene/queryParser/TestQueryParserLocaleOperators.properties
===================================================================
--- src/test/org/apache/lucene/queryParser/TestQueryParserLocaleOperators.properties	(revision 0)
+++ src/test/org/apache/lucene/queryParser/TestQueryParserLocaleOperators.properties	(revision 0)
@@ -0,0 +1,3 @@
+AND=
+OR=
+NOT=
\ No newline at end of file
Index: src/java/org/apache/lucene/queryParser/QueryParser_en.properties
===================================================================
--- src/java/org/apache/lucene/queryParser/QueryParser_en.properties	(revision 0)
+++ src/java/org/apache/lucene/queryParser/QueryParser_en.properties	(revision 0)
@@ -0,0 +1,4 @@
+# Each may contain more than one value, separated by ;
+AND=AND
+OR=OR
+NOT=NOT
Index: src/java/org/apache/lucene/queryParser/QueryParser.java
===================================================================
--- src/java/org/apache/lucene/queryParser/QueryParser.java	(revision 477094)
+++ src/java/org/apache/lucene/queryParser/QueryParser.java	(working copy)
@@ -59,6 +59,7 @@
  * @author Brian Goetz
  * @author Peter Halacsy
  * @author Tatu Saloranta
+ * @author Patrick Turcotte
  */
 
 public class QueryParser implements QueryParserConstants {
@@ -71,6 +72,10 @@
   private static final int MOD_NOT     = 10;
   private static final int MOD_REQ     = 11;
 
+  /** Default BundleBaseName */
+  public static final String DEFAULT_BUNDLE_BASE_NAME
+    = "org.apache.lucene.queryParser.QueryParser";
+
   // make it possible to call setDefaultOperator() without accessing 
   // the nested class:
   /** Alternative form of QueryParser.Operator.AND */
@@ -84,7 +89,8 @@
   boolean lowercaseExpandedTerms = true;
   boolean useOldRangeQuery= false;
   boolean allowLeadingWildcard = false;
-
+  boolean useLocalizedOperators = false;
+  String bundleBaseName = DEFAULT_BUNDLE_BASE_NAME;
   Analyzer analyzer;
   String field;
   int phraseSlop = 0;
@@ -273,7 +279,8 @@
 
 
   /**
-   * Set locale used by date range parsing.
+   * Set locale used by date range parsing and Localized Operators.
+   * @see #setUseLocalizedOperators
    */
   public void setLocale(Locale locale) {
     this.locale = locale;
@@ -286,6 +293,46 @@
     return locale;
   }
 
+  /**
+   * Set use of localized operator.
+   * <p>
+   * If <code>true</code>, Operators will be read from a resourcesBundle
+   * determined by <code>getBundleBaseName()</code> and <code>getLocale</code>.
+   * The default is <code>false</code>.
+   * </p>
+   * <p>
+   * When using this feature, if hte default Locale and BundleBaseName are 
+   * not appropriate, <code>setLocale</code> and <code>setBundleBaseName</code>
+   * should be called prior to calling this method.
+   * </p>
+   * @see #setLocale
+   * @see #setBundleBaseName
+   */
+  public void setUseLocalizedOperators(boolean useLocalizedOperators){
+    this.useLocalizedOperators = useLocalizedOperators;
+    createLocalizedTokenMap();
+  }
+
+  /** @see #setUseLocalizedOperators
+  public boolean getUseLocalizedOperators(){
+    return useLocalizedOperators;
+  }
+  
+  /**
+   * Set the base name for bundles, defaults to "org.apache.lucene.queryParser.QueryParser".
+   *
+   * WARNING : if useLocalizedOperators is set to true, this bundle MUST exist, or it will fail parsing.
+   * @see #setUseLocalizedOperators
+   */
+  public void setBundleBaseName(String bundleBaseName){
+    this.bundleBaseName = bundleBaseName;
+  }
+
+  /** @see #setUseLocalizedOperators */
+  public String getBundleBaseName(){
+    return bundleBaseName;
+  }
+
   protected void addClause(Vector clauses, int conj, int mods, Query q) {
     boolean required, prohibited;
 
@@ -618,6 +665,36 @@
   }
 
   /**
+   * Create map for AND, OR and NOT token based on Locale.
+   */
+  private void createLocalizedTokenMap() {
+    token_source.useLocalizedOperators = useLocalizedOperators;
+    String splitPattern = "\\s*;\\s*";
+    if (useLocalizedOperators) {
+      // read stuff from ResourceBundle
+      ResourceBundle bundle = ResourceBundle.getBundle(bundleBaseName, locale);
+
+      // Get cases for AND
+      String[] string = bundle.getString("AND").trim().split(splitPattern);
+      List andCases = new ArrayList();
+      andCases.addAll(Arrays.asList(string));
+      token_source.andCases = andCases;
+
+      // Get cases for OR
+      string = bundle.getString("OR").trim().split(splitPattern);
+      List orCases = new ArrayList();
+      orCases.addAll(Arrays.asList(string));
+      token_source.orCases = orCases;
+
+      // Get cases for NOT
+      string = bundle.getString("NOT").trim().split(splitPattern);
+      List notCases = new ArrayList();
+      notCases.addAll(Arrays.asList(string));
+      token_source.notCases = notCases;
+    }
+  }
+
+  /**
    * Returns a String where the escape char has been
    * removed, or kept only once if there was a double escape.
    * 
Index: src/java/org/apache/lucene/queryParser/QueryParser.jj
===================================================================
--- src/java/org/apache/lucene/queryParser/QueryParser.jj	(revision 477094)
+++ src/java/org/apache/lucene/queryParser/QueryParser.jj	(working copy)
@@ -83,6 +83,7 @@
  * @author Brian Goetz
  * @author Peter Halacsy
  * @author Tatu Saloranta
+ * @author Patrick Turcotte
  */
 
 public class QueryParser {
@@ -95,6 +96,10 @@
   private static final int MOD_NOT     = 10;
   private static final int MOD_REQ     = 11;
 
+  /** Default BundleBaseName */
+  public static final String DEFAULT_BUNDLE_BASE_NAME 
+    = "org.apache.lucene.queryParser.QueryParser";
+
   // make it possible to call setDefaultOperator() without accessing 
   // the nested class:
   /** Alternative form of QueryParser.Operator.AND */
@@ -108,7 +113,8 @@
   boolean lowercaseExpandedTerms = true;
   boolean useOldRangeQuery= false;  
   boolean allowLeadingWildcard = false;
-
+  boolean useLocalizedOperators = false;
+  String bundleBaseName = DEFAULT_BUNDLE_BASE_NAME;
   Analyzer analyzer;
   String field;
   int phraseSlop = 0;
@@ -297,7 +303,8 @@
   
 
   /**
-   * Set locale used by date range parsing.
+   * Set locale used by date range parsing and Localized Operators.
+   * @see #setUseLocalizedOperators
    */
   public void setLocale(Locale locale) {
     this.locale = locale;
@@ -310,6 +317,46 @@
     return locale;
   }
 
+  /**
+   * Set use of localized operator.
+   * <p>
+   * If <code>true</code>, Operators will be read from a resourcesBundle
+   * determined by <code>getBundleBaseName()</code> and <code>getLocale</code>.
+   * The default is <code>false</code>.
+   * </p>
+   * <p>
+   * When using this feature, if hte default Locale and BundleBaseName are 
+   * not appropriate, <code>setLocale</code> and <code>setBundleBaseName</code>
+   * should be called prior to calling this method.
+   * </p>
+   * @see #setLocale
+   * @see #setBundleBaseName
+   */
+  public void setUseLocalizedOperators(boolean useLocalizedOperators){
+    this.useLocalizedOperators = useLocalizedOperators;
+    createLocalizedTokenMap();
+  }
+  
+  /** @see #setUseLocalizedOperators
+  public boolean getUseLocalizedOperators(){
+    return useLocalizedOperators;
+  }
+  
+  /**
+   * Set the base name for bundles, defaults to "org.apache.lucene.queryParser.QueryParser".
+   *
+   * WARNING : if useLocalizedOperators is set to true, this bundle MUST exist, or it will fail parsing.
+   * @see #setUseLocalizedOperators
+   */
+  public void setBundleBaseName(String bundleBaseName){
+    this.bundleBaseName = bundleBaseName;
+  }
+  
+  /** @see #setUseLocalizedOperators */
+  public String getBundleBaseName(){
+    return bundleBaseName;
+  }
+   
   protected void addClause(Vector clauses, int conj, int mods, Query q) {
     boolean required, prohibited;
 
@@ -642,6 +689,36 @@
   }
 
   /**
+   * Create map for AND, OR and NOT token based on Locale.
+   */
+  private void createLocalizedTokenMap() {
+    token_source.useLocalizedOperators = useLocalizedOperators;
+    String splitPattern = "\\s*;\\s*";
+    if (useLocalizedOperators) {
+      // read stuff from ResourceBundle
+      ResourceBundle bundle = ResourceBundle.getBundle(bundleBaseName, locale);
+
+      // Get cases for AND
+      String[] string = bundle.getString("AND").trim().split(splitPattern);
+      List andCases = new ArrayList();
+      andCases.addAll(Arrays.asList(string));
+      token_source.andCases = andCases;
+
+      // Get cases for OR
+      string = bundle.getString("OR").trim().split(splitPattern);
+      List orCases = new ArrayList();
+      orCases.addAll(Arrays.asList(string));
+      token_source.orCases = orCases;
+
+      // Get cases for NOT
+      string = bundle.getString("NOT").trim().split(splitPattern);
+      List notCases = new ArrayList();
+      notCases.addAll(Arrays.asList(string));
+      token_source.notCases = notCases;
+    }
+  }
+
+  /**
    * Returns a String where the escape char has been
    * removed, or kept only once if there was a double escape.
    * 
@@ -782,8 +859,23 @@
 
 <DEFAULT> TOKEN : {
   <AND:       ("AND" | "&&") >
+{
+  if (useLocalizedOperators && !matchedToken.image.equals("&&")){
+    matchedToken.kind = parseLocalized(matchedToken.image);
+  }
+}
 | <OR:        ("OR" | "||") >
+{
+  if (useLocalizedOperators && !matchedToken.image.equals("||")){
+    matchedToken.kind = parseLocalized(matchedToken.image);
+  }
+}
 | <NOT:       ("NOT" | "!") >
+{
+  if (useLocalizedOperators && !matchedToken.image.equals("!")){
+    matchedToken.kind = parseLocalized(matchedToken.image);
+  }
+}
 | <PLUS:      "+" >
 | <MINUS:     "-" >
 | <LPAREN:    "(" >
@@ -792,6 +884,11 @@
 | <CARAT:     "^" > : Boost
 | <QUOTED:     "\"" (~["\""] | "\\\"")+ "\"">
 | <TERM:      <_TERM_START_CHAR> (<_TERM_CHAR>)*  >
+{
+  if (useLocalizedOperators){
+    matchedToken.kind = parseLocalized(matchedToken.image);
+  }
+}
 | <FUZZY_SLOP:     "~" ( (<_NUM_CHAR>)+ ( "." (<_NUM_CHAR>)+ )? )? >
 | <PREFIXTERM:  (<_TERM_START_CHAR> | "*") (<_TERM_CHAR>)* "*" >
 | <WILDTERM:  (<_TERM_START_CHAR> | [ "*", "?" ]) (<_TERM_CHAR> | ( [ "*", "?" ] ))* >
@@ -1000,3 +1097,22 @@
     return q;
   }
 }
+
+TOKEN_MGR_DECLS :
+{
+  List andCases = new ArrayList();
+  List orCases = new ArrayList();
+  List notCases = new ArrayList();
+  boolean useLocalizedOperators = false;
+
+  protected int parseLocalized(String tokenImage) {
+    if (andCases.contains(tokenImage)) {
+      return AND;
+    } else if (orCases.contains(tokenImage)) {
+      return OR;
+    } else if (notCases.contains(tokenImage)) {
+      return NOT;
+    } 
+    return TERM;
+  }
+}
Index: src/java/org/apache/lucene/queryParser/QueryParser_fr.properties
===================================================================
--- src/java/org/apache/lucene/queryParser/QueryParser_fr.properties	(revision 0)
+++ src/java/org/apache/lucene/queryParser/QueryParser_fr.properties	(revision 0)
@@ -0,0 +1,4 @@
+# Each may contain more than one value, separated by ;
+AND=ET
+OR=OU
+NOT=SAUF
Index: src/java/org/apache/lucene/queryParser/QueryParser.properties
===================================================================
--- src/java/org/apache/lucene/queryParser/QueryParser.properties	(revision 0)
+++ src/java/org/apache/lucene/queryParser/QueryParser.properties	(revision 0)
@@ -0,0 +1,4 @@
+# Each may contain more than one value, separated by ;
+AND=AND
+OR=OR
+NOT=NOT
Index: src/java/org/apache/lucene/queryParser/QueryParserTokenManager.java
===================================================================
--- src/java/org/apache/lucene/queryParser/QueryParserTokenManager.java	(revision 477094)
+++ src/java/org/apache/lucene/queryParser/QueryParserTokenManager.java	(working copy)
@@ -12,6 +12,21 @@
 
 public class QueryParserTokenManager implements QueryParserConstants
 {
+  List andCases = new ArrayList();
+  List orCases = new ArrayList();
+  List notCases = new ArrayList();
+  boolean useLocalizedOperators = false;
+
+  protected int parseLocalized(String tokenImage) {
+    if (andCases.contains(tokenImage)) {
+      return AND;
+    } else if (orCases.contains(tokenImage)) {
+      return OR;
+    } else if (notCases.contains(tokenImage)) {
+      return NOT;
+    }
+    return TERM;
+  }
   public  java.io.PrintStream debugStream = System.out;
   public  void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
 private final int jjStopStringLiteralDfa_3(int pos, long active0)
@@ -968,6 +983,9 @@
 protected CharStream input_stream;
 private final int[] jjrounds = new int[37];
 private final int[] jjstateSet = new int[74];
+StringBuffer image;
+int jjimageLen;
+int lengthOfMatch;
 protected char curChar;
 public QueryParserTokenManager(CharStream stream)
 {
@@ -1045,6 +1063,8 @@
       matchedToken = jjFillToken();
       return matchedToken;
    }
+   image = null;
+   jjimageLen = 0;
 
    switch(curLexState)
    {
@@ -1076,6 +1096,7 @@
         if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L)
         {
            matchedToken = jjFillToken();
+           TokenLexicalActions(matchedToken);
        if (jjnewLexState[jjmatchedKind] != -1)
          curLexState = jjnewLexState[jjmatchedKind];
            return matchedToken;
@@ -1110,4 +1131,48 @@
   }
 }
 
+void TokenLexicalActions(Token matchedToken)
+{
+   switch(jjmatchedKind)
+   {
+      case 7 :
+        if (image == null)
+            image = new StringBuffer(new String(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))));
+         else
+            image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
+  if (useLocalizedOperators && !matchedToken.image.equals("&&")){
+    matchedToken.kind = parseLocalized(matchedToken.image);
+  }
+         break;
+      case 8 :
+        if (image == null)
+            image = new StringBuffer(new String(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))));
+         else
+            image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
+  if (useLocalizedOperators && !matchedToken.image.equals("||")){
+    matchedToken.kind = parseLocalized(matchedToken.image);
+  }
+         break;
+      case 9 :
+        if (image == null)
+            image = new StringBuffer(new String(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))));
+         else
+            image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
+  if (useLocalizedOperators && !matchedToken.image.equals("!")){
+    matchedToken.kind = parseLocalized(matchedToken.image);
+  }
+         break;
+      case 17 :
+        if (image == null)
+            image = new StringBuffer(new String(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))));
+         else
+            image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
+  if (useLocalizedOperators){
+    matchedToken.kind = parseLocalized(matchedToken.image);
+  }
+         break;
+      default : 
+         break;
+   }
 }
+}
