electron-userland / electron-spellchecker

Implement spellchecking, correctly
MIT License
238 stars 83 forks source link

macOS does NOT do automatic language detection #85

Open alexstrat opened 7 years ago

alexstrat commented 7 years ago

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

akashnimare commented 7 years ago

@alexstrat I can reproduce. Any workaround for this?

akashnimare commented 7 years ago

@alexstrat same on Linux as well.

ccorcos commented 6 years ago

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

ccorcos commented 6 years ago

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 commented 6 years ago

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

ccorcos commented 6 years ago

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)
}
will-russell commented 6 years ago

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).

quanglam2807 commented 4 years ago

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