Is it possible to use flyspell on an org document where the dictionary is specified by a property variable? For instance

* English
  set property to english dictionary

* Italiano
  set property to italian dictionary

Flyspell would then use the English dictionary on the English section, the Italian dictionary on the Italian section.

1 Answer 1


Let me provide an answer as long as there is no better answer. What you are asking for should not be so difficult to achieve, using the Mapping API and Property API, if flyspell could run in synchronous mode (via a synchronous process). Unfortunately, it seems that that synchronous use of flyspell is broken (at least here using GNU/linux with Emacs 29). As the ispell code is quite complicated/long, there is no way of fixing this quickly (for me).

Therefore, this answer uses (the default) asynchronous flyspell 'mode' (see ispell-async-processp). It seems to work quite fine for highlighting the misspelled words. However, for getting the correct correction suggestions, you have to select the right dictionary manually (using M-x ispell-change-dictionary).

For this solution, you can select the dictionary to use for some section via a :dictionary: property in a property drawer as follows:

* English
:dictionary: en

set property to english dictionary

* Nederlands
:dictionary: nl

stel de eigenschap in op Engels woordenboek

For the dictionary names you can use the names as listed by M-x ispell-change-dictionary.

Then use the following function for flyspelling the org buffer:

(defun org-flyspell-buffer ()
  (let* (start end dict regions)
     (lambda ()
       (when start (push (list dict start (point)) regions))
       (setq start (point)
             dict (org-entry-get nil "dictionary"))))
    (push (list dict start (point-max)) regions)
    (dolist (r (nreverse regions))
      (let ((ispell-local-dictionary (car r)))
        (print r)
        (flyspell-region (+ (nth 1 r) 3) (1- (nth 2 r)))))))

I did not rigorously test this solution, so success may vary, but it seems to work quite fine here.

  • 1
    I'm not sure you can do everything you need with the [Mapping API]() and the [Property API]() but assuming you can, it should be somewhat simpler than using the Org Element API. The main thing that might be missing is the region beg/end positions: that might require a parsing call from the Org Element API (probably org-element-at-point would be enough). One more point: what happens if there is no dictionary property in the headline? I haven't tested it out, but by my reading, the section will be skipped - am I wrong?
    – NickD
    Dec 28, 2023 at 16:34
  • 1
    @NickD Indeed, I more or less knew there was an easier method (that's why I mentioned the 'better' answer), but I was not aware of the Mapping and Property API's (I had seen org-map-entries before, but could not remember its name). Anyway, I agree that those API's are the more appropriate choice here . About the dictionary, if a headline has no dictionary property, then the local value for ispell-local-dictionary will be nil and flyspell will use the default dictionary. Dec 28, 2023 at 18:46
  • 1
    Thanks for adding the links (that I forgot to add :-) ). The updated code looks better to me: in particular, it addresses my concern about skipping sections, I think.
    – NickD
    Dec 28, 2023 at 21:00
  • The last word in the English section "dictionary" is underlined as misspelled (and so does english, which should be capitalized). When I extend the text, i.e. add "and see what happens", then "dictionary" is not underlined as misspelled, but "happens" is.
    – tempestcat
    Dec 29, 2023 at 15:39
  • I see. It is because, for some reason, flyspell-region acts on a region that is a slightly larger than the region passed to it (this might be a bug in flyspell-region). Anyway, just increasing the start position passed to flyspell-region to 3 places after the start of the headline, seems to fix this 'faulty' behavior. I have updated the code example with the 'fix/workaround'. Dec 29, 2023 at 16:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.