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

krickert pushed a commit to branch OPENNLP-1893-hunspell
in repository https://gitbox.apache.org/repos/asf/opennlp.git

commit 8bee7cb5b640cc53b7ab901db418d35d8b7ef022
Author: Kristian Rickert <[email protected]>
AuthorDate: Thu Jul 16 01:45:20 2026 -0400

    OPENNLP-1893: Document Hunspell dictionary acquisition with a 
license-preserving download helper
---
 .../dev/README-hunspell-dictionaries.md            | 53 +++++++++++++++++
 .../dev/download-hunspell-dictionary.sh            | 66 ++++++++++++++++++++++
 2 files changed, 119 insertions(+)

diff --git a/opennlp-core/opennlp-runtime/dev/README-hunspell-dictionaries.md 
b/opennlp-core/opennlp-runtime/dev/README-hunspell-dictionaries.md
new file mode 100644
index 000000000..98195b029
--- /dev/null
+++ b/opennlp-core/opennlp-runtime/dev/README-hunspell-dictionaries.md
@@ -0,0 +1,53 @@
+<!--
+   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.
+-->
+
+# Hunspell dictionaries for the affix stemmer
+
+The Hunspell stemmer (`opennlp.tools.stemmer.hunspell`) is a clean-room engine 
over the documented Hunspell dictionary format: a `.dic` word list plus its 
`.aff` affix companion, both supplied by the user. Apache OpenNLP bundles no 
dictionary data, so the dictionaries' own licenses never attach to the library; 
whichever dictionary you download, its license is stated in the readme shipped 
alongside it and is yours to comply with.
+
+## Where dictionaries come from
+
+The LibreOffice project maintains a large collection of Hunspell dictionaries, 
one directory per language, at `github.com/LibreOffice/dictionaries`. Licenses 
differ per dictionary, which is exactly why nothing is bundled: for example, 
the `en_US` dictionary derives from SCOWL and states its terms in 
`README_en_US.txt` in the same directory. Many other sources work too; the 
engine only cares that the pair follows the Hunspell format.
+
+The helper next to this file fetches a pair together with its readme files:
+
+```
+./download-hunspell-dictionary.sh en en_US /tmp/hunspell-en_US
+```
+
+## Loading and stemming
+
+```java
+import java.nio.file.Path;
+import opennlp.tools.stemmer.Stemmer;
+import opennlp.tools.stemmer.hunspell.HunspellDictionary;
+import opennlp.tools.stemmer.hunspell.HunspellStemmerFactory;
+
+HunspellDictionary dictionary = HunspellDictionary.load(
+    Path.of("/tmp/hunspell-en_US/en_US.aff"),
+    Path.of("/tmp/hunspell-en_US/en_US.dic"));
+HunspellStemmerFactory factory = new HunspellStemmerFactory(dictionary);
+
+Stemmer stemmer = factory.newStemmer();
+CharSequence stem = stemmer.stem("workers");
+```
+
+The dictionary is immutable and safe to share between threads; the factory 
hands out a fresh stemmer per call, so each thread takes its own from 
`newStemmer()`. A dictionary that declares a non-UTF-8 encoding through the 
`SET` directive in its `.aff` file is decoded accordingly; nothing needs 
converting beforehand.
+
+## What the engine supports
+
+Supported affix features: `PFX` and `SFX` rules with strip strings, 
character-class conditions, cross-product combination of one prefix with one 
suffix, twofold suffixes through continuation classes, `FLAG` modes `char`, 
`long`, and `num`, and the `SET` encoding declaration. Compounding and 
conversion tables are not interpreted; rules that use them simply do not fire, 
so unsupported analyses are missed rather than invented. A malformed `.aff` 
file fails loudly at load time with the offen [...]
diff --git a/opennlp-core/opennlp-runtime/dev/download-hunspell-dictionary.sh 
b/opennlp-core/opennlp-runtime/dev/download-hunspell-dictionary.sh
new file mode 100755
index 000000000..5ed95511b
--- /dev/null
+++ b/opennlp-core/opennlp-runtime/dev/download-hunspell-dictionary.sh
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+# 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.
+
+# Fetches one Hunspell dictionary pair (.aff and .dic) plus its license/readme 
files
+# from the LibreOffice dictionaries collection. Each dictionary carries its own
+# license, stated in the readme files this script downloads alongside it, and 
you
+# accept that license by using the dictionary. Apache OpenNLP bundles no 
dictionary
+# data. See README-hunspell-dictionaries.md in this directory for the Java 
steps that
+# follow.
+
+set -euo pipefail
+
+usage() {
+  echo "usage: $0 <collection-dir> <dictionary-name> <target-dir>" >&2
+  echo "" >&2
+  echo "  collection-dir   the language directory inside the LibreOffice 
dictionaries" >&2
+  echo "                   repository, for example: en" >&2
+  echo "  dictionary-name  the dictionary base name inside it, for example: 
en_US" >&2
+  echo "  target-dir       where the .aff, .dic, and readme files are placed" 
>&2
+  echo "" >&2
+  echo "example: $0 en en_US /tmp/hunspell-en_US" >&2
+  exit 2
+}
+
+[ $# -ne 3 ] && usage
+collection="$1"
+name="$2"
+target="$3"
+
+base="https://raw.githubusercontent.com/LibreOffice/dictionaries/master/${collection}";
+mkdir -p "${target}"
+
+# The .aff and .dic pair is mandatory; a missing file fails the script.
+for ext in aff dic; do
+  echo "downloading ${name}.${ext}"
+  curl --fail --location --silent --show-error --retry 3 \
+    --output "${target}/${name}.${ext}" "${base}/${name}.${ext}"
+done
+
+# The readme carries the dictionary's license; keep it next to the data. 
Different
+# collections name it differently, so try the common patterns and keep what 
exists.
+for readme in "README_${name}.txt" "README_${collection}.txt" "README.txt" 
"license.txt"; do
+  if curl --fail --location --silent --retry 3 \
+      --output "${target}/${readme}" "${base}/${readme}" 2>/dev/null; then
+    echo "downloaded ${readme} (contains the dictionary's license; read it)"
+  else
+    rm -f "${target}/${readme}"
+  fi
+done
+
+echo ""
+echo "stored ${target}/${name}.aff and ${target}/${name}.dic"
+echo "next: load them from Java; see README-hunspell-dictionaries.md"

Reply via email to