diff --git a/requirements.txt b/requirements.txt
index 9e6b515fa..87542ca39 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -16,3 +16,4 @@ redis==5.0.8
markdown-it-py==3.0.0
fasttext-predict==0.9.2.2
pytomlpp==1.0.13; python_version < '3.11'
+pyspellchecker>=0.8.1
diff --git a/searx/templates/simple/preferences.html b/searx/templates/simple/preferences.html
index 825a98fe2..3e2111bba 100644
--- a/searx/templates/simple/preferences.html
+++ b/searx/templates/simple/preferences.html
@@ -176,6 +176,9 @@
{% if 'safesearch' not in locked_preferences %}
{%- include 'simple/preferences/safesearch.html' -%}
{%- endif -%}
+ {% if 'spellcheck' not in locked_preferences %}
+ {%- include 'simple/preferences/spellcheck.html' -%}
+ {%- endif -%}
{{- plugin_preferences('general') -}}
{%- if 'doi_resolver' not in locked_preferences %}
{%- include 'simple/preferences/doi_resolver.html' -%}
diff --git a/searx/templates/simple/preferences/spellcheck.html b/searx/templates/simple/preferences/spellcheck.html
new file mode 100644
index 000000000..b84f17642
--- /dev/null
+++ b/searx/templates/simple/preferences/spellcheck.html
@@ -0,0 +1,18 @@
+
{% for result in results %}
{% if result.open_group and not only_template %}
{% endif %}
diff --git a/searx/webapp.py b/searx/webapp.py
index dd79defcb..925e56e92 100755
--- a/searx/webapp.py
+++ b/searx/webapp.py
@@ -21,6 +21,7 @@ from typing import List, Dict, Iterable
import urllib
import urllib.parse
from urllib.parse import urlencode, urlparse, unquote
+from spellchecker import SpellChecker
import httpx
@@ -254,6 +255,17 @@ def code_highlighter(codelines, language=None):
return html_code
+def spellcheck(query: str, user_language: str):
+ spell = SpellChecker(language=user_language)
+ spellcheck_list = query.split(' ')
+ spellcheck_list = [x for x in spellcheck_list if x != '']
+ has_misspelled_words = bool(spell.unknown(spellcheck_list))
+ for word, i in zip(spellcheck_list, range(0, len(spellcheck_list))):
+ if word != '':
+ spellcheck_list[i] = spell.correction(word) if spell.correction(word) is not None else ''
+ return ' '.join(spellcheck_list), has_misspelled_words
+
+
def get_result_template(theme_name: str, template_name: str):
themed_path = theme_name + '/result_templates/' + template_name
if themed_path in result_templates:
@@ -760,6 +772,11 @@ def search():
result_container.corrections,
)
)
+ spellcheck_query, has_misspelled_words = '', False
+ if request.preferences.get_value("spellcheck") == "1" and search.search_query.lang[0:2] in SpellChecker.languages():
+ spellcheck_query, has_misspelled_words = spellcheck(
+ request.form['q'], user_language=search.search_query.lang[0:2]
+ )
# engine_timings: get engine response times sorted from slowest to fastest
engine_timings = sorted(result_container.get_timings(), reverse=True, key=lambda e: e.total)
@@ -774,6 +791,8 @@ def search():
'results.html',
results = results,
q=request.form['q'],
+ spellcheck_query = spellcheck_query,
+ has_misspelled_words = has_misspelled_words,
selected_categories = search_query.categories,
pageno = search_query.pageno,
time_range = search_query.time_range or '',
@@ -1010,6 +1029,7 @@ def preferences():
current_locale = request.preferences.get_value("locale"),
image_proxy = image_proxy,
engines_by_category = engines_by_category,
+ spellcheck = request.preferences.get_value("spellcheck"),
stats = stats,
max_rate95 = max_rate95,
reliabilities = reliabilities,