Instant Smart Quotes is a browser extension that converts typewriter quotes, apostrophes, ellipses and dashes with their typographically correct counterparts as you type.
By that Instant Smart Quotes enables you produce text with nice typography in mind.
In a browser it works like this:

An icon in the toolbar to quickly switch it on/off and change the language and you just type like you’d normally do. Demo
The default language is the browser’s UI language. On/off status and language settings are persisted per page and shared across all of your devices.
I think for Trix the very same concept can be used. Instead of having the icon in the browser toolbar, you’d have it in the Trix toolbar with the very same dropdown.
What would you think of that?
Source of the idea: Zemke/instant-smart-quotes#1
Hey @Zemke
I threw together a quick proof of concept implementation of this for a hobby project I’m working on. Still needs a lot of work; I think some of this can be done much more efficiently, but it’s a start.
@mwichary, would you be able to give us some pointers? 😻
Here’s a demo.

document.addEventListener "trix-change", (event) ->
editor = event.target.editor
unless editor.attributeIsActive "code"
position = editor.getPosition()
text = editor.getDocument().toString()
character = text.charAt(position - 1)
before = text.substr 0, position
after = text.substr position, text.length
if position is 1 and character is '"'
editor.deleteInDirection "backward"
editor.insertString "“"
else if position is 1 and character is "'"
editor.deleteInDirection "backward"
editor.insertString "‘"
else if before.match /\s"$/
editor.deleteInDirection "backward"
editor.insertString "“"
else if before.match /\s'$/
editor.deleteInDirection "backward"
editor.insertString "‘"
else if before.match(/“[^”]*"$/) and not after.match(/^[^“]*”/)
editor.deleteInDirection "backward"
editor.insertString "”"
else if before.match(/‘[^’]*'$/) and not after.match(/^[^‘]*’/)
editor.deleteInDirection "backward"
editor.insertString "’"
else if before.match /‘[^’]*'$/
editor.deleteInDirection "backward"
editor.insertString "’"
else if before.match /[0-9↉½⅓¼⅕⅙⅐⅛⅑⅒⅔⅖¾⅗⅜⅘⅚⅝⅞]"$/
editor.deleteInDirection "backward"
editor.insertString '″'
else if before.match(/[0-9↉½⅓¼⅕⅙⅐⅛⅑⅒⅔⅖¾⅗⅜⅘⅚⅝⅞]'$/) and not after.match(/^s/)
editor.deleteInDirection "backward"
editor.insertString "′"
else if before.match /′s$/
editor.setSelectedRange position - 1
editor.deleteInDirection "backward"
editor.insertString "’"
editor.setSelectedRange position
else if before.match /[^\s]'$/
editor.deleteInDirection "backward"
editor.insertString "’"
else if before.match /‘em[^a-z0-9]$/i
editor.setSelectedRange [position - 4, position - 3]
editor.insertString "’"
editor.setSelectedRange position
else if before.match /‘cause[^a-z0-9]$/i
editor.setSelectedRange [position - 7, position - 6]
editor.insertString "’"
editor.setSelectedRange position
else if before.match /‘n[^a-z0-9]$/i
editor.setSelectedRange [position - 3, position - 2]
editor.insertString "’"
editor.setSelectedRange position
else if before.match /‘twas[^a-z0-9]$/i
editor.setSelectedRange [position - 6, position - 5]
editor.insertString "’"
editor.setSelectedRange position
else if before.match /‘[0-9]{2}[^a-rt-z0-9]$/i
editor.setSelectedRange [position - 4, position - 3]
editor.insertString "’"
editor.setSelectedRange position
I'm going to simplify this and mature it in my app for a bit, then I'll make a PR to enable it as an option.
Lovely!
Awesome! It’s only missing en-dash and em-dash and ellipses substitutions. Other than that—usability-wise—perfect!
@Zemke and @sstephenson thanks! I’m working on those other substitutions. Also, I think I’ve found a way to combine some of these rules and make them a little bit more efficient. I’m pretty new to Regex, so still using a cheat-sheet for this. 😊
I’m starting to think this would have been a damn good time to write tests first, so I’m going to take a step back for now and work on those.
@javan, are you happy for me to work on a PR, with the aim being to add it as a configuration option?
This issue has been automatically marked as stale after 90 days of inactivity. It will be closed if no further activity occurs.
@joeldrapper, have you progressed on this or could you share more of the code than what you’ve already provided? No matter if this is going to be part of Trix or not—which it is apparently not—I would like to include improvements for Zemke/instant-smart-quotes.
This issue has been automatically marked as stale after 90 days of inactivity. It will be closed if no further activity occurs.
Most helpful comment
Hey @Zemke
I threw together a quick proof of concept implementation of this for a hobby project I’m working on. Still needs a lot of work; I think some of this can be done much more efficiently, but it’s a start.
@mwichary, would you be able to give us some pointers? 😻
Here’s a demo.
I'm going to simplify this and mature it in my app for a bit, then I'll make a PR to enable it as an option.