diff --git a/requirements.txt b/requirements.txt
index 94578abbf..4529f0303 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -19,3 +19,4 @@ tomli==2.0.2; python_version < '3.11'
msgspec==0.18.6
eval_type_backport; python_version < '3.9'
typer-slim==0.12.5
+pyspellchecker>=0.8.1
diff --git a/searx/templates/simple/preferences.html b/searx/templates/simple/preferences.html
index bc96e1198..77de4fc8f 100644
--- a/searx/templates/simple/preferences.html
+++ b/searx/templates/simple/preferences.html
@@ -179,6 +179,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 d2d486d20..41d35cbaa 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
@@ -256,6 +257,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:
@@ -764,6 +776,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)
@@ -778,6 +795,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 '',
@@ -1014,6 +1033,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,