Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto-detect note language (for spelling and fonts) #1205

Closed
wants to merge 2 commits into from

Conversation

mirka
Copy link
Member

@mirka mirka commented Feb 15, 2019

Closes #949
Closes #788

This enhances the spellchecker by adding language auto-detection. It's working pretty well in my testing, and hopefully it will be a better experience than having to manually select a language from a dropdown.

Bonus: Better fonts on Windows

As a bonus, I'm using the auto-detected language to set a lang attribute to the note body. This will help Electron in Windows to select language-appropriate fonts when the primary OS language is not set to that (#788). One limitation to this is that it's only for the Note Editor, and not the NoteList, since that would be too costly. Still, it's an improvement over the current behavior.

To test

These changes only affect Electron on Windows and Linux. (Don't know why, but the language detection feature in electron-spellchecker is disabled on Mac. It's strange because it works if I just comment out that line.)

You can set localStorage.debug="electron-spellchecker:*" in the devtools console and reload to see the language switching in action. Language will be determined every time a different note is selected, or whenever the spelling/auto-detection menu items are toggled. If a language cannot be detected (either by being too short or ambiguous), it will use the OS language.

Test instructions on Linux/Windows

  1. Enable Check Spelling in the Edit menu.
  2. Grab some dummy text in several spell-checkable languages (English, French, German, etc.) and make a new note for each language text. Assuming that your system language is set to English, non-English words should be considered spelling errors and have squiggly lines.
  3. Enable Auto-Detect Language in the Edit menu.
  4. Now see the non-English notes again. Squiggly lines should be gone. (It may take a few seconds because the spelling dictionaries need to be downloaded first.)
  5. Delete some characters from a word or two to make spelling errors, and see that they show squiggly lines.

Test instructions on Mac

  1. See that the Auto-Detect Language option in the Edit menu is hidden. (Feature is disabled on Mac)

@mirka mirka changed the title [WIP] Auto-detect note language (for spelling and fonts) Auto-detect note language (for spelling and fonts) Feb 15, 2019
@mirka mirka requested a review from roundhill February 15, 2019 21:28
@roundhill roundhill removed their request for review May 21, 2019 16:07
@belcherj belcherj changed the base branch from master to develop June 12, 2019 20:31
@dmsnell dmsnell force-pushed the feature/detect-language branch from c3c0149 to afbc023 Compare November 5, 2019 02:18
@dmsnell dmsnell force-pushed the feature/detect-language branch from afbc023 to 5feb67f Compare December 4, 2019 15:27
@mirka mirka self-assigned this Feb 3, 2020
@mirka mirka force-pushed the feature/detect-language branch from b36c0c3 to 8e0c914 Compare February 3, 2020 18:24
} = getState();

dispatch(setLanguageDetection(!languageDetectionEnabled));
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey! fun fact - we don't ned to create imperative thunk-style action creators for toggle-actions like this. reducers are great at keeping things simpler and then our actions remain a basic data object representing the action our users want to take.

// state/action-types
export type ToggleLanguageDetection = Action<'TOGGLE_LANGUAGE_DETECTION'>;

// …/settings/actions.ts
import * as A from '../action-types';

export const toggleLanguageDetection: A.ActionCreator<A.ToggleLanguageDetection> = () => ( {
	type: 'TOGGLE_LANGUAGE_DETECTION',
} );

// …/settings/reducer.ts
const languageDetection: A.Reducer<boolean> = (state = false, action) => {
	switch (action.type) {
		case 'SET_LANGUAGE_DETECTION':
			return action.languageDetectionEnabled;

		case 'TOGGLE_LANGUAGE_DETECTION':
			return !state;

		default:
			return state;
	}
}

Note here that we never need to base our intentions/actions off of the current app state - all we have to do is explain what we're trying to accomplish and then the reducers govern their own data type. It gets murkier when we mash together multiple data values into a single reducer, which is where combineReducers can help (you can see how things were prior to #1216 - sometimes the boiler plate is valuable for other reasons, here in keeping separate data types isolated from each other and making it easier to reason about changes from the user's perspective).

It may also be the case that when we're working this way we don't need the SET_ action at all. This is the css with the edit/preview mode toggle, because the app starts in one mode and all that matters from the user's perspective is the intention to toggle the mode, not necessary to set it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that makes sense. And I see the benefits of the combineReducers approach better now. Thanks for the tip!

@dmsnell
Copy link
Member

dmsnell commented Feb 6, 2020

@mirka it's so awesome to see you updating this PR - thanks so much!

if (spellCheckEnabled !== prevProps.spellCheckEnabled) {
this.editorKey += 1;
this.forceUpdate();
updateLanguage();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we instead incorporate the language into the key itself so that we don't need to track an increasing number and force a re-render?

getEditorKey = () => {
  const { spellCheckEnabled } = this.props;
  const { lang } = this.state;

  return `${ spellCheckEnabled ? 'spellcheck' : 'no_spellcheck' }-${ lang || 'unknown_lang' }`;
}

render() {
  
    key={ this.editorKey() }
  
}

With this I believe we should be able to get rid of the force-update cycles and allow the prop-changes to trigger the editor update naturally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaced in ef9fcb7

@@ -241,6 +245,7 @@ export class NoteDetail extends Component {
}

const mapStateToProps = ({ appState: state, ui, settings }) => ({
detectLanguage: settings.languageDetectionEnabled,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might as well add this inside of <NoteContentEditor /> so we don't have to pass it through this component.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in ef9fcb7

checked: settings.languageDetectionEnabled,
click: appCommandSender({ action: 'toggleLanguageDetection' }),
visible: !platform.isOSX(), // currently not working on Mac
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides disabling language-detection (and switching back to English?) is there any way to manually choose the language?

I can imagine that if someone wants to write in one language about another language the detection could be consistently wrong but then I'd want to manually fix it.

For example, writing in German about French poetry.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is technically feasible, but I think that use case probably isn't common enough to warrant the clutter of an additional menu item. However, I did initially post a UI suggestion for a global manual select (as well as some other, per-note possibilities) in the original issue.

This is mostly a UI design decision, so my thinking was to start out with the most simplest design, which is auto detection. If that works well enough for the majority of our users, great. If not, we can revisit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did initially post a UI suggestion for a global manual select

Yeah that's what I meant. It's a good idea.

start out with the most simplest design

Also a great idea. I wouldn't want to hold up this PR for not adding more but I wanted to ask if it was something already available we might have just been overlooking, for example, in the right-click menu of the text view if all we had to do were set some boolean toggle…

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to ask if it was something already available we might have just been overlooking

I'm afraid not 😅 Also, I wrote "technically feasible" but I overlooked one possible complication we might run into: Getting a definitive list of available spell checker languages. I haven't looked into it too deeply, but it doesn't seem like a language list is exposed through electron-spellchecker. If Chromium doesn't expose a list, it might get complicated. (Just a heads up in case you consider it in the future.)

@dmsnell dmsnell force-pushed the feature/detect-language branch 2 times, most recently from 7cb3fad to 710cd0f Compare February 26, 2020 21:00
@dmsnell
Copy link
Member

dmsnell commented Feb 26, 2020

@mirka I have rebased this PR against the latest develop and pushed some changes from my comments. I tested it in macOS with the disable commented out but haven't yet tested it in Windows or Linux

@mirka
Copy link
Member Author

mirka commented Feb 27, 2020

@dmsnell Thanks for the review and improvements! Feel free to move forward however the team wishes 👍

@dmsnell dmsnell force-pushed the feature/detect-language branch from ef9fcb7 to c66fe2c Compare March 17, 2020 00:19
dmsnell added 2 commits March 16, 2020 22:52
This commit rebases the work done by @mirka in 528aa9c against the latest `develop`
to simplify the branching and merging which had been keeping it up to date.
In this commit we're replacing the method of force-upating the editor
to reflect language changes with a declarative method based on incorporating
the relevant paratemers into the editor key.

For example, instead of setting the editor key to `0` and then incrementing
to `1` and force-updating it we now set the key to `NOTE_ID-with-spelling-lang-de`
or if the detected language change to `NOTE_ID-with-spell-lang-en_US`. This
should automatically recreate the editor as we want without going through
a workaround to force it.

We're also moving the dependent state-based props from `<NoteDetail />` to
`<NoteContentEditor />` where they are used so that we don't inexplicably
pass them _through_ the parent component.
@belcherj
Copy link
Contributor

Closing for now. Too out of date. We can take a look when we are ready to work on this.

@belcherj belcherj closed this Mar 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants