electron-spellchecker icon indicating copy to clipboard operation
electron-spellchecker copied to clipboard

macOS does NOT do automatic language detection

Open alexstrat opened this issue 7 years ago • 8 comments

In opposite to what is assumed here it does not look like macOS is doing automatic language detection.

My guess is that because we use node-spellchecker#setDictionary, the automatic language detection is deactivated given what is done in MacSpellchecker::UpdateGlobalSpellchecker

alexstrat avatar Aug 10 '17 15:08 alexstrat

@alexstrat I can reproduce. Any workaround for this?

akashnimare avatar Oct 11 '17 13:10 akashnimare

@alexstrat same on Linux as well.

akashnimare avatar Oct 16 '17 21:10 akashnimare

Hmm. This used to work for me, but not anymore.

ccorcos avatar Mar 01 '18 22:03 ccorcos

I just wanted to loop back around to this. It appears that somewhere in the past 6 months, the language auto detect has stopped working. @paulcbetts any ideas how to fix this? I'm more than happy to help.

ccorcos avatar Apr 05 '18 01:04 ccorcos

This is a little suspect: https://github.com/atom/node-spellchecker/blob/master/src/spellchecker_mac.mm#L149-L154

ccorcos avatar Apr 05 '18 01:04 ccorcos

Here's my attempt for language detection:

import * as _ from "lodash"
import { webFrame } from "electron"

interface Spellchecker {
	isMisspelled: (word: string) => boolean
	getCorrectionsForMisspelling: (word: string) => Array<string>
	checkSpelling: (corpus: string) => Array<{ start: number; end: number }>
	checkSpellingAsync: (
		corpus: string
	) => Promise<Array<{ start: number; end: number }>>
	add: (word: string) => void
	remove: (word: string) => void
	getAvailableDictionaries: () => Array<string>
	setDictionary: (lang: string) => void
}

interface Cld {
	detect: (text: string, fn: (err: Error, result: any) => void) => void
}

class SpellCheck {
	// cld is a native C program by google for determining the language
	// of a body of text. Note: it can handle HTML with the right options.
	private cld?: Cld

	// Native bindings to Mac and Windows spellcheckers.
	private spellchecker?: Spellchecker

	// Language that we're spell-checking
	private currentLanguage: string | undefined

	// Dynamically import the native modules in case they fail.
	async initialize() {
		this.spellchecker = await import("spellchecker")
		this.cld = await import("cld")
		document.addEventListener("selectionchange", this.handleSelectionChange)
	}

	// Detect language as the selection changes.
	private currentInputElement: HTMLElement | undefined
	private handleSelectionChange = () => {
		const inputElement = this.getInputElement()
		if (inputElement && inputElement !== this.currentInputElement) {
			// If the selection changed inputs, compute new language immediately.
			this.currentInputElement = inputElement
			this.handleDetectLanguageChange(inputElement)
		} else if (inputElement) {
			this.handleDetectLanguageChangeThrottled(inputElement)
		}
	}

	private handleDetectLanguageChange = (element: HTMLElement) => {
		const text = element.textContent
		if (text) {
			const lang = this.detectLanguage(text)
			if (lang) {
				this.setLanguage(lang)
			}
		}
	}

	private handleDetectLanguageChangeThrottled = _.throttle(
		this.handleDetectLanguageChange,
		500
	)

	private detectLanguage(text: string) {
		let lang: string | undefined
		if (this.cld) {
			this.cld.detect(text, (err, result) => {
				if (result && result.reliable) {
					if (result.languages) {
						const language = result.languages[0]
						if (language && language.code) {
							lang = language.code
						}
					}
				}
			})
		}
		return lang
	}

	private setLanguage(lang: string) {
		if (this.currentLanguage !== lang) {
			this.currentLanguage = lang
			webFrame.setSpellCheckProvider(lang, true, {
				spellCheck: word => {
					if (this.spellchecker) {
						// Note: you can iterate through multiple languages here.
						this.spellchecker.setDictionary(lang)
						if (!this.spellchecker.isMisspelled(word)) {
							return true
						}
					}
					return false
				},
			})
		}
	}

	private getInputElement() {
		const element = document.activeElement
		if (element instanceof HTMLElement) {
			if (element.tagName === "input" || element.contentEditable === "true") {
				return element
			}
		}
	}
}

const spellcheck = new SpellCheck()

try {
	spellcheck.initialize()
} catch (error) {
	console.error(error)
}

ccorcos avatar Apr 06 '18 17:04 ccorcos

Just to expand (confound?) the issue; this is also not working on Windows 10 (64-bit) (discovered in GitKraken, which uses electron for spell checking).

will-russell avatar Jun 18 '18 15:06 will-russell

I confirm language detection still doesn't work. It'd be great if this can be fixed.

@ccorcos Is there an easy way to use your patch?

Update: it seems like Electron now supports spellchecker natively: https://github.com/electron/electron/pull/20692

quanglam2807 avatar Nov 30 '19 18:11 quanglam2807