[MacPorts] #74298: hunspell-en: contractions such as "didn't" are reported as misspellings (WORDCHARS omits the apostrophe)

MacPorts noreply at macports.org
Tue Aug 4 03:18:00 UTC 2026


#74298: hunspell-en: contractions such as "didn't" are reported as misspellings
(WORDCHARS omits the apostrophe)
----------------------------+-------------------------
 Reporter:  diekhans        |      Owner:  (none)
     Type:  defect          |     Status:  new
 Priority:  Normal          |  Milestone:
Component:  ports           |    Version:
 Keywords:  hunspell scowl  |       Port:  hunspell-en
----------------------------+-------------------------
 Analysis and ticket text by Claude Code

 = Description =

 The affix files installed by every `hunspell-en_*` subport declare

 {{{
 WORDCHARS 0123456789
 }}}

 with no apostrophe. Hunspell derives its word tokenizer from that line, so
 it
 splits at the apostrophe and checks the fragments separately. Contractions
 are
 therefore never matched against the dictionary, even though they are
 present in
 it verbatim: `en_US.dic` line 20172 is literally `didn't`, and it can
 never be
 reached.

 Where the leading fragment is not itself a word, the user sees a bogus
 misspelling: `didn`, `doesn`, `isn`, `wasn`, `hasn`, `couldn`, and so on.
 Where
 it happens to be a word (`don't` -> `don` + `t`), the error is silent but
 the
 dictionary entry is still unused.

 `en_US.dic` contains 412 entries with an apostrophe. Checking that list
 against
 the installed dictionary produces bogus misspellings for 56 of them; the
 other
 356 pass only by accident.

 = Steps to reproduce =

 {{{
 $ printf "didn't\nisn't\ndoesn't\n" | hunspell -d en_US -a
 @(#) International Ispell Version 3.2.06 (but really Hunspell 1.7.3)
 & didn 4 0: did, din, dido, did n
 *

 & isn 9 0: sin, ins, ism, is, in, inn, ion, isl, is n
 *

 & doesn 2 0: does, does n
 *
 }}}

 Two result lines per input word show the split. Expected output is a
 single `*`
 for each.

 = Cause =

 The line comes straight from the SCOWL tarball, `speller/en.aff:17`.
 `speller/make-hunspell-dict` copies that one file verbatim to every
 generated
 affix file (line 44: `cp $SPELLER/en.aff $1.aff`), so all `en_*` subports
 are
 affected, not just `en_US`.

 This was fixed upstream in the LibreOffice/AOO/Mozilla English
 dictionaries on
 2024-01-29 by Marco A.G. Pinto, whose `en_US.aff` carries the comment

 {{{
 # 2024-01-29 (Marco A.G.Pinto)
 # - Fix: apostrophe handling, by adding: WORDCHARS 0123456789’ to the
 .aff.
 }}}

 and the line `WORDCHARS 0123456789’` (U+2019 RIGHT SINGLE QUOTATION MARK).
 Note
 that adding U+2019 alone is sufficient: it makes hunspell accept both the
 typographic and the ASCII apostrophe, and the affix file already maps one
 to the
 other with `ICONV ’ '`.

 Bumping the port will not pick this up. SCOWL itself has had no release
 since
 2020.12.07 (confirmed against the SourceForge file listing and `port
 livecheck`),
 so the port needs to carry the fix.

 = Suggested fix =

 Add a patchfile against the SCOWL affix template:

 {{{
 --- speller/en.aff.orig
 +++ speller/en.aff
 @@ -14,7 +14,7 @@
  COMPOUNDRULE 2
  COMPOUNDRULE n*1t
  COMPOUNDRULE n*mp
 -WORDCHARS 0123456789
 +WORDCHARS 0123456789’

  PFX A Y 1
  PFX A   0     re         .
 }}}

 `speller/en.aff` is also fed to `munch-list` during the build, so if
 regenerating
 the `.dic` files byte for byte matters, the substitution can be done in a
 `post-build` block against the generated `${worksrcpath}/speller/en_*.aff`
 instead. Patching the template matches what upstream did.

 = Verification =

 Applying the change to the installed affix files and rechecking the same
 412
 apostrophe entries gives zero misspellings, and the contractions above all
 return `*`. Both `en_US` and `en_US-large` behave the same way before and
 after.

 = Impact =

 Anything that consumes the MacPorts hunspell dictionaries: the `hunspell`
 CLI,
 Emacs `ispell`/`flyspell` (which reads the same WORDCHARS line to build
 its own
 word regexp, so it also treats the apostrophe as a word boundary), vim
 `spelllang`, and any application pointed at `${prefix}/share/hunspell`.

 = Workaround =

 Shadow the affix files with corrected copies and point `DICPATH` at them,
 which
 hunspell prepends to its search path:

 {{{
 D=~/Library/Spelling/hunspell; mkdir -p $D
 for d in en_US en_US-large; do
   ln -sf ${prefix}/share/hunspell/$d.dic $D/$d.dic
   perl -CSD -pe "s/^WORDCHARS (.*)\$/WORDCHARS \$1\x{2019}/" \
        ${prefix}/share/hunspell/$d.aff > $D/$d.aff
 done
 export DICPATH=$D
 }}}

 = Environment =

 macOS 26.5.2 (25F84), arm64, MacPorts 2.12.5
 hunspell @1.7.3_0
 hunspell-en_US @2020.12.07_0, hunspell-en_US_large @2020.12.07_0

-- 
Ticket URL: <https://trac.macports.org/ticket/74298>
MacPorts <https://www.macports.org/>
Ports system for macOS


More information about the macports-tickets mailing list