2

I am working on a package that makes an API call and gets strings with improperly oriented curly quotes, e. g.,

The word ’syneresis’ comes from Greek roots meaning ’take’ and ’together’.

Is there a clean builtin way (function I could call) to fix issues like this?

Edit: Example

(fix-quotes "’syneresis’ comes from Greek roots meaning ’take’")
    => ‘syneresis’ comes from Greek roots meaning ‘take’

Which looks like this:

‘syneresis’ comes from Greek roots meaning ‘take’

See here for curly quotes reference. Note that in the original string we only had ending quotes.

Edit: Picture

Here is a picture that may make it more clear,

enter image description here

The top text has the improperly oriented quote mark because it is the right quote (punctuation that denotes the end of a quotation). We need the properly oriented quote mark which is the left quote (punctuation that denotes the start of a quotation). Imagine <,> are quote marks, the input is >take> and we want <take>.

8
  • Please describe or show just what the problem is - it's not clear. Specify what you mean by "improperly oriented".
    – Drew
    Commented Oct 29, 2021 at 4:31
  • Does that help? Zoom in to the opening quote mark in the original and returned string.
    – scribe
    Commented Oct 29, 2021 at 4:42
  • If it's guaranteed that the character is only used in non-nested pairs for quoting, then it's a pretty easy regexp replacement. Is that the case?
    – phils
    Commented Oct 29, 2021 at 6:06
  • What about (replace-regexp-in-string "’\<" "‘" "‘syneresis’ comes from Greek roots meaning ‘take’" ) ?
    – gigiair
    Commented Oct 29, 2021 at 10:07
  • 1
    In the zoomed image, the quotes on the top line look fat at the top and skinny at the bottom, whereas on the bottom line the quotes look skinny at the top and fat at the bottom. Is that what you are asking about? Have you tried C-u C-x = on these to find out what code points they correspond to? Please add this information to your question.
    – NickD
    Commented Oct 29, 2021 at 19:28

2 Answers 2

0

If I understand your question, it asks whether there is an existing function that will automatically "fix" the use of wrong curly-quote chars, replacing them with the right curly-quote chars (i.e. RIGHT SINGLE QUOTATION MARK), in any bit of English text.

Not knowing about every existing function, I think it's a safe bet to answer that no, there is no such function. How is such an imagined function to know what anyone thinks is the use of improper quote marks?


On the other hand, if you can recognize such improper use then you can correct it, using, for example, M-% (query-replace). Just enter the right (close) curly single-quote char to replace and the left (open) curly single-quote char as the replacement. And then replace the occurrences that need to be replaced.


If you can specify a regular pattern of replacement (e.g. every odd occurrence of a right curly quote needs to be replaced) then you can maybe make a bulk replacement using Emacs Lisp. For that, you'd typically continually search forward from (point-min) for the right curly quote using, say, search-forward, and use replace-match on every other match.

For example, if you know you have something like this:

’aaa’bbb’ccc’ddd’eee’fff’ggg’hhh’iii’

Then you can use a command such as this, to change every other right-quote mark.

(defun foo ()
  (interactive)
  (let ((cnt  0))
    (while (search-forward "’" nil :NOERROR)
      (setq cnt  (1+ cnt))
      (unless (zerop (mod cnt 2)) (replace-match "‘")))))

M-x foo changes that to this:

‘aaa’bbb‘ccc’ddd‘eee’fff‘ggg’hhh‘iii’

But if there's no such regular pattern, then interactive query-replace is what I'd recommend.

For example, what do you expect to do if this is the order of curly-quotes encountered (Right Left Right Left Right Left Right Right Right)?

’  ‘  ’  ‘  ’  ‘  ’ ’ ’ 
0

Assuming English typesetting (different languages and sometimes countries have different rules), and assuming pure text (markup languages make this more complicated), the rules are:

  • At the beginning of a word, it can only be an opening quote .
  • At the end of a word, it can only be a closing quote .
  • In the middle of a word, it's probably an apostrophe which is identical to a closing single quote.
  • If not inside or adjacent to a word, this isn't normal typography and should be left alone.

You can use the regular expression constructs to detect word boundaries, in particular \< to match only at the beginning of a word and \> to match only at the end of a word.

In the function below, I use search-forward-regexp to look for quotes that are either at the beginning of a word, at the end of a word, or inside a word. A quote inside a word is both at the beginning and at the end of a word since quote characters themselves are not word constituents. When I detect a mismatched or ASCII quote, I replace it by the appropriate curly quote.

(defun normalize-english-quotes (start end)
  "Normalize English single and double quotes.

Make sure that only ‘ or “ is used as an opening quote, and only ’ or ” is
used as a closing quote. Mistyped ‘’“” and ASCII '\" are replaced by the correct
curly quote for English typography.

Interactively, operate on the region if the mark is active, otherwise operate
on the remainder of the buffer."
  (interactive (if (use-region-p)
           (list (region-beginning) (region-end))
         (list (point) nil)))
  (save-excursion
    (goto-char start)
    (while (search-forward-regexp "\\>\\(['\"‘’“”]\\)\\|\\(['\"‘’“”]\\)\\<" end t)
      (let* ((old-char (char-after (1- (point))))
         (double (memq old-char '(?\" ?“ ?”)))
         (no-word-before (null (match-beginning 1)))
         (no-word-after (not (looking-at "\\<")))
         (new-char (cond
            ((and double no-word-before) ?“)
            ((and double no-word-after) ?”)
            ((and (not double) no-word-before) ?‘)
            ((not double) ?’))))
    (when new-char
      (delete-char -1)
      (insert-char new-char))))))

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.