aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Stefan Malzner <stefan@adlk.io>2019-02-12 20:49:05 +0100
committerLibravatar Stefan Malzner <stefan@adlk.io>2019-02-12 20:49:05 +0100
commit243bd8fe742449668d559b9e7eaeff91467ac794 (patch)
tree53fcca658be5bb906bf6b335d6e96c6b67d84e52 /src
parentfeat(Windows): Add option to quit Franz from Taskbar icon (diff)
parentfeat(Spell checking): Add option to automatically detect language (diff)
downloadferdium-app-243bd8fe742449668d559b9e7eaeff91467ac794.tar.gz
ferdium-app-243bd8fe742449668d559b9e7eaeff91467ac794.tar.zst
ferdium-app-243bd8fe742449668d559b9e7eaeff91467ac794.zip
Merge branch 'feature/spellcheck-autodetect' into release/5.0.0
Diffstat (limited to 'src')
-rw-r--r--src/components/settings/services/EditServiceForm.js10
-rw-r--r--src/containers/settings/EditServiceScreen.js40
-rw-r--r--src/containers/settings/EditSettingsScreen.js16
-rw-r--r--src/features/spellchecker/index.js8
-rw-r--r--src/helpers/i18n-helpers.js27
-rw-r--r--src/i18n/globalMessages.js16
-rw-r--r--src/i18n/locales/en-US.json4
-rw-r--r--src/webview/contextMenu.js12
-rw-r--r--src/webview/recipe.js56
-rw-r--r--src/webview/spellchecker.js11
10 files changed, 157 insertions, 43 deletions
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 468d85c45..21616b5de 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -128,7 +128,8 @@ export default @observer class EditServiceForm extends Component {
128 isSaving: PropTypes.bool.isRequired, 128 isSaving: PropTypes.bool.isRequired,
129 isDeleting: PropTypes.bool.isRequired, 129 isDeleting: PropTypes.bool.isRequired,
130 isProxyFeatureEnabled: PropTypes.bool.isRequired, 130 isProxyFeatureEnabled: PropTypes.bool.isRequired,
131 isProxyFeaturePremiumFeature: PropTypes.bool.isRequired, 131 isProxyPremiumFeature: PropTypes.bool.isRequired,
132 isSpellcheckerPremiumFeature: PropTypes.bool.isRequired,
132 }; 133 };
133 134
134 static defaultProps = { 135 static defaultProps = {
@@ -191,7 +192,8 @@ export default @observer class EditServiceForm extends Component {
191 isDeleting, 192 isDeleting,
192 onDelete, 193 onDelete,
193 isProxyFeatureEnabled, 194 isProxyFeatureEnabled,
194 isProxyFeaturePremiumFeature, 195 isProxyPremiumFeature,
196 isSpellcheckerPremiumFeature,
195 } = this.props; 197 } = this.props;
196 const { intl } = this.context; 198 const { intl } = this.context;
197 199
@@ -339,14 +341,14 @@ export default @observer class EditServiceForm extends Component {
339 </div> 341 </div>
340 </div> 342 </div>
341 343
342 <PremiumFeatureContainer> 344 <PremiumFeatureContainer condition={isSpellcheckerPremiumFeature}>
343 <div className="settings__settings-group"> 345 <div className="settings__settings-group">
344 <Select field={form.$('spellcheckerLanguage')} /> 346 <Select field={form.$('spellcheckerLanguage')} />
345 </div> 347 </div>
346 </PremiumFeatureContainer> 348 </PremiumFeatureContainer>
347 349
348 {isProxyFeatureEnabled && ( 350 {isProxyFeatureEnabled && (
349 <PremiumFeatureContainer condition={isProxyFeaturePremiumFeature}> 351 <PremiumFeatureContainer condition={isProxyPremiumFeature}>
350 <div className="settings__settings-group"> 352 <div className="settings__settings-group">
351 <h3> 353 <h3>
352 {intl.formatMessage(messages.headlineProxy)} 354 {intl.formatMessage(messages.headlineProxy)}
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index b46908344..d08f0a52e 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -19,9 +19,12 @@ import { required, url, oneRequired } from '../../helpers/validation-helpers';
19import { getSelectOptions } from '../../helpers/i18n-helpers'; 19import { getSelectOptions } from '../../helpers/i18n-helpers';
20 20
21import { config as proxyFeature } from '../../features/serviceProxy'; 21import { config as proxyFeature } from '../../features/serviceProxy';
22import { config as spellcheckerFeature } from '../../features/spellchecker';
22 23
23import { SPELLCHECKER_LOCALES } from '../../i18n/languages'; 24import { SPELLCHECKER_LOCALES } from '../../i18n/languages';
24 25
26import globalMessages from '../../i18n/globalMessages';
27
25const messages = defineMessages({ 28const messages = defineMessages({
26 name: { 29 name: {
27 id: 'settings.service.form.name', 30 id: 'settings.service.form.name',
@@ -83,14 +86,6 @@ const messages = defineMessages({
83 id: 'settings.service.form.proxy.password', 86 id: 'settings.service.form.proxy.password',
84 defaultMessage: '!!!Password', 87 defaultMessage: '!!!Password',
85 }, 88 },
86 spellcheckerLanguage: {
87 id: 'settings.service.form.spellcheckerLanguage',
88 defaultMessage: '!!!Spell checking Language',
89 },
90 spellcheckerSystemDefault: {
91 id: 'settings.service.form.spellcheckerLanguage.default',
92 defaultMessage: '!!!Use System Default ({default})',
93 },
94}); 89});
95 90
96export default @inject('stores', 'actions') @observer class EditServiceScreen extends Component { 91export default @inject('stores', 'actions') @observer class EditServiceScreen extends Component {
@@ -118,12 +113,26 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
118 } 113 }
119 114
120 prepareForm(recipe, service, proxy) { 115 prepareForm(recipe, service, proxy) {
116 const {
117 intl,
118 } = this.context;
119
120 const {
121 stores,
122 } = this.props;
123
124 let defaultSpellcheckerLanguage = SPELLCHECKER_LOCALES[stores.settings.app.spellcheckerLanguage];
125
126 if (stores.settings.app.spellcheckerLanguage === 'automatic') {
127 defaultSpellcheckerLanguage = intl.formatMessage(globalMessages.spellcheckerAutomaticDetectionShort);
128 }
129
121 const spellcheckerLanguage = getSelectOptions({ 130 const spellcheckerLanguage = getSelectOptions({
122 locales: SPELLCHECKER_LOCALES, 131 locales: SPELLCHECKER_LOCALES,
123 resetToDefaultText: this.context.intl.formatMessage(messages.spellcheckerSystemDefault, { default: SPELLCHECKER_LOCALES[this.props.stores.settings.app.spellcheckerLanguage] }), 132 resetToDefaultText: intl.formatMessage(globalMessages.spellcheckerSystemDefault, { default: defaultSpellcheckerLanguage }),
133 automaticDetectionText: stores.settings.app.spellcheckerLanguage !== 'automatic' ? intl.formatMessage(globalMessages.spellcheckerAutomaticDetection) : '',
124 }); 134 });
125 135
126 const { intl } = this.context;
127 const config = { 136 const config = {
128 fields: { 137 fields: {
129 name: { 138 name: {
@@ -160,13 +169,13 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
160 isDarkModeEnabled: { 169 isDarkModeEnabled: {
161 label: intl.formatMessage(messages.enableDarkMode), 170 label: intl.formatMessage(messages.enableDarkMode),
162 value: service.isDarkModeEnabled, 171 value: service.isDarkModeEnabled,
163 default: this.props.stores.settings.app.darkMode, 172 default: stores.settings.app.darkMode,
164 }, 173 },
165 spellcheckerLanguage: { 174 spellcheckerLanguage: {
166 label: intl.formatMessage(messages.spellcheckerLanguage), 175 label: intl.formatMessage(globalMessages.spellcheckerLanguage),
167 value: service.spellcheckerLanguage, 176 value: service.spellcheckerLanguage,
168 options: spellcheckerLanguage, 177 options: spellcheckerLanguage,
169 disabled: !this.props.stores.settings.app.enableSpellchecking, 178 disabled: !stores.settings.app.enableSpellchecking,
170 }, 179 },
171 }, 180 },
172 }; 181 };
@@ -220,7 +229,7 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
220 } 229 }
221 230
222 if (proxy.isEnabled) { 231 if (proxy.isEnabled) {
223 const serviceProxyConfig = this.props.stores.settings.proxy[service.id] || {}; 232 const serviceProxyConfig = stores.settings.proxy[service.id] || {};
224 233
225 Object.assign(config.fields, { 234 Object.assign(config.fields, {
226 proxy: { 235 proxy: {
@@ -326,7 +335,8 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
326 onSubmit={d => this.onSubmit(d)} 335 onSubmit={d => this.onSubmit(d)}
327 onDelete={() => this.deleteService()} 336 onDelete={() => this.deleteService()}
328 isProxyFeatureEnabled={proxyFeature.isEnabled} 337 isProxyFeatureEnabled={proxyFeature.isEnabled}
329 isProxyFeaturePremiumFeature={proxyFeature.isPremium} 338 isProxyPremiumFeature={proxyFeature.isPremium}
339 isSpellcheckerPremiumFeature={spellcheckerFeature.isPremium}
330 /> 340 />
331 </ErrorBoundary> 341 </ErrorBoundary>
332 ); 342 );
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js
index f1706a721..992c49b09 100644
--- a/src/containers/settings/EditSettingsScreen.js
+++ b/src/containers/settings/EditSettingsScreen.js
@@ -14,10 +14,11 @@ import { config as spellcheckerConfig } from '../../features/spellchecker';
14 14
15import { getSelectOptions } from '../../helpers/i18n-helpers'; 15import { getSelectOptions } from '../../helpers/i18n-helpers';
16 16
17
18import EditSettingsForm from '../../components/settings/settings/EditSettingsForm'; 17import EditSettingsForm from '../../components/settings/settings/EditSettingsForm';
19import ErrorBoundary from '../../components/util/ErrorBoundary'; 18import ErrorBoundary from '../../components/util/ErrorBoundary';
20 19
20import globalMessages from '../../i18n/globalMessages';
21
21const messages = defineMessages({ 22const messages = defineMessages({
22 autoLaunchOnStart: { 23 autoLaunchOnStart: {
23 id: 'settings.app.form.autoLaunchOnStart', 24 id: 'settings.app.form.autoLaunchOnStart',
@@ -63,10 +64,6 @@ const messages = defineMessages({
63 id: 'settings.app.form.enableGPUAcceleration', 64 id: 'settings.app.form.enableGPUAcceleration',
64 defaultMessage: '!!!Enable GPU Acceleration', 65 defaultMessage: '!!!Enable GPU Acceleration',
65 }, 66 },
66 spellcheckerLanguage: {
67 id: 'settings.app.form.spellcheckerLanguage',
68 defaultMessage: '!!!Language for spell checking',
69 },
70 beta: { 67 beta: {
71 id: 'settings.app.form.beta', 68 id: 'settings.app.form.beta',
72 defaultMessage: '!!!Include beta versions', 69 defaultMessage: '!!!Include beta versions',
@@ -125,6 +122,7 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
125 122
126 const spellcheckingLanguages = getSelectOptions({ 123 const spellcheckingLanguages = getSelectOptions({
127 locales: SPELLCHECKER_LOCALES, 124 locales: SPELLCHECKER_LOCALES,
125 automaticDetectionText: this.context.intl.formatMessage(globalMessages.spellcheckerAutomaticDetection),
128 }); 126 });
129 127
130 const config = { 128 const config = {
@@ -166,11 +164,11 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
166 }, 164 },
167 enableSpellchecking: { 165 enableSpellchecking: {
168 label: intl.formatMessage(messages.enableSpellchecking), 166 label: intl.formatMessage(messages.enableSpellchecking),
169 value: !this.props.stores.user.data.isPremium && spellcheckerConfig.isPremiumFeature ? false : settings.all.app.enableSpellchecking, 167 value: !this.props.stores.user.data.isPremium && spellcheckerConfig.isPremium ? false : settings.all.app.enableSpellchecking,
170 default: !this.props.stores.user.data.isPremium && spellcheckerConfig.isPremiumFeature ? false : DEFAULT_APP_SETTINGS.enableSpellchecking, 168 default: !this.props.stores.user.data.isPremium && spellcheckerConfig.isPremium ? false : DEFAULT_APP_SETTINGS.enableSpellchecking,
171 }, 169 },
172 spellcheckerLanguage: { 170 spellcheckerLanguage: {
173 label: intl.formatMessage(messages.spellcheckerLanguage), 171 label: intl.formatMessage(globalMessages.spellcheckerLanguage),
174 value: settings.all.app.spellcheckerLanguage, 172 value: settings.all.app.spellcheckerLanguage,
175 options: spellcheckingLanguages, 173 options: spellcheckingLanguages,
176 default: DEFAULT_APP_SETTINGS.spellcheckerLanguage, 174 default: DEFAULT_APP_SETTINGS.spellcheckerLanguage,
@@ -230,7 +228,7 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
230 cacheSize={cacheSize} 228 cacheSize={cacheSize}
231 isClearingAllCache={isClearingAllCache} 229 isClearingAllCache={isClearingAllCache}
232 onClearAllCache={clearAllCache} 230 onClearAllCache={clearAllCache}
233 isSpellcheckerPremiumFeature={spellcheckerConfig.isPremiumFeature} 231 isSpellcheckerPremiumFeature={spellcheckerConfig.isPremium}
234 /> 232 />
235 </ErrorBoundary> 233 </ErrorBoundary>
236 ); 234 );
diff --git a/src/features/spellchecker/index.js b/src/features/spellchecker/index.js
index 63506103c..94883ad17 100644
--- a/src/features/spellchecker/index.js
+++ b/src/features/spellchecker/index.js
@@ -5,7 +5,7 @@ import { DEFAULT_FEATURES_CONFIG } from '../../config';
5const debug = require('debug')('Franz:feature:spellchecker'); 5const debug = require('debug')('Franz:feature:spellchecker');
6 6
7export const config = observable({ 7export const config = observable({
8 isPremiumFeature: DEFAULT_FEATURES_CONFIG.isSpellcheckerPremiumFeature, 8 isPremium: DEFAULT_FEATURES_CONFIG.isSpellcheckerPremiumFeature,
9}); 9});
10 10
11export default function init(stores) { 11export default function init(stores) {
@@ -14,9 +14,11 @@ export default function init(stores) {
14 autorun(() => { 14 autorun(() => {
15 const { isSpellcheckerPremiumFeature } = stores.features.features; 15 const { isSpellcheckerPremiumFeature } = stores.features.features;
16 16
17 config.isPremiumFeature = isSpellcheckerPremiumFeature !== undefined ? isSpellcheckerPremiumFeature : DEFAULT_FEATURES_CONFIG.isSpellcheckerPremiumFeature; 17 console.log('isSpellcheckerPremiumFeature', isSpellcheckerPremiumFeature);
18 18
19 if (!stores.user.data.isPremium && config.isPremiumFeature && stores.settings.app.enableSpellchecking) { 19 config.isPremium = isSpellcheckerPremiumFeature !== undefined ? isSpellcheckerPremiumFeature : DEFAULT_FEATURES_CONFIG.isSpellcheckerPremiumFeature;
20
21 if (!stores.user.data.isPremium && config.isPremium && stores.settings.app.enableSpellchecking) {
20 debug('Override settings.spellcheckerEnabled flag to false'); 22 debug('Override settings.spellcheckerEnabled flag to false');
21 23
22 Object.assign(stores.settings.app, { 24 Object.assign(stores.settings.app, {
diff --git a/src/helpers/i18n-helpers.js b/src/helpers/i18n-helpers.js
index 091b86b06..84146dd8c 100644
--- a/src/helpers/i18n-helpers.js
+++ b/src/helpers/i18n-helpers.js
@@ -28,22 +28,33 @@ export function getLocale({
28 return localeStr; 28 return localeStr;
29} 29}
30 30
31export function getSelectOptions({ locales, resetToDefaultText = '' }) { 31export function getSelectOptions({ locales, resetToDefaultText = '', automaticDetectionText = '' }) {
32 let options = []; 32 const options = [];
33 33
34 if (resetToDefaultText) { 34 if (resetToDefaultText) {
35 options = [ 35 options.push(
36 { 36 {
37 value: '', 37 value: '',
38 label: resetToDefaultText, 38 label: resetToDefaultText,
39 }, {
40 value: '───',
41 label: '───',
42 disabled: true,
43 }, 39 },
44 ]; 40 );
45 } 41 }
46 42
43 if (automaticDetectionText) {
44 options.push(
45 {
46 value: 'automatic',
47 label: automaticDetectionText,
48 },
49 );
50 }
51
52 options.push({
53 value: '───',
54 label: '───',
55 disabled: true,
56 });
57
47 Object.keys(locales).sort(Intl.Collator().compare).forEach((key) => { 58 Object.keys(locales).sort(Intl.Collator().compare).forEach((key) => {
48 options.push({ 59 options.push({
49 value: key, 60 value: key,
diff --git a/src/i18n/globalMessages.js b/src/i18n/globalMessages.js
index 2c724ff6f..7b1618602 100644
--- a/src/i18n/globalMessages.js
+++ b/src/i18n/globalMessages.js
@@ -13,4 +13,20 @@ export default defineMessages({
13 id: 'global.notConnectedToTheInternet', 13 id: 'global.notConnectedToTheInternet',
14 defaultMessage: '!!!You are not connected to the internet.', 14 defaultMessage: '!!!You are not connected to the internet.',
15 }, 15 },
16 spellcheckerLanguage: {
17 id: 'global.spellchecking.language',
18 defaultMessage: '!!!Spell checking language',
19 },
20 spellcheckerSystemDefault: {
21 id: 'global.spellchecker.useDefault',
22 defaultMessage: '!!!Use System Default ({default})',
23 },
24 spellcheckerAutomaticDetection: {
25 id: 'global.spellchecking.autodetect',
26 defaultMessage: '!!!Detect language automatically',
27 },
28 spellcheckerAutomaticDetectionShort: {
29 id: 'global.spellchecking.autodetect.short',
30 defaultMessage: '!!!Automatic',
31 },
16}); 32});
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 99df6a0ca..4e0c5575d 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -1,6 +1,10 @@
1{ 1{
2 "global.api.unhealthy": "Can't connect to Franz online services", 2 "global.api.unhealthy": "Can't connect to Franz online services",
3 "global.notConnectedToTheInternet": "You are not connected to the internet.", 3 "global.notConnectedToTheInternet": "You are not connected to the internet.",
4 "global.spellchecking.language": "Spell checking language",
5 "global.spellchecker.useDefault": "Use System Default ({default})",
6 "global.spellchecking.autodetect": "Detect language automatically",
7 "global.spellchecking.autodetect.short": "Automatic",
4 "welcome.signupButton": "Create a free account", 8 "welcome.signupButton": "Create a free account",
5 "welcome.loginButton": "Login to your account", 9 "welcome.loginButton": "Login to your account",
6 "welcome.slogan": "Messaging that works for you", 10 "welcome.slogan": "Messaging that works for you",
diff --git a/src/webview/contextMenu.js b/src/webview/contextMenu.js
index a76c03e5a..afb1d8912 100644
--- a/src/webview/contextMenu.js
+++ b/src/webview/contextMenu.js
@@ -207,8 +207,6 @@ const buildMenuTpl = (props, suggestions, isSpellcheckEnabled, defaultSpellcheck
207 }); 207 });
208 }); 208 });
209 209
210 console.log('isSpellcheckEnabled', isSpellcheckEnabled);
211
212 menuTpl.push({ 210 menuTpl.push({
213 type: 'separator', 211 type: 'separator',
214 }, { 212 }, {
@@ -234,6 +232,16 @@ const buildMenuTpl = (props, suggestions, isSpellcheckEnabled, defaultSpellcheck
234 }, 232 },
235 }, 233 },
236 { 234 {
235 id: 'automaticDetection',
236 label: 'Automatic language detection',
237 type: 'radio',
238 checked: spellcheckerLanguage === 'automatic',
239 click() {
240 debug('Detect language automatically');
241 ipcRenderer.sendToHost('set-service-spellchecker-language', 'automatic');
242 },
243 },
244 {
237 type: 'separator', 245 type: 'separator',
238 visible: defaultSpellcheckerLanguage !== spellcheckerLanguage, 246 visible: defaultSpellcheckerLanguage !== spellcheckerLanguage,
239 }, 247 },
diff --git a/src/webview/recipe.js b/src/webview/recipe.js
index c718b348e..1e5d74b1f 100644
--- a/src/webview/recipe.js
+++ b/src/webview/recipe.js
@@ -1,10 +1,12 @@
1import { ipcRenderer } from 'electron'; 1import { ipcRenderer } from 'electron';
2import path from 'path'; 2import path from 'path';
3import { autorun, computed, observable } from 'mobx'; 3import { autorun, computed, observable } from 'mobx';
4import { loadModule } from 'cld3-asm';
5import { debounce } from 'lodash';
4 6
5import RecipeWebview from './lib/RecipeWebview'; 7import RecipeWebview from './lib/RecipeWebview';
6 8
7import spellchecker, { switchDict, disable as disableSpellchecker } from './spellchecker'; 9import spellchecker, { switchDict, disable as disableSpellchecker, getSpellcheckerLocaleByFuzzyIdentifier } from './spellchecker';
8import { injectDarkModeStyle, isDarkModeStyleInjected, removeDarkModeStyle } from './darkmode'; 10import { injectDarkModeStyle, isDarkModeStyleInjected, removeDarkModeStyle } from './darkmode';
9import contextMenu from './contextMenu'; 11import contextMenu from './contextMenu';
10import './notifications'; 12import './notifications';
@@ -40,6 +42,8 @@ class RecipeController {
40 return this.settings.service.spellcheckerLanguage || this.settings.app.spellcheckerLanguage; 42 return this.settings.service.spellcheckerLanguage || this.settings.app.spellcheckerLanguage;
41 } 43 }
42 44
45 cldIdentifier = null;
46
43 async initialize() { 47 async initialize() {
44 Object.keys(this.ipcEvents).forEach((channel) => { 48 Object.keys(this.ipcEvents).forEach((channel) => {
45 ipcRenderer.on(channel, (...args) => { 49 ipcRenderer.on(channel, (...args) => {
@@ -87,10 +91,22 @@ class RecipeController {
87 91
88 if (this.settings.app.enableSpellchecking) { 92 if (this.settings.app.enableSpellchecking) {
89 debug('Setting spellchecker language to', this.spellcheckerLanguage); 93 debug('Setting spellchecker language to', this.spellcheckerLanguage);
90 switchDict(this.spellcheckerLanguage); 94 let { spellcheckerLanguage } = this;
95 if (spellcheckerLanguage === 'automatic') {
96 this.automaticLanguageDetection();
97 debug('Found `automatic` locale, falling back to user locale until detected', this.settings.app.locale);
98 spellcheckerLanguage = this.settings.app.locale;
99 } else if (this.cldIdentifier) {
100 this.cldIdentifier.destroy();
101 }
102 switchDict(spellcheckerLanguage);
91 } else { 103 } else {
92 debug('Disable spellchecker'); 104 debug('Disable spellchecker');
93 disableSpellchecker(); 105 disableSpellchecker();
106
107 if (this.cldIdentifier) {
108 this.cldIdentifier.destroy();
109 }
94 } 110 }
95 111
96 if (this.settings.service.isDarkModeEnabled) { 112 if (this.settings.service.isDarkModeEnabled) {
@@ -113,6 +129,42 @@ class RecipeController {
113 serviceIdEcho(event) { 129 serviceIdEcho(event) {
114 event.sender.send('service-id', this.settings.service.id); 130 event.sender.send('service-id', this.settings.service.id);
115 } 131 }
132
133 async automaticLanguageDetection() {
134 const cldFactory = await loadModule();
135 this.cldIdentifier = cldFactory.create(0, 1000);
136
137 window.addEventListener('keyup', debounce((e) => {
138 const element = e.target;
139
140 console.log(element);
141
142 if (!element) return;
143
144 let value = '';
145 if (element.isContentEditable) {
146 value = element.textContent;
147 } else if (element.value) {
148 value = element.value;
149 }
150
151 // Force a minimum length to get better detection results
152 if (value.length < 30) return;
153
154 debug('Detecting language for', value);
155 const findResult = this.cldIdentifier.findLanguage(value);
156
157 debug('Language detection result', findResult);
158
159 if (findResult.is_reliable) {
160 const spellcheckerLocale = getSpellcheckerLocaleByFuzzyIdentifier(findResult.language);
161 debug('Language detected reliably, setting spellchecker language to', spellcheckerLocale);
162 if (spellcheckerLocale) {
163 switchDict(spellcheckerLocale);
164 }
165 }
166 }, 225));
167 }
116} 168}
117 169
118/* eslint-disable no-new */ 170/* eslint-disable no-new */
diff --git a/src/webview/spellchecker.js b/src/webview/spellchecker.js
index becaed449..9158b3b94 100644
--- a/src/webview/spellchecker.js
+++ b/src/webview/spellchecker.js
@@ -3,6 +3,7 @@ import { SpellCheckerProvider } from 'electron-hunspell';
3import path from 'path'; 3import path from 'path';
4 4
5import { DICTIONARY_PATH } from '../config'; 5import { DICTIONARY_PATH } from '../config';
6import { SPELLCHECKER_LOCALES } from '../i18n/languages';
6 7
7const debug = require('debug')('Franz:spellchecker'); 8const debug = require('debug')('Franz:spellchecker');
8 9
@@ -82,3 +83,13 @@ export function disable() {
82 currentDict = null; 83 currentDict = null;
83 } 84 }
84} 85}
86
87export function getSpellcheckerLocaleByFuzzyIdentifier(identifier) {
88 const locales = Object.keys(SPELLCHECKER_LOCALES).filter(key => key === identifier.toLowerCase() || key.split('-')[0] === identifier.toLowerCase());
89
90 if (locales.length >= 1) {
91 return locales[0];
92 }
93
94 return null;
95}