You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by kk...@apache.org on 2016/02/07 01:43:07 UTC

[6/6] tika git commit: Roll in new lang detect support in new module

Roll in new lang detect support in new module

This is one of several steps. Next is to get rid of the old support for
language detection (in tika-core), and fix up all of those dependencies.


Project: http://git-wip-us.apache.org/repos/asf/tika/repo
Commit: http://git-wip-us.apache.org/repos/asf/tika/commit/d621ce8d
Tree: http://git-wip-us.apache.org/repos/asf/tika/tree/d621ce8d
Diff: http://git-wip-us.apache.org/repos/asf/tika/diff/d621ce8d

Branch: refs/heads/2.x
Commit: d621ce8d4553e300742c0f64b0e1d95143783332
Parents: 96886cd
Author: Ken Krugler <ke...@transpac.com>
Authored: Sat Feb 6 16:42:54 2016 -0800
Committer: Ken Krugler <ke...@transpac.com>
Committed: Sat Feb 6 16:42:54 2016 -0800

----------------------------------------------------------------------
 pom.xml                                         |   1 +
 tika-langdetect/pom.xml                         | 156 ++++++++++++
 .../tika/langdetect/LanguageConfidence.java     |   8 +
 .../tika/langdetect/LanguageDetector.java       | 182 ++++++++++++++
 .../apache/tika/langdetect/LanguageHandler.java |  52 ++++
 .../apache/tika/langdetect/LanguageNames.java   |  70 ++++++
 .../apache/tika/langdetect/LanguageResult.java  |  75 ++++++
 .../apache/tika/langdetect/LanguageWriter.java  |  69 ++++++
 .../tika/langdetect/OptimaizeLangDetector.java  | 171 +++++++++++++
 .../tika/langdetect/LanguageDetectorTest.java   |  76 ++++++
 .../tika/langdetect/LanguageNamesTest.java      |  22 ++
 .../langdetect/OptimaizeLangDetectorTest.java   | 246 +++++++++++++++++++
 .../src/test/resources/log4j.properties         |  24 ++
 .../apache/tika/langdetect/language-codes.txt   | 186 ++++++++++++++
 .../tika/langdetect/language-tests/da.test      | 108 ++++++++
 .../tika/langdetect/language-tests/de.test      | 104 ++++++++
 .../tika/langdetect/language-tests/el.test      | 109 ++++++++
 .../tika/langdetect/language-tests/en.test      | 105 ++++++++
 .../tika/langdetect/language-tests/es.test      | 107 ++++++++
 .../tika/langdetect/language-tests/et.test      |  17 ++
 .../tika/langdetect/language-tests/fi.test      | 106 ++++++++
 .../tika/langdetect/language-tests/fr.test      | 105 ++++++++
 .../tika/langdetect/language-tests/it.test      | 109 ++++++++
 .../tika/langdetect/language-tests/ja.test      |  78 ++++++
 .../tika/langdetect/language-tests/lt.test      |  32 +++
 .../tika/langdetect/language-tests/nl.test      | 105 ++++++++
 .../tika/langdetect/language-tests/pt.test      | 105 ++++++++
 .../tika/langdetect/language-tests/sv.test      | 108 ++++++++
 .../tika/langdetect/language-tests/th.test      |  28 +++
 .../tika/langdetect/language-tests/zh.test      |  57 +++++
 .../org/apache/tika/langdetect/udhr-known.txt   |  11 +
 .../org/apache/tika/langdetect/udhr-unknown.txt |   4 +
 32 files changed, 2736 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 2d867e4..f4024e3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -56,6 +56,7 @@
     <module>tika-bundle</module>
     <module>tika-server</module>
     <module>tika-translate</module>
+    <module>tika-langdetect</module>
     <module>tika-example</module>
     <module>tika-java7</module>
     <module>tika-parser-modules</module>

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/pom.xml
----------------------------------------------------------------------
diff --git a/tika-langdetect/pom.xml b/tika-langdetect/pom.xml
new file mode 100644
index 0000000..0d35f58
--- /dev/null
+++ b/tika-langdetect/pom.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.tika</groupId>
+    <artifactId>tika-parent</artifactId>
+    <version>2.0-SNAPSHOT</version>
+    <relativePath>../tika-parent/pom.xml</relativePath>
+  </parent>
+
+  <artifactId>tika-langdetect</artifactId>
+  <packaging>bundle</packaging>
+  <name>Apache Tika language detection</name>
+  <url>http://tika.apache.org/</url>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.tika</groupId>
+      <artifactId>tika-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.optimaize.languagedetector</groupId>
+      <artifactId>language-detector</artifactId>
+      <version>0.5</version>
+    </dependency>
+
+    <!-- Test dependencies -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-DocURL>${project.url}</Bundle-DocURL>
+            <Bundle-Activator>
+              org.apache.tika.parser.internal.Activator
+            </Bundle-Activator>
+            <Import-Package>
+              org.w3c.dom,
+              org.apache.tika.*,
+              *;resolution:=optional
+            </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>src/main/java/org/apache/tika/parser/txt/Charset*.java</exclude>
+            <exclude>src/test/resources/test-documents/**</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+
+    <pluginManagement>
+      <plugins>
+        <!-- This plugin's configuration is used to store Eclipse m2e      -->
+        <!-- settings only. It has no influence on the Maven build itself. -->
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-scr-plugin</artifactId>
+                    <versionRange>[1.7.2,)</versionRange>
+                    <goals>
+                      <goal>scr</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <execute />
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+
+  <description>This is the language detection Apache Tika™ toolkit.
+  </description>
+  <organization>
+    <name>The Apache Software Foundation</name>
+    <url>http://www.apache.org</url>
+  </organization>
+  <scm>
+    <url>http://svn.apache.org/viewvc/tika/trunk/tika-langdetect</url>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/tika/trunk/tika-langdetect</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/tika/trunk/tika-langdetect</developerConnection>
+  </scm>
+  <issueManagement>
+    <system>JIRA</system>
+    <url>https://issues.apache.org/jira/browse/TIKA</url>
+  </issueManagement>
+  <ciManagement>
+    <system>Jenkins</system>
+    <url>https://builds.apache.org/job/Tika-trunk/</url>
+  </ciManagement>
+</project>

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageConfidence.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageConfidence.java b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageConfidence.java
new file mode 100644
index 0000000..9c689fa
--- /dev/null
+++ b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageConfidence.java
@@ -0,0 +1,8 @@
+package org.apache.tika.langdetect;
+
+public enum LanguageConfidence {
+
+	HIGH,
+	MEDIUM,
+	LOW
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageDetector.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageDetector.java b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageDetector.java
new file mode 100644
index 0000000..62592db
--- /dev/null
+++ b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageDetector.java
@@ -0,0 +1,182 @@
+package org.apache.tika.langdetect;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+// We should use the IANA registry for primary language names...see
+// http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
+// There must be a package that uses this dataset to support knowledge of
+// the default script, etc. And how to map from <lang>-<country> (e.g. 'zh-CN')
+// to <sublang> ('cmn'), or <lang>-<sublang> to <sublan> ('zh-cmn' => 'cmn')
+// We'd also want to know the default sublang for a macro language ('zh' => 'zh-cmn')
+// There's also mapping 'zh-CN' to 'cmn-Hans' (simplified chinese script)
+
+// TODO decide how deep to go into supporting extended language tags, see
+// http://www.w3.org/International/articles/language-tags/. For example,
+// what should you expect from calling hasModel("en-GB") if there's only
+// a model for "en"?
+
+// This is mostly an issue for interpreting language tags in (X)HTML docs,
+// and maybe XML if we really care. In those cases you could get something
+// like "ast" (three letter language code), or even zh-cmn-Hant-SG
+// (Chinese, Mandarin, Traditional script, in Singapore) plus additional:
+// language-extlang-script-region-variant-extension-privateuse
+
+// The full spec is at http://www.rfc-editor.org/rfc/bcp/bcp47.txt
+
+public abstract class LanguageDetector {
+
+	// True if text is expected to be a mix of languages, and thus higher-resolution
+	// detection must be done to avoid under-sampling the text.
+	protected boolean mixedLanguages = false;
+	
+	// True if the text is expected to be 'short' (typically less than 100 chars), and
+	// thus a different algorithm and/or set of profiles should be used.
+	protected boolean shortText = false;
+	
+	public boolean isMixedLanguages() {
+		return mixedLanguages;
+	}
+	
+	public LanguageDetector setMixedLanguages(boolean mixedLanguages) {
+		this.mixedLanguages = mixedLanguages;
+		return this;
+	}
+	
+	public boolean isShortText() {
+		return shortText;
+	}
+	
+	public LanguageDetector setShortText(boolean shortText) {
+		this.shortText = shortText;
+		return this;
+	}
+	
+	/**
+	 * Load (or re-load) all available language models. This must
+	 * be called after any settings that would impact the models
+	 * being loaded (e.g. mixed language/short text), but
+	 * before any of the document processing routines (below)
+	 * are called. Note that it only needs to be called once.
+	 * 
+	 * @return this
+	 */
+	public abstract LanguageDetector loadModels() throws IOException;
+	
+	/**
+	 * Load (or re-load) the models specified in <languages>. These use the
+	 * ISO 639-1 names, with an optional "-<country code>" for more
+	 * specific specification (e.g. "zh-CN" for Chinese in China).
+	 * 
+	 * @param languages list of target languages.
+	 * @return this
+	 */
+	public abstract LanguageDetector loadModels(Set<String> languages) throws IOException;
+	
+	/**
+	 * Provide information about whether a model exists for a specific
+	 * language.
+	 * 
+	 * @param language ISO 639-1 name for language
+	 * @return true if a model for this language exists.
+	 */
+	public abstract boolean hasModel(String language);
+	
+	/**
+	 * Set the a-priori probabilities for these languages. The provided map uses the language
+	 * as the key, and the probability (0.0 > probability < 1.0) of text being in that language.
+	 * Note that if the probabilities don't sum to 1.0, these values will be normalized.
+	 * 
+	 * If hasModel() returns false for any of the languages, an IllegalArgumentException is thrown.
+	 * 
+	 * Use of these probabilities is detector-specific, and thus might not impact the results at all.
+	 * As such, these should be viewed as a hint.
+	 * 
+	 * @param languageProbabilities Map from language to probability
+	 * @return this
+	 */
+	public abstract LanguageDetector setPriors(Map<String, Float> languageProbabilities) throws IOException;
+	
+	// ============================================================
+	// The routines below are called when processing a document
+	// ============================================================
+
+	/**
+	 * Reset statistics about the current document being processed
+	 */
+	public abstract void reset();
+	
+	/**
+	 * Add statistics about this text for the current document. Note
+	 * that we assume an implicit word break exists before/after
+	 * each of these runs of text.
+	 * 
+	 * @param cbuf Character buffer
+	 * @param off Offset into cbuf to first character in the run of text
+	 * @param len Number of characters in the run of text.
+	 */
+	public abstract void addText(char[] cbuf, int off, int len);
+	
+	/**
+	 * Add <text> to the statistics being accumulated for the current
+	 * document. Note that this is a default implementation for adding
+	 * a string (not optimized)
+	 * 
+	 * @param text Characters to add to current statistics.
+	 */
+	public void addText(CharSequence text) {
+		char[] chars = text.toString().toCharArray();
+		addText(chars, 0, chars.length);
+	}
+
+	
+	/**
+	 * Tell the caller whether more text is required for the current document
+	 * before the language can be reliably detected.
+	 * 
+	 * Implementations can override this to do early termination of stats
+	 * collection, which can improve performance with longer documents.
+	 * 
+	 * Note that detect() can be called even when this returns false
+	 * 
+	 * @return true if we have enough text for reliable detection.
+	 */
+	public boolean hasEnoughText() {
+		return false;
+	}
+	
+	/**
+	 * Detect languages based on previously submitted text (via addText calls).
+	 * 
+	 * @return list of all possible languages with at least medium confidence,
+	 * 			sorted by confidence from highest to lowest.
+	 */
+	public abstract List<LanguageResult> detectAll();
+	
+	public LanguageResult detect() {
+		List<LanguageResult> results = detectAll();
+		return results.isEmpty() ? null : results.get(0);
+	}
+
+	/**
+	 * Utility wrapper that detects the language of a given chunk of text.
+	 * 
+	 * @param text String to add to current statistics.
+	 * @return list of all possible languages with at least medium confidence,
+	 * 			sorted by confidence from highest to lowest.
+	 */
+	public List<LanguageResult> detectAll(String text) {
+		reset();
+		addText(text);
+		return detectAll();
+	}
+	
+	public LanguageResult detect(CharSequence text) {
+		reset();
+		addText(text);
+		return detect();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageHandler.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageHandler.java b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageHandler.java
new file mode 100644
index 0000000..f317950
--- /dev/null
+++ b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageHandler.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+package org.apache.tika.langdetect;
+
+import org.apache.tika.sax.WriteOutContentHandler;
+
+/**
+ * SAX content handler that updates a language detector based on all the
+ * received character content.
+ *
+ * @since Apache Tika 0.10
+ */
+public class LanguageHandler extends WriteOutContentHandler {
+
+    private final LanguageWriter writer;
+
+    public LanguageHandler(LanguageWriter writer) {
+        super(writer);
+        
+        this.writer = writer;
+    }
+
+    public LanguageHandler(LanguageDetector detector) {
+        this(new LanguageWriter(detector));
+    }
+
+    /**
+     * Returns the language detector used by this content handler.
+     * Note that the returned detector gets updated whenever new SAX events
+     * are received by this content handler.
+     *
+     * @return language detector
+     */
+    public LanguageDetector getDetector() {
+        return writer.getDetector();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageNames.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageNames.java b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageNames.java
new file mode 100644
index 0000000..abed277
--- /dev/null
+++ b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageNames.java
@@ -0,0 +1,70 @@
+package org.apache.tika.langdetect;
+
+import java.util.Locale;
+
+/**
+ * Support for language tags (as defined by https://tools.ietf.org/html/bcp47)
+ * 
+ * See https://en.wikipedia.org/wiki/List_of_ISO_639-3_codes for a list of
+ * three character language codes.
+ * 
+ * TODO change to LanguageTag, and use these vs. strings everywhere in the
+ * language detector API?
+ *
+ */
+public class LanguageNames {
+
+	public static String makeName(String language, String script, String region) {
+		Locale locale = new Locale.Builder().setLanguage(language).setScript(script).setRegion(region).build();
+		return locale.toLanguageTag();
+	}
+
+	public static String normalizeName(String languageTag) {
+		Locale locale = Locale.forLanguageTag(languageTag);
+		return locale.toLanguageTag();
+	}
+	
+	public static boolean isMacroLanguage(String languageTag) {
+		Locale locale = Locale.forLanguageTag(languageTag);
+		// TODO make it so.
+		return false;
+	}
+	
+	public static boolean hasMacroLanguage(String languageTag) {
+		Locale locale = Locale.forLanguageTag(languageTag);
+		// TODO make it so
+		return false;
+	}
+	
+	/**
+	 * If language is a specific variant of a macro language (e.g. 'nb' for Norwegian Bokmal),
+	 * return the macro language (e.g. 'no' for Norwegian). If it doesn't have a macro language,
+	 * return unchanged.
+	 * 
+	 * @param languageTag
+	 * @return
+	 */
+	public static String getMacroLanguage(String languageTag) {
+		// TODO make it so
+		return languageTag;
+	}
+	
+	public static boolean equals(String languageTagA, String languageTagB) {
+		Locale localeA = Locale.forLanguageTag(languageTagA);
+		Locale localeB = Locale.forLanguageTag(languageTagB);
+		
+		// TODO Fill in script if missing and something we could derive from lang+region
+		// e.g. zh-CN => zh-Hans-CN, zh-TW => zh-Hant-TW.
+		
+		// TODO Treat missing script == present script, if present script is default (suppressed) for
+		// the language. So "en-Latn" == "en"
+		
+		// TODO probably OK to ignore extensions
+		
+		// TODO Do we want/need a fuzzy match for region (and script)
+		// E.g. are 'en' and 'en-GB' equal? Depends on the direction, e.g. if you want 'en', and
+		// you get back something more specific (en-GB) then that's OK, but if you explicitly want
+		// en-GB and you get back en then that might not be OK.
+		return localeA.equals(localeB);
+	}
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageResult.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageResult.java b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageResult.java
new file mode 100644
index 0000000..e02b1bd
--- /dev/null
+++ b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageResult.java
@@ -0,0 +1,75 @@
+package org.apache.tika.langdetect;
+
+import java.util.Locale;
+
+public class LanguageResult {
+
+	private String language;
+	
+	private LanguageConfidence confidence;
+	
+	// rawScore should be a number from 0.0 to 1.0, with higher values implying
+	// greater confidence.
+	private float rawScore;
+	
+	/**
+	 * 
+	 * @param language ISO 639-1 language code (plus optional "-<country code>")
+	 * @param rawScore confidence of detector in the result.
+	 */
+	public LanguageResult(String language, LanguageConfidence confidence, float rawScore) {
+		this.language = language;
+		this.confidence = confidence;
+		this.rawScore = rawScore;
+	}
+
+	public String getLanguage() {
+		return language;
+	}
+
+	public float getRawScore() {
+		return rawScore;
+	}
+	
+	public LanguageConfidence getConfidence() {
+		return confidence;
+	}
+	
+	public boolean isReasonablyCertain() {
+		return confidence == LanguageConfidence.HIGH;
+	}
+	
+	/**
+	 * Return true if the target language matches the detected language. We consider
+	 * it a match if, for the precision requested or detected, it matches. This means:
+	 * 
+	 * target	|	detected	| match?
+	 * zh		|	en			| false
+	 * zh		|	zh			| true
+	 * zh		|	zh-CN		| true
+	 * zh-CN	|	zh			| true
+	 * zh-CN	|	zh-TW		| false
+	 * zh-CN	|	zh-cn		| true (case-insensitive)
+	 * 
+	 * @param language
+	 * @return
+	 */
+	public boolean isLanguage(String language) {
+		String[] targetLanguage = language.split("\\-");
+		String[] resultLanguage = this.language.split("\\-");
+		
+		int minLength = Math.min(targetLanguage.length, resultLanguage.length);
+		for (int i = 0; i < minLength; i++) {
+			if (!targetLanguage[i].equalsIgnoreCase(resultLanguage[i])) {
+				return false;
+			}
+		}
+		
+		return true;
+	}
+	
+	@Override
+	public String toString() {
+		return String.format(Locale.US, "%s: %s (%f)", language, confidence, rawScore);
+	}
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageWriter.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageWriter.java b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageWriter.java
new file mode 100644
index 0000000..18026b2
--- /dev/null
+++ b/tika-langdetect/src/main/java/org/apache/tika/langdetect/LanguageWriter.java
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+package org.apache.tika.langdetect;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Writer that builds a language profile based on all the written content.
+ *
+ * @since Apache Tika 0.10
+ */
+public class LanguageWriter extends Writer {
+
+    private final LanguageDetector detector;
+
+    public LanguageWriter(LanguageDetector detector) {
+        this.detector = detector;
+        detector.reset();
+    }
+
+    /**
+     * Returns the language detector used by this writer. Note that
+     * the returned language detector gets updated whenever new characters
+     * are written.
+     *
+     * @return language detector
+     */
+    public LanguageDetector getDetector() {
+        return detector;
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) {
+    	detector.addText(cbuf, off, len);
+    }
+
+    /**
+     * Ignored.
+     */
+    @Override
+    public void close() throws IOException {
+    }
+
+    /**
+     * Ignored.
+     */
+    @Override
+    public void flush() {
+    }
+
+    public void reset() {
+    	detector.reset();
+    }
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/main/java/org/apache/tika/langdetect/OptimaizeLangDetector.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/main/java/org/apache/tika/langdetect/OptimaizeLangDetector.java b/tika-langdetect/src/main/java/org/apache/tika/langdetect/OptimaizeLangDetector.java
new file mode 100644
index 0000000..4aa93c1
--- /dev/null
+++ b/tika-langdetect/src/main/java/org/apache/tika/langdetect/OptimaizeLangDetector.java
@@ -0,0 +1,171 @@
+package org.apache.tika.langdetect;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.optimaize.langdetect.DetectedLanguage;
+import com.optimaize.langdetect.LanguageDetectorBuilder;
+import com.optimaize.langdetect.i18n.LdLocale;
+import com.optimaize.langdetect.ngram.NgramExtractors;
+import com.optimaize.langdetect.profiles.BuiltInLanguages;
+import com.optimaize.langdetect.profiles.LanguageProfile;
+import com.optimaize.langdetect.profiles.LanguageProfileReader;
+
+/**
+ * Implementation of the LanguageDetector API that uses
+ * https://github.com/optimaize/language-detector
+ */
+public class OptimaizeLangDetector extends LanguageDetector {
+
+	private static final int MAX_CHARS_FOR_DETECTION = 20000;
+	private static final int MAX_CHARS_FOR_SHORT_DETECTION = 200;
+	
+	private com.optimaize.langdetect.LanguageDetector detector;
+	private CharArrayWriter writer;
+	private Set<String> languages;
+	private Map<String, Float> languageProbabilities;
+	
+	public OptimaizeLangDetector() {
+		super();
+		
+		writer = new CharArrayWriter(MAX_CHARS_FOR_DETECTION);
+	}
+	
+	@Override
+	public LanguageDetector loadModels() throws IOException {
+		List<LanguageProfile> languageProfiles = new LanguageProfileReader().readAllBuiltIn();
+		
+		// FUTURE when the "language-detector" project supports short profiles, check if
+		// isShortText() returns true and switch to those.
+		
+		languages = new HashSet<>();
+		for (LanguageProfile profile : languageProfiles) {
+			languages.add(makeLanguageName(profile.getLocale()));
+		}
+		
+		detector = createDetector(languageProfiles);
+		
+		return this;
+
+	}
+
+	private String makeLanguageName(LdLocale locale) {
+		return LanguageNames.makeName(locale.getLanguage(), locale.getScript().orNull(), locale.getRegion().orNull());
+	}
+
+	@Override
+	public LanguageDetector loadModels(Set<String> languages) throws IOException {
+		
+		// Normalize languages.
+		this.languages = new HashSet<>(languages.size());
+		for (String language : languages) {
+			this.languages.add(LanguageNames.normalizeName(language));
+		}
+		
+		// TODO what happens if you request a language that has no profile?
+		Set<LdLocale> locales = new HashSet<>();
+		for (LdLocale locale : BuiltInLanguages.getLanguages()) {
+			String languageName = makeLanguageName(locale);
+			if (this.languages.contains(languageName)) {
+				locales.add(locale);
+			}
+		}
+		
+		detector = createDetector(new LanguageProfileReader().readBuiltIn(locales));
+		
+		return this;
+	}
+
+	private com.optimaize.langdetect.LanguageDetector createDetector(List<LanguageProfile> languageProfiles) {
+		// FUTURE decide whether we really want to use the short text algorithm when dealing with mixed languages,
+		// as that would get really, really slow for big chunks of text.
+		LanguageDetectorBuilder builder = LanguageDetectorBuilder.create(NgramExtractors.standard())
+				.shortTextAlgorithm(mixedLanguages ? Integer.MAX_VALUE : 100)
+		        .withProfiles(languageProfiles);
+		
+		if (languageProbabilities != null) {
+			Map<LdLocale, Double> languageWeights = new HashMap<>(languageProbabilities.size());
+			for (String language : languageProbabilities.keySet()) {
+				Double priority = (double)languageProbabilities.get(language);
+				languageWeights.put(LdLocale.fromString(language), priority);
+			}
+			
+			builder.languagePriorities(languageWeights);
+		}
+		
+		return builder.build();
+	}
+	
+	@Override
+	public boolean hasModel(String language) {
+		return languages.contains(language);
+	}
+
+	@Override
+	public LanguageDetector setPriors(Map<String, Float> languageProbabilities) throws IOException {
+		this.languageProbabilities = languageProbabilities;
+		
+		loadModels(languageProbabilities.keySet());
+
+		return this;
+	}
+	
+	@Override
+	public void reset() {
+		writer.reset();
+	}
+
+	@Override
+	public void addText(char[] cbuf, int off, int len) {
+		if (hasEnoughText()) {
+			return; // do nothing if we've already got enough text.
+		}
+		
+		writer.write(cbuf, off, len);
+		
+		// FUTURE - use support to get padding char from NGramExtractors.standard().
+		// We'd like to get the textPadding character from the NGramExtractor, but
+		// that's not exposed. NGramExtractors.standard() returns extractor with ' '
+		// as padding, so that's what we'll use here.
+		writer.write(' ');
+	}
+
+	@Override
+	public List<LanguageResult> detectAll() {
+		// TODO throw exception if models haven't been loaded, or auto-load all?
+		
+		List<LanguageResult> result = new ArrayList<>();
+		
+		List<DetectedLanguage> rawResults = detector.getProbabilities(writer.toString());
+		for (DetectedLanguage rawResult : rawResults) {
+			// TODO figure out right level for confidence brackets.
+			LanguageConfidence confidence = rawResult.getProbability() > 0.9 ? LanguageConfidence.HIGH : LanguageConfidence.MEDIUM;
+			result.add(new LanguageResult(makeLanguageName(rawResult.getLocale()), confidence, (float)rawResult.getProbability()));
+		}
+
+		return result;
+	}
+
+	@Override
+	public boolean hasEnoughText() {
+		return writer.size() >= getTextLimit();
+	}
+
+	private int getTextLimit() {
+		int limit = (shortText ? MAX_CHARS_FOR_SHORT_DETECTION : MAX_CHARS_FOR_DETECTION);
+		
+		// We want more text if we're processing documents that have a mixture of languages.
+		// FUTURE - figure out right amount to bump up the limit.
+		if (mixedLanguages) {
+			limit *= 2;
+		}
+		
+		return limit;
+	}
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/test/java/org/apache/tika/langdetect/LanguageDetectorTest.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/test/java/org/apache/tika/langdetect/LanguageDetectorTest.java b/tika-langdetect/src/test/java/org/apache/tika/langdetect/LanguageDetectorTest.java
new file mode 100644
index 0000000..a79e83f
--- /dev/null
+++ b/tika-langdetect/src/test/java/org/apache/tika/langdetect/LanguageDetectorTest.java
@@ -0,0 +1,76 @@
+package org.apache.tika.langdetect;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.tika.io.IOUtils;
+
+public abstract class LanguageDetectorTest {
+
+    protected String[] getTestLanguages() throws IOException {
+    	List<String> result = new ArrayList<>();
+    	
+    	List<String> lines = IOUtils.readLines(LanguageDetector.class.getResourceAsStream("language-codes.txt"));
+    	for (String line : lines) {
+    		line = line.trim();
+    		if (line.isEmpty() || line.startsWith("#")) {
+    			continue;
+    		}
+    		
+    		String[] parsed = line.split("\t");
+    		String language = parsed[0];
+    		if (hasTestLanguage(language)) {
+    			result.add(language);
+    		}
+    	}
+    	
+    	return result.toArray(new String[result.size()]);
+    }
+    
+
+    protected boolean hasTestLanguage(String language) {
+        InputStream stream = LanguageDetectorTest.class.getResourceAsStream("/language-tests/" + language + ".test");
+        if (stream != null) {
+        	IOUtils.closeQuietly(stream);
+        	return true;
+        } else {
+        	return false;
+        }
+    }
+    
+    protected void writeTo(String language, Writer writer) throws IOException {
+    	writeTo(language, writer, Integer.MAX_VALUE);
+    }
+
+    protected void writeTo(String language, Writer writer, int limit) throws IOException {
+        InputStream stream = LanguageDetectorTest.class.getResourceAsStream("/language-tests/" + language + ".test");
+        
+        try {
+        	copyAtMost(new InputStreamReader(stream, UTF_8), writer, limit);
+        } finally {
+            stream.close();
+        }
+    }
+
+    protected int copyAtMost(Reader input, Writer output, int limit) throws IOException {
+        char[] buffer = new char[4096];
+        int count = 0;
+        int n = 0;
+        
+        while ((-1 != (n = input.read(buffer))) && (count < limit)) {
+        	int bytesToCopy = Math.min(limit - count, n);
+            output.write(buffer, 0, bytesToCopy);
+            count += bytesToCopy;
+        }
+        
+        return count;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/test/java/org/apache/tika/langdetect/LanguageNamesTest.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/test/java/org/apache/tika/langdetect/LanguageNamesTest.java b/tika-langdetect/src/test/java/org/apache/tika/langdetect/LanguageNamesTest.java
new file mode 100644
index 0000000..8ff8fd2
--- /dev/null
+++ b/tika-langdetect/src/test/java/org/apache/tika/langdetect/LanguageNamesTest.java
@@ -0,0 +1,22 @@
+package org.apache.tika.langdetect;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class LanguageNamesTest {
+
+	@Test
+	public void test() {
+		
+		// macro language + language == language
+		String languageA = LanguageNames.normalizeName("zh-yue");
+		String languageB = LanguageNames.normalizeName("yue");
+		assertTrue(LanguageNames.equals(languageA, languageB));
+		
+		// TODO verify that "en-Latn" == "en"
+		
+		// TODO verify that "en-GB" == "en"???
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/test/java/org/apache/tika/langdetect/OptimaizeLangDetectorTest.java
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/test/java/org/apache/tika/langdetect/OptimaizeLangDetectorTest.java b/tika-langdetect/src/test/java/org/apache/tika/langdetect/OptimaizeLangDetectorTest.java
new file mode 100644
index 0000000..a95c6a1
--- /dev/null
+++ b/tika-langdetect/src/test/java/org/apache/tika/langdetect/OptimaizeLangDetectorTest.java
@@ -0,0 +1,246 @@
+package org.apache.tika.langdetect;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.tika.io.IOUtils;
+import org.junit.Test;
+
+public class OptimaizeLangDetectorTest extends LanguageDetectorTest {
+
+	/*
+	 * The complete list of supported languages (as of 0.5) is below.
+	 * The ones we have tests for have '*' after the name.
+	 * 
+    af Afrikaans
+    an Aragonese
+    ar Arabic
+    ast Asturian
+    be Belarusian
+    br Breton
+    ca Catalan
+    bg Bulgarian
+    bn Bengali
+    cs Czech
+    cy Welsh
+    da Danish *
+    de German *
+    el Greek *
+    en English *
+    es Spanish *
+    et Estonian
+    eu Basque
+    fa Persian
+    fi Finnish *
+    fr French *
+    ga Irish
+    gl Galician
+    gu Gujarati
+    he Hebrew
+    hi Hindi
+    hr Croatian
+    ht Haitian
+    hu Hungarian
+    id Indonesian
+    is Icelandic
+    it Italian *
+    ja Japanese *
+    km Khmer
+    kn Kannada
+    ko Korean
+    lt Lithuanian *
+    lv Latvian
+    mk Macedonian
+    ml Malayalam
+    mr Marathi
+    ms Malay
+    mt Maltese
+    ne Nepali
+    nl Dutch *
+    no Norwegian
+    oc Occitan
+    pa Punjabi
+    pl Polish
+    pt Portuguese *
+    ro Romanian
+    ru Russian
+    sk Slovak
+    sl Slovene
+    so Somali
+    sq Albanian
+    sr Serbian
+    sv Swedish *
+    sw Swahili
+    ta Tamil
+    te Telugu
+    th Thai *
+    tl Tagalog
+    tr Turkish
+    uk Ukrainian
+    ur Urdu
+    vi Vietnamese
+    yi Yiddish
+    zh-CN Simplified Chinese * (just generic Chinese)
+    zh-TW Traditional Chinese * (just generic Chinese)
+	*/
+	
+	/**
+	 * Test correct detection for the many (short) translations of the
+	 * "Universal Declaration of Human Rights (Article 1)", at
+	 * http://www.omniglot.com/udhr
+	 * 
+	 * Also make sure we get uncertain results for some set of unsupported
+	 * languages.
+	 * 
+	 * @throws Exception
+	 */
+	@Test
+	public void testUniversalDeclarationOfHumanRights() throws Exception {
+    	LanguageDetector detector = new OptimaizeLangDetector();
+    	detector.loadModels();
+
+		LanguageWriter writer = new LanguageWriter(detector);
+
+		Map<String, String> knownText = getTestLanguages("udhr-known.txt");
+        for (String language : knownText.keySet()) {
+			writer.reset();
+			writer.append(knownText.get(language));
+			
+    		LanguageResult result = detector.detect();
+    		assertNotNull(result);
+
+            assertEquals(language, result.getLanguage());
+            // System.out.println(String.format("'%s': %s (%f)", language, result.getConfidence(), result.getRawScore()));
+        }
+        
+		Map<String, String> unknownText = getTestLanguages("udhr-unknown.txt");
+        for (String language : unknownText.keySet()) {
+			writer.reset();
+			writer.append(unknownText.get(language));
+			
+    		LanguageResult result = detector.detect();
+    		if (result != null) {
+    			assertFalse(result.isReasonablyCertain());
+                // System.out.println(String.format("Looking for '%s', got '%s': %s (%f)", language, result.getLanguage(), result.getConfidence(), result.getRawScore()));
+    		}
+        }
+        
+        writer.close();
+	}
+	
+    @Test
+    public void testAllLanguages() throws IOException {
+    	LanguageDetector detector = new OptimaizeLangDetector();
+    	detector.loadModels();
+
+		LanguageWriter writer = new LanguageWriter(detector);
+
+		for (String language : getTestLanguages()) {
+			writer.reset();
+			
+    		writeTo(language, writer);
+
+    		LanguageResult result = detector.detect();
+    		assertNotNull(result);
+
+    		assertTrue(result.isLanguage(language));
+    		assertTrue(result.isReasonablyCertain());
+    	}
+    }
+    
+    @Test
+    public void testMixedLanguages() throws IOException {
+    	LanguageDetector detector = new OptimaizeLangDetector()
+    		.setMixedLanguages(true);
+    	
+    	detector.loadModels();
+		LanguageWriter writer = new LanguageWriter(detector);
+    	
+    	String[] languages = getTestLanguages();
+        for (int i = 0; i < languages.length; i++) {
+        	String language = languages[i];
+        	for (int j = i + 1; j < languages.length; j++) {
+        		String other = languages[j];
+        		
+        		writer.reset();
+        		writeTo(language, writer);
+        		writeTo(other, writer);
+        		
+        		List<LanguageResult> results = detector.detectAll();
+        		if (results.size() > 0) {
+        			LanguageResult result = results.get(0);
+
+        			assertFalse("mix of " + language + " and " + other + " incorrectly detected as " + result, result.isReasonablyCertain());
+        		}
+        	}
+        }
+        
+        writer.close();
+    }
+    
+    @Test
+    public void testShortText() throws IOException {
+    	LanguageDetector detector = new OptimaizeLangDetector()
+    		.setShortText(true)
+    		.loadModels();
+
+    	// First verify that we get no result with empty or very short text.
+		LanguageWriter writer = new LanguageWriter(detector);
+		writer.append("");
+		assertNull(detector.detect());
+		
+		writer.reset();
+		writer.append("  ");
+		assertNull(detector.detect());
+
+    	for (String language : getTestLanguages()) {
+    		// Short pieces of Japanese are detected as Chinese
+    		if (language.equals("ja")) {
+    			continue;
+    		}
+    		
+    		// We need at least 300 characters to detect Chinese reliably.
+    		writer.reset();
+    		writeTo(language, writer, 300);
+
+    		LanguageResult result = detector.detect();
+    		assertNotNull(String.format(Locale.US, "Language '%s' wasn't detected", language), result);
+
+    		assertTrue(String.format(Locale.US, "Language '%s' was detected as '%s'", language, result.getLanguage()), result.isLanguage(language));
+    		assertTrue(String.format(Locale.US, "Language '%s' isn't reasonably certain: %s", language, result.getConfidence()), result.isReasonablyCertain());
+    	}
+    	
+    	writer.close();
+    }
+    
+	private Map<String, String> getTestLanguages(String resourceName) throws IOException {
+		Map<String, String> result = new HashMap<>();
+		List<String> languages = IOUtils.readLines(OptimaizeLangDetectorTest.class.getResourceAsStream(resourceName));
+        for (String line : languages) {
+        	line = line.trim();
+        	if (line.isEmpty() || line.startsWith("#")) {
+        		continue;
+        	}
+
+        	String[] pieces = line.split("\t", 2);
+        	if (pieces.length != 2) {
+        		throw new IllegalArgumentException("Invalid language data line: " + line);
+        	}
+        	
+        	result.put(pieces[0], pieces[1]);
+        }
+        
+        return result;
+	}
+	
+
+}

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/test/resources/log4j.properties b/tika-langdetect/src/test/resources/log4j.properties
new file mode 100644
index 0000000..f2c0b92
--- /dev/null
+++ b/tika-langdetect/src/test/resources/log4j.properties
@@ -0,0 +1,24 @@
+# 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.
+
+#info,debug, error,fatal ...
+log4j.rootLogger=info,stdout
+
+#console
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+
+# Pattern to output the caller's file name and line number.
+log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/test/resources/org/apache/tika/langdetect/language-codes.txt
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/test/resources/org/apache/tika/langdetect/language-codes.txt b/tika-langdetect/src/test/resources/org/apache/tika/langdetect/language-codes.txt
new file mode 100644
index 0000000..e060691
--- /dev/null
+++ b/tika-langdetect/src/test/resources/org/apache/tika/langdetect/language-codes.txt
@@ -0,0 +1,186 @@
+# From https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+
+aa	Afar 
+ab	Abkhaz 
+ae	Avestan 
+af	Afrikaans 
+ak	Akan 
+am	Amharic 
+an	Aragonese 
+ar	Arabic 
+as	Assamese 
+av	Avaric 
+ay	Aymara 
+az	Azerbaijani 
+ba	Bashkir 
+be	Belarusian 
+bg	Bulgarian 
+bh	Bihari 
+bi	Bislama 
+bm	Bambara 
+bn	Bengali, Bangla 
+bo	Tibetan Standard, Tibetan, Central 
+br	Breton 
+bs	Bosnian 
+ca	Catalan 
+ce	Chechen 
+ch	Chamorro 
+co	Corsican 
+cr	Cree 
+cs	Czech 
+cu	Old Church Slavonic, Church Slavonic, Old Bulgarian 
+cv	Chuvash 
+cy	Welsh 
+da	Danish 
+de	German 
+dv	Divehi, Dhivehi, Maldivian 
+dz	Dzongkha 
+ee	Ewe 
+el	Greek (modern) 
+en	English 
+eo	Esperanto 
+es	Spanish 
+et	Estonian 
+eu	Basque 
+fa	Persian (Farsi) 
+ff	Fula, Fulah, Pulaar, Pular 
+fi	Finnish 
+fj	Fijian 
+fo	Faroese 
+fr	French 
+fy	Western Frisian 
+ga	Irish 
+gd	Scottish Gaelic, Gaelic 
+gl	Galician 
+gn	Guaraní 
+gu	Gujarati 
+gv	Manx 
+ha	Hausa 
+he	Hebrew (modern) 
+hi	Hindi 
+ho	Hiri Motu 
+hr	Croatian 
+ht	Haitian, Haitian Creole 
+hu	Hungarian 
+hy	Armenian 
+hz	Herero 
+ia	Interlingua 
+id	Indonesian 
+ie	Interlingue 
+ig	Igbo 
+ii	Nuosu 
+ik	Inupiaq 
+io	Ido 
+is	Icelandic 
+it	Italian 
+iu	Inuktitut 
+ja	Japanese 
+jv	Javanese 
+ka	Georgian 
+kg	Kongo 
+ki	Kikuyu, Gikuyu 
+kj	Kwanyama, Kuanyama 
+kk	Kazakh 
+kl	Kalaallisut, Greenlandic 
+km	Khmer 
+kn	Kannada 
+ko	Korean 
+kr	Kanuri 
+ks	Kashmiri 
+ku	Kurdish 
+kv	Komi 
+kw	Cornish 
+ky	Kyrgyz 
+la	Latin 
+lb	Luxembourgish, Letzeburgesch 
+lg	Ganda 
+li	Limburgish, Limburgan, Limburger 
+ln	Lingala 
+lo	Lao 
+lt	Lithuanian 
+lu	Luba-Katanga 
+lv	Latvian 
+mg	Malagasy 
+mh	Marshallese 
+mi	Māori 
+mk	Macedonian 
+ml	Malayalam 
+mn	Mongolian 
+mr	Marathi (Marāṭhī) 
+ms	Malay 
+mt	Maltese 
+my	Burmese 
+na	Nauru 
+nb	Norwegian Bokmål 
+nd	Northern Ndebele 
+ne	Nepali 
+ng	Ndonga 
+nl	Dutch 
+nn	Norwegian Nynorsk 
+no	Norwegian 
+nr	Southern Ndebele 
+nv	Navajo, Navaho 
+ny	Chichewa, Chewa, Nyanja 
+oc	Occitan 
+oj	Ojibwe, Ojibwa 
+om	Oromo 
+or	Oriya 
+os	Ossetian, Ossetic 
+pa	Panjabi, Punjabi 
+pi	Pāli 
+pl	Polish 
+ps	Pashto, Pushto 
+pt	Portuguese 
+qu	Quechua 
+rm	Romansh 
+rn	Kirundi 
+ro	Romanian 
+ru	Russian 
+rw	Kinyarwanda 
+sa	Sanskrit (Saṁskṛta) 
+sc	Sardinian 
+sd	Sindhi 
+se	Northern Sami 
+sg	Sango 
+si	Sinhala, Sinhalese 
+sk	Slovak 
+sl	Slovene 
+sm	Samoan 
+sn	Shona 
+so	Somali 
+sq	Albanian 
+sr	Serbian 
+ss	Swati 
+st	Southern Sotho 
+su	Sundanese 
+sv	Swedish 
+sw	Swahili 
+ta	Tamil 
+te	Telugu 
+tg	Tajik 
+th	Thai 
+ti	Tigrinya 
+tk	Turkmen 
+tl	Tagalog 
+tn	Tswana 
+to	Tonga (Tonga Islands) 
+tr	Turkish 
+ts	Tsonga 
+tt	Tatar 
+tw	Twi 
+ty	Tahitian 
+ug	Uyghur 
+uk	Ukrainian 
+ur	Urdu 
+uz	Uzbek 
+ve	Venda 
+vi	Vietnamese 
+vo	Volapük 
+wa	Walloon 
+wo	Wolof 
+xh	Xhosa 
+yi	Yiddish 
+yo	Yoruba 
+za	Zhuang, Chuang 
+zh	Chinese 
+zu	Zulu 

http://git-wip-us.apache.org/repos/asf/tika/blob/d621ce8d/tika-langdetect/src/test/resources/org/apache/tika/langdetect/language-tests/da.test
----------------------------------------------------------------------
diff --git a/tika-langdetect/src/test/resources/org/apache/tika/langdetect/language-tests/da.test b/tika-langdetect/src/test/resources/org/apache/tika/langdetect/language-tests/da.test
new file mode 100644
index 0000000..1238cd5
--- /dev/null
+++ b/tika-langdetect/src/test/resources/org/apache/tika/langdetect/language-tests/da.test
@@ -0,0 +1,108 @@
+Genoptagelse af sessionen
+Jeg erklærer Europa-Parlamentets session, der blev afbrudt fredag den 17. december, for genoptaget. Endnu en gang vil jeg ønske Dem godt nytår, og jeg håber, De har haft en god ferie.
+Som De kan se, indfandt det store "år 2000-problem" sig ikke. Til gengæld har borgerne i en del af medlemslandene været ramt af meget forfærdelige naturkatastrofer. De har udtrykt ønske om en debat om dette emne i løbet af mødeperioden. I mellemtiden ønsker jeg - som også en del kolleger har anmodet om - at vi iagttager et minuts stilhed til minde om ofrene for bl.a. stormene i de medlemslande, der blev ramt. Jeg opfordrer Dem til stående at iagttage et minuts stilhed.
+(Parlamentet iagttog stående et minuts stilhed
+
+Fru formand, en bemærkning til forretningsordenen. Gennem pressen og tv vil De være bekendt med en række bombeeksplosioner og drab i Sri Lanka. En af de personer, der blev myrdet for ganske nylig i Sri Lanka, var hr. Kumar Ponnambalam, der besøgte Europa-Parlamentet for få måneder siden. Ville det være passende, hvis De, fru formand, sendte en skrivelse til Sri Lankas præsident for at udtrykke vores dybe beklagelse i forbindelse med Kumar Ponnambalams død og de andre voldsomme dødsfald i Sri Lanka og for indtrængende at anmode præsidenten om at gøre alt for at opnå en fredelig løsning på en meget vanskelig situation?
+
+Ja, hr. Evans, jeg mener, at et initiativ, som det, De foreslår, ville være meget hensigtsmæssigt. Hvis Europa-Parlamentet er enigt, vil jeg gøre, som hr. Evans har foreslået.
+
+Fru formand, en bemærkning til forretningsordenen. Jeg vil gerne have Deres råd om artikel 143 vedrørende afvisning. Mit spørgsmål omhandler et emne, der vil blive behandlet på torsdag, og jeg vil gerne tage emnet op igen ved den lejlighed.
+Betænkningen af Cunha om flerårige udviklingsprogrammer skal forhandles af Parlamentet på torsdag og indeholder et forslag i punkt 6 om, at der skal indføres kvotesanktioner for lande, der ikke overholder deres årlige målsætninger for flådereduktion. Dette skal i henhold til punkt 6 indføres til trods for princippet om relativ stabilitet. Jeg mener, at princippet om relativ stabilitet er et grundlæggende retsprincip for den fælles fiskeripolitik, og at der vil være juridisk belæg for at afvise et forslag om at undergrave dette princip. Jeg vil gerne vide, om man kan gøre indsigelse mod noget, der bare er en betænkning og ikke et forslag til retsakt, og om det er noget, jeg kan gøre på torsdag?
+
+Det er netop dér, De - hvis De ønsker det - kan rejse dette spørgsmål, det vil sige på torsdag ved forhandlingens begyndelse.
+
+Fru formand, samtidig med Europa-Parlamentets første mødeperiode i år har man i Texas i USA fastsat datoen for henrettelsen af en dødsdømt, nemlig en ung mand på 34 år ved navn Hicks, og det er desværre på næste torsdag.
+På anmodning af et fransk parlamentsmedlem, hr. Zimeray, er der allerede indgivet et andragende, som mange har skrevet under på, heriblandt undertegnede, men i tråd med den holdning, som Europa-Parlamentet og hele Det Europæiske Fællesskab konstant giver udtryk for, anmoder jeg Dem om at gøre den indflydelse, De har i kraft af Deres embede og den institution, De repræsenterer, gældende over for præsidenten og Texas' guvernør Bush, som har beføjelse til at ophæve dødsdommen og benåde den dømte.
+Alt dette er i tråd med de principper, vi altid har været tilhængere af.
+
+Tak, hr. Segni, det gør jeg med glæde. Det er således helt i tråd med den holdning, Europa-Parlamentet altid har indtaget.
+
+Fru formand, jeg vil gerne gøre Dem opmærksom på en sag, som Parlamentet har beskæftiget sig med gentagne gange. Det drejer sig om Alexander Nikitin. Vi glæder os alle sammen over, at domstolen har frifundet ham og understreget, at adgangen til miljøinformationer også er konstitutionel ret i Rusland. Men nu er det sådan, at han skal anklages igen, fordi statsadvokaten har anket dommen. Vi ved og har fastslået i virkelig mange beslutninger - netop på det sidste møde sidste år - at dette ikke bare er en juridisk sag, og at det er forkert at beskylde Alexander Nikitin for at have begået kriminalitet og forræderi, fordi vi som berørte nyder godt af hans resultater. Disse resultater er grundlaget for de europæiske programmer til beskyttelse af Barentsee, og derfor beder jeg Dem gennemgå et brevudkast, som beskriver de vigtigste fakta, og tydeliggøre denne holdning i Rusland i overensstemmelse med Parlamentets beslutninger.
+
+Ja, fru Schroedter, jeg skal med glæde undersøge dette spørgsmål, når jeg har modtaget Deres brev.
+
+Fru formand, jeg vil gerne først give Dem en kompliment for den kendsgerning, at De har holdt Deres ord, og at antallet af tv-kanaler på vores kontorer faktisk er udvidet enormt nu i denne første mødeperiode i det nye år. Men, fru formand, det, som jeg havde anmodet om, er ikke sket. Der er nu ganske vist to finske kanaler og en portugisisk kanal, men der er stadig ingen nederlandsk kanal, og jeg havde anmodet Dem om en nederlandsk kanal, fordi også nederlændere gerne vil følge med i nyhederne hver måned, når vi forvises til dette sted. Jeg vil således endnu en gang anmode Dem om alligevel at sørge for, at vi også får en nederlandsk kanal.
+
+Fru Plooij-van Gorsel, jeg kan oplyse Dem om, at dette spørgsmål er opført på dagsordenen for kvæstorernes møde på onsdag. Det vil, håber jeg, blive behandlet i en positiv ånd.
+
+Fru formand, kan De fortælle mig, hvorfor Parlamentet ikke overholder de lovgivningsbestemmelser om sundhed og sikkerhed, som det selv har fastsat? Hvorfor er der ikke foretaget en undersøgelse af luftkvaliteten i denne bygning, siden vi blev valgt? Hvorfor har Sundheds- og Sikkerhedsudvalget ikke haft et møde siden 1998? Hvorfor har der ikke været brandøvelser, hverken i parlamentsbygningerne i Bruxelles eller Strasbourg? Hvorfor er der ingen brandinstrukser? Hvorfor etableres der ikke områder med rygeforbud? Det er fuldstændig skandaløst, at vi fastsætter lovgivningsbestemmelser og så ikke overholder dem selv.
+
+Fru Lynne, De har fuldstændig ret, og jeg vil kontrollere, om alle disse ting virkelig ikke er blevet gjort. Jeg vil ligeledes fremlægge problemet for kvæstorerne, og jeg er sikker på, at kvæstorerne vil bestræbe sig på at sørge for, at vi overholder den lovgivning, vi vedtager.
+
+Fru formand, fru Díez González og jeg havde stillet nogle spørgsmål om visse holdninger gengivet i en spansk avis, som næstformanden, fru de Palacio, har givet udtryk for. De kompetente tjenestegrene har ikke opført dem på dagsordenen, fordi de mener, at de blev besvaret ved et tidligere møde.
+Jeg anmoder om, at denne beslutning tages op til fornyet overvejelse, for det er ikke tilfældet. De spørgsmål, der tidligere er blevet besvaret, drejede sig om fru de Palacios medvirken i en bestemt sag og ikke om de erklæringer, som kunne læses i avisen ABC den 18. november sidste år.
+
+Kære kolleger, vi vil undersøge alt dette. Jeg må indrømme, at det hele forekommer mig lidt forvirrende i øjeblikket. Derfor vil vi undersøge det meget omhyggeligt, således at alt er, som det skal være.
+
+Fru formand, jeg vil gerne vide, om der kommer en klar melding fra Parlamentet i denne uge om vores utilfredshed i forbindelse med dagens beslutning om ikke at forlænge embargoen mod våbeneksport til Indonesien i betragtning af, at et stort flertal i Parlamentet tidligere har undertegnet våbenembargoen i Indonesien. Dagens beslutning om ikke at forlænge embargoen er meget farlig på grund af situationen der. Parlamentet bør derfor tilkendegive sin holdning, da det er flertallets ønske. Det er uansvarligt af EU-medlemsstater at nægte at forlænge embargoen. Som nævnt tidligere er der tale om en meget ustabil situation. Der er endog fare for et militærkup i fremtiden. Vi ved ikke, hvad der sker. Så hvorfor skal våbenproducenter i EU profitere på bekostning af uskyldige mennesker?
+
+Under alle omstændigheder er punktet ikke på nuværende tidspunkt opført under forhandlingen om aktuelle og uopsættelige spørgsmål på torsdag.
+
+Arbejdsplan
+Næste punkt på dagsordenen er fastsættelse af arbejdsplanen.
+Det endelige forslag til dagsorden, som det blev opstillet af Formandskonferencen på mødet torsdag den 13. januar i overensstemmelse med forretningsordenens artikel 95, er omdelt.
+Det foreligger ingen forslag til ændring for mandag og tirsdag.
+Onsdag:
+PSE-gruppen anmoder om at få en redegørelse fra Kommissionen om dens strategiske mål for de kommende fem år samt om den administrative reform opført på dagsordenen.
+Hvis hr. Barón Crespo, der har fremsat anmodningen, ønsker det, opfordrer jeg ham til at begrunde sit forslag. Dernæst gør vi, som vi plejer, det vil sige, at vi hører et indlæg for og et indlæg imod forslaget.
+
+Fru formand, forelæggelsen af Prodi-Kommissionens politiske program for hele valgperioden var til at begynde med et forslag fra De Europæiske Socialdemokraters Gruppe, som opnåede enstemmighed på Formandskonferencen i september og også hr. Prodis udtrykkelige accept, og han gentog sit løfte i sin indsættelsestale.
+Dette løfte er vigtigt, fordi Kommissionen er et organ, der har initiativmonopol i henhold til traktaterne og derfor grundlæggende udformer Parlamentets politiske arbejde og lovgivningsarbejde i de kommende fem år. Jeg vil også minde om, fru formand, at Parlamentet to gange i foregående valgperiode ved afstemning gav udtryk for sin tillid til formand Prodi. I denne valgperiode igen i juli og senere, med den nye Kommission på plads, gav det igen i september hele Kommissionen et tillidsvotum. Der har derfor været tid nok til, at Kommissionen kunne forberede sit program, og til at vi kunne få kendskab til det og forklare det til borgerne. I den forbindelse vil jeg minde om beslutningen fra 15. september, hvori der blev henstillet til, at forslaget blev forelagt hurtigst muligt.
+Det, der skete i sidste uge - og som opstod uden for Formandskonferencen, hvor den udelukkende blev brugt til at bekræfte og godkende beslutninger, som var truffet uden for den - skaber et dilemma: Enten er Kommissionen ikke i stand til at fremlægge det program. (I så fald ville det være passende, at den informerede om det. Ifølge kommissionsformandens udsagn er de i stand til at gøre det. Eftersom Kommissionen er repræsenteret af næstformanden, fru de Palacio, mener jeg, at det før afstemningen ville være på sin plads at være på det rene med Kommissionens situation, hvad angår dets vilje til at forelægge programmet, ligesom det var blevet aftalt.) Eller også er Parlamentet ikke i stand til at behandle dette program, som der vist er nogle, der påstår. Efter min mening ville denne anden hypotese være det samme som at give afkald på vores ansvar som parlament og desuden at indføre en original teori, en ukendt metode, der består i skriftligt at give de politiske g
 rupper kendskab til Kommissionens program en uge før - og ikke dagen før, som det var aftalen - i betragtning af, at lovgivningsprogrammet skal diskuteres i februar, således at vi kunne springe forhandlingen over, fordi pressen og Internettet dagen efter havde givet alle borgerne kendskab til det, og Parlamentet ville ikke længere behøve at bekymre sig om sagen.
+Da min gruppe mener, at et parlament er til for at lytte, diskutere og overveje, mener vi, at der ikke er noget som helst, der kan retfærdiggøre denne udsættelse, og vi mener, at hvis Kommissionen er i stand til at gøre det, er der tid nok til, at vi kan genetablere den oprindelige aftale mellem Parlamentet og Kommissionen og handle ansvarligt over for vores medborgere. Derfor går det forslag, som De Europæiske Socialdemokraters Gruppe stiller, og som De har nævnt, ud på, at vi holder fast ved forelæggelsen af Prodi-Kommissionens program for valgperioden på onsdag, og at dette program også omfatter forslaget til administrativ reform, for hvis det ikke bliver sådan, kan vi komme i en paradoksal situation: Med en undskyldning om at der ikke er en tekst, nægtes formanden for Kommissionen på den ene side retten til at tale i Parlamentet, og på den anden side forhindres det, at der finder en forhandling sted om reformen, uden at Parlamentet på forhånd kender de tekster, 
 som den er baseret på. Derfor, fru formand, anmoder jeg Dem om at bede Kommissionen om at udtale sig nu, og at vi derefter går over til afstemning.
+(Bifald fra PSE-gruppen)
+
+Fru formand, kære kolleger, jeg er godt nok noget forbavset over vores kollega Barón Crespos opførsel. Han forlanger nu, at dette punkt sættes på dagsordenen for onsdag.
+Hr. Barón Crespo, De kunne ikke deltage den sidste torsdag på Formandskonferencen. Det kritiserer jeg ikke, for det sker af og til, at man lader sig repræsentere. Hr. Hänsch repræsenterede Dem dér. Vi havde en udførlig debat på Formandskonferencen. Kun Deres gruppe repræsenterede det, som De siger nu. Vi stemte derefter om det. Hver ordfører har jo lige så mange stemmer, som der er medlemmer i gruppen. Der var en afstemning om dette punkt. Så vidt jeg husker, faldt denne afstemning således ud: 422 mod 180 stemmer og nogle få, der undlod at stemme. Det vil sige, at alle grupper med undtagelse af løsgængerne - men de udgør jo ikke nogen gruppe - var enige, kun Deres gruppe mente, at man skulle bære sig sådan ad, som De har foreslået her. Alle andre mente noget andet. Det var beslutningen.
+Nu vil jeg gerne sige noget til selve sagen. Vi har tillid til Kommissionen, til Romano Prodi, og flertallet i vores gruppe har udtrykt tillid til Romano Prodi og Kommissionen efter en vanskelig proces, som alle kender til. Men vi mener også, at vi skal have en debat om Kommissionens strategi i en ordinær procedure, ikke kun på baggrund af en mundtlig forklaring her i Europa-Parlamentet, men også på baggrund af et dokument, som er blevet besluttet i Kommissionen, og som beskriver dette program for fem år. Et sådant dokument findes ikke!
+
+Kommissionen vil fremlægge programmet for år 2000 til februar. Vi har sagt, at hvis Kommissionen ikke ønsker at lave programmet for år 2000 i januar, så gør vi det i februar. Det har vi godkendt. Vi ønsker sådan set ikke nogen konflikt med Kommissionen, vi mener derimod, at hvis det går, skal Kommissionen og Parlamentet gå samme vej. Men Parlamentet er også Kommissionens kontrollør. Og ikke alt, hvad der kommer fra Kommissionen, skal nødvendigvis være i overensstemmelse med os.
+Jeg vil gerne have, at vi får mulighed for at forberede os fornuftigt på en debat om femårsprogrammet i grupperne. Man kan ikke forberede sig, hvis man hører en forklaring her og slet ikke ved, hvad indholdet af en sådan forklaring er. Derfor anbefaler vi - og det er mit indtryk, at Kommissionen også er åben over for denne tanke - at vi fører debatten om Kommissionens langsigtede program frem til år 2005 i februar - jeg håber også, at Kommissionen er blevet enig om et program til den tid, som den vil foreslå os - og at vi samtidig fører en debat om Kommissionens lovgivningsprogram for år 2000 i februar. Det er således også en fornuftig saglig sammenhæng, som råder os til at føre debatten om begge programmer i fællesskab. Derfor afviser min gruppe på det bestemteste Den Socialdemokratiske Gruppes forslag!
+(Bifald fra PPE-DE-gruppen)
+
+Fru formand, jeg vil gøre det meget klart, at Kommissionen først og fremmest har den største respekt for Parlamentets beslutninger, deriblandt opstillingen af dagsordenen. Derfor respekterer vi Parlamentets beslutning, hvad det angår.
+Men jeg vil også gøre det meget klart, at hr. Prodi aftalte med Parlamentet at indføre en ny forhandling, som hr. Barón nok husker, ud over den årlige forhandling om Kommissionens lovgivningsprogram, om hovedlinjerne i aktionerne for den kommende femårsperiode, altså for denne valgperiode.
+Jeg vil sige, fru formand, at denne forhandling i den aftale, som blev indgået i september, adskilte sig fra Kommissionens årlige forelæggelse af programmet for lovgivningen. Og jeg vil sige, fru formand, at vi i Kommissionen er forberedt på og rede til at deltage i den forhandling, når det er belejligt, at vi var rede til at gennemføre den i denne uge, som det var aftalt fra begyndelsen, med udgangspunkt i at den blev forelagt dagen før i en tale til de parlamentariske grupper.
+Jeg vil derfor gentage, fru formand, at vi for vores del har diskuteret handlingsprogrammet for de kommende fem år, og at vi er rede til, når Parlamentet bestemmer det - i denne uge, hvis det er beslutningen - at komme og forelægge programmet for de kommende fem år og i næste måned programmet for 2000, hvilket er helt i overensstemmelse med aftalen.
+
+Jeg foreslår, at vi stemmer om PSE-gruppens anmodning om at få en redegørelse fra Kommissionen om dens strategiske mål genopført på dagsordenen.
+(Forslaget forkastedes) Formanden. Stadig med hensyn til dagsordenen for onsdag har jeg et forslag om de mundtlige forespørgsler om kapitalskat. PPE-DE-gruppen ønsker, at dette punkt tages af dagsordenen.
+Ønsker nogen at tage ordet på vegne af gruppen for at begrunde denne anmodning?
+
+Fru formand, da jeg kan høre en smule latter fra Socialdemokraterne - jeg har fået fortalt, at brede kredse i Den Socialdemokratiske Gruppe også gerne vil have taget dette punkt af dagsordenen, fordi der ved afstemningen på Formandskonferencen ikke forelå noget votum fra arbejdsgruppen af ansvarlige kolleger i Den Socialdemokratiske Gruppe. Jeg ved ikke, om denne oplysning er rigtig, men PPE-DE-gruppen ville i hvert fald være taknemmelig, hvis dette punkt blev annulleret, fordi Parlamentet allerede har beskæftiget sig med dette spørgsmål flere gange. Der er også truffet beslutninger mod en sådan skat. Derfor anmoder min gruppe om, at dette punkt tages af dagsordenen.
+
+Tak, hr. Poettering.
+Vi skal nu høre hr. Wurtz, der er imod forslaget.
+
+Fru formand, jeg vil først og fremmest fremhæve hr. Poetterings manglende konsekvens. For et øjeblik siden belærte han socialdemokraterne, fordi de ville ændre en klar beslutning truffet på Formandskonferencen. Imidlertid gør han det samme. Vi havde en diskussion, vi var alle - på nær PPE-DE-gruppen og Den Liberale Gruppe - enige, og jeg bemærkede endda - som De sikkert husker, kære medformænd - at det ikke drejede sig om, hvorvidt De er for eller imod Tobin-afgiften, men om De turde høre, hvad Kommissionen og Rådet mente om den. Dette er ikke for meget forlangt. Derfor fastholder jeg forslaget om at bevare det mundtlige spørgsmål til Kommissionen og Rådet, således at vi én gang for alle får opklaret, hvilken holdning de to institutioner har til dette ret beskedne forslag, som dog sender et vigtigt signal til befolkningen, navnlig efter fiaskoen i Seattle.
+
+Vi skal stemme om PPE-DE-gruppens anmodning om, at de mundtlige forespørgsler om kapitalskat tages af dagsordenen.
+(Forslaget forkastedes. 164 stemte for, 166 stemte imod, og 7 undlod at stemme)
+
+Fru formand, jeg vil gerne takke hr. Poettering for den reklame, han netop har gjort for denne debat. Tak.
+
+Fru formand, er min stemme, som jeg ikke kunne afgive elektronisk, fordi jeg ikke har kortet, blevet talt med? Jeg stemte for.
+
+Det er rigtigt. Hvis vi tilføjer de to kolleger, der har givet sig til kende, bliver resultatet ...
+
+Fru formand, formandskabet har bekendtgjort afstemningens udfald. Det kan der ikke laves om på.
+
+Kære kolleger, jeg minder endnu en gang om, at det er vigtigt, at alle har deres kort om mandagen. Det er tydeligt, at vi har et problem, og jeg må derfor træffe en beslutning.
+Jeg har også glemt mit kort, og jeg ville have stemt imod. Derfor mener jeg, at det mundtlige spørgsmål fortsat skal medtages på dagsordenen.
+Det er sidste gang, vi vil tage hensyn til glemte kort. Lad dette være helt klart, og husk det.
+(Bifald)
+Ja, det mundtlige spørgsmål er fortsat opført på dagsordenen, og ja, formanden har ret til at stemme, ligesom hun har ret til at glemme sit kort.
+Vi fortsætter nu med de øvrige ændringer af dagsordenen.
+
+Fru formand, i den tidligere afstemning - og jeg vil rette mig efter Deres afgørelse om dette emne - om spørgsmålet om Kommissionens redegørelse om dens strategiske mål gav jeg udtryk for, at jeg gerne ville tale på vegne af min gruppe før afstemningen. Det blev ikke til noget. Jeg vil sætte pris på at få lejlighed til at afgive stemmeforklaring på vegne af min gruppe i forbindelse med afslutningen af dette spørgsmål. Det er et vigtigt spørgsmål, og det vil være nyttigt for Parlamentet, hvis det er angivet, hvordan de forskellige personer opfatter vores handlinger i lyset af deres egne politiske analyser.
+
+Fru formand, jeg vil ikke genoptage debatten, men jeg havde også meldt mig for at tage stilling til hr. Barón Crespos ændringsforslag. De råbte mig heller ikke op. Det beklager jeg, men afstemningen er gennemført, afgørelsen er truffet, vi lader det altså ligge.
+
+Jeg beklager, hr. Hänsch og hr. Cox, jeg så ikke, at De anmodede om ordet. Men i øvrigt mener jeg, at holdningerne er meget klare, og de vil blive indført i protokollen. Når vi i morgen skal vedtage protokollen for i dag, kan de kolleger, der ikke synes, at holdningerne er blevet tilstrækkeligt forklaret, anmode om ændringer. Det, mener jeg, er en god løsning. Selvfølgelig vil protokollen for mødet i morgen tage hensyn til alle de supplerende forklaringer. Jeg mener, at det er en bedre løsning end at gå over til stemmeforklaringer på nuværende tidspunkt, som ville være et stort sidespring. Hr. Cox og hr. Hänsch, passer denne løsning Dem?
+
+Fru formand, hvis protokollen giver korrekt udtryk for min gruppes holdning i forbindelse med afstemningen, vil og kan jeg ikke gøre indsigelser. Hvis De afgør, at der ikke er grund til at afgive stemmeforklaring, vil jeg acceptere det, men med forbehold.
+
+Vi vil derfor være meget opmærksomme på udarbejdelsen af protokollen. Det er vi i øvrigt altid. Hvis holdningerne ikke klart fremgår, kan vi eventuelt ændre den.
+(Den således ændrede dagsorden godkendtes)
+