summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.coveragerc20
-rw-r--r--.gitignore18
-rw-r--r--.landscape.yaml3
-rw-r--r--.travis.yml37
-rw-r--r--AUTHORS.rst69
-rw-r--r--CHANGELOG.rst252
-rw-r--r--Dockerfile53
-rw-r--r--LICENSE661
-rw-r--r--README.rst45
-rw-r--r--babel.cfg3
-rw-r--r--examples/basic_engine.py25
-rwxr-xr-xmanage.sh128
-rw-r--r--requirements-dev.txt10
-rw-r--r--requirements.txt10
-rw-r--r--searx/__init__.py89
-rw-r--r--searx/answerers/__init__.py50
-rw-r--r--searx/answerers/random/answerer.py55
-rw-r--r--searx/answerers/statistics/answerer.py55
-rw-r--r--searx/autocomplete.py201
-rw-r--r--searx/data/currencies.json7655
-rw-r--r--searx/data/engines_languages.json1
-rw-r--r--searx/engines/1337x.py39
-rw-r--r--searx/engines/__init__.py221
-rw-r--r--searx/engines/archlinux.py142
-rwxr-xr-xsearx/engines/base.py121
-rw-r--r--searx/engines/bing.py101
-rw-r--r--searx/engines/bing_images.py108
-rw-r--r--searx/engines/bing_news.py127
-rw-r--r--searx/engines/blekko_images.py70
-rw-r--r--searx/engines/btdigg.py92
-rw-r--r--searx/engines/currency_convert.py105
-rw-r--r--searx/engines/dailymotion.py97
-rw-r--r--searx/engines/deezer.py67
-rw-r--r--searx/engines/deviantart.py84
-rw-r--r--searx/engines/dictzone.py68
-rw-r--r--searx/engines/digbt.py62
-rw-r--r--searx/engines/digg.py74
-rw-r--r--searx/engines/doku.py84
-rw-r--r--searx/engines/duckduckgo.py137
-rw-r--r--searx/engines/duckduckgo_definitions.py157
-rw-r--r--searx/engines/duckduckgo_images.py91
-rw-r--r--searx/engines/dummy.py16
-rw-r--r--searx/engines/faroo.py116
-rw-r--r--searx/engines/fdroid.py51
-rw-r--r--searx/engines/filecrop.py88
-rw-r--r--searx/engines/flickr.py90
-rw-r--r--searx/engines/flickr_noapi.py117
-rw-r--r--searx/engines/framalibre.py69
-rw-r--r--searx/engines/frinkiac.py44
-rw-r--r--searx/engines/generalfile.py62
-rw-r--r--searx/engines/gigablast.py106
-rw-r--r--searx/engines/github.py60
-rw-r--r--searx/engines/google.py388
-rw-r--r--searx/engines/google_images.py95
-rw-r--r--searx/engines/google_news.py84
-rw-r--r--searx/engines/ina.py87
-rw-r--r--searx/engines/json_engine.py118
-rw-r--r--searx/engines/kickass.py92
-rw-r--r--searx/engines/mediawiki.py90
-rw-r--r--searx/engines/mixcloud.py61
-rw-r--r--searx/engines/nyaa.py117
-rw-r--r--searx/engines/openstreetmap.py95
-rw-r--r--searx/engines/pdbe.py109
-rw-r--r--searx/engines/photon.py131
-rw-r--r--searx/engines/piratebay.py96
-rw-r--r--searx/engines/qwant.py140
-rw-r--r--searx/engines/reddit.py76
-rw-r--r--searx/engines/scanr_structures.py76
-rw-r--r--searx/engines/searchcode_code.py69
-rw-r--r--searx/engines/searchcode_doc.py49
-rw-r--r--searx/engines/searx_engine.py57
-rw-r--r--searx/engines/seedpeer.py75
-rw-r--r--searx/engines/soundcloud.py104
-rw-r--r--searx/engines/spotify.py62
-rw-r--r--searx/engines/stackoverflow.py57
-rw-r--r--searx/engines/startpage.py123
-rw-r--r--searx/engines/subtitleseeker.py86
-rw-r--r--searx/engines/swisscows.py126
-rw-r--r--searx/engines/tokyotoshokan.py100
-rw-r--r--searx/engines/torrentz.py92
-rw-r--r--searx/engines/translated.py68
-rw-r--r--searx/engines/twitter.py87
-rw-r--r--searx/engines/vimeo.py67
-rw-r--r--searx/engines/wikidata.py488
-rw-r--r--searx/engines/wikipedia.py135
-rw-r--r--searx/engines/wolframalpha_api.py129
-rw-r--r--searx/engines/wolframalpha_noapi.py120
-rw-r--r--searx/engines/www1x.py81
-rw-r--r--searx/engines/www500px.py73
-rw-r--r--searx/engines/xpath.py122
-rw-r--r--searx/engines/yacy.py99
-rw-r--r--searx/engines/yahoo.py153
-rw-r--r--searx/engines/yahoo_news.py107
-rw-r--r--searx/engines/yandex.py64
-rw-r--r--searx/engines/youtube_api.py83
-rw-r--r--searx/engines/youtube_noapi.py89
-rw-r--r--searx/exceptions.py32
-rw-r--r--searx/languages.py77
-rw-r--r--searx/plugins/__init__.py88
-rw-r--r--searx/plugins/doai_rewrite.py31
-rw-r--r--searx/plugins/https_rewrite.py232
-rw-r--r--searx/plugins/https_rules/00README17
-rw-r--r--searx/plugins/https_rules/Bing.xml56
-rw-r--r--searx/plugins/https_rules/Dailymotion.xml69
-rw-r--r--searx/plugins/https_rules/Deviantart.xml53
-rw-r--r--searx/plugins/https_rules/DuckDuckGo.xml38
-rw-r--r--searx/plugins/https_rules/Flickr.xml44
-rw-r--r--searx/plugins/https_rules/Github-Pages.xml11
-rw-r--r--searx/plugins/https_rules/Github.xml94
-rw-r--r--searx/plugins/https_rules/Google-mismatches.xml26
-rw-r--r--searx/plugins/https_rules/Google.org.xml14
-rw-r--r--searx/plugins/https_rules/GoogleAPIs.xml143
-rw-r--r--searx/plugins/https_rules/GoogleCanada.xml6
-rw-r--r--searx/plugins/https_rules/GoogleImages.xml65
-rw-r--r--searx/plugins/https_rules/GoogleMainSearch.xml78
-rw-r--r--searx/plugins/https_rules/GoogleMaps.xml67
-rw-r--r--searx/plugins/https_rules/GoogleMelange.xml6
-rw-r--r--searx/plugins/https_rules/GoogleSearch.xml135
-rw-r--r--searx/plugins/https_rules/GoogleServices.xml345
-rw-r--r--searx/plugins/https_rules/GoogleShopping.xml28
-rw-r--r--searx/plugins/https_rules/GoogleSorry.xml7
-rw-r--r--searx/plugins/https_rules/GoogleTranslate.xml8
-rw-r--r--searx/plugins/https_rules/GoogleVideos.xml83
-rw-r--r--searx/plugins/https_rules/GoogleWatchBlog.xml17
-rw-r--r--searx/plugins/https_rules/Google_App_Engine.xml21
-rw-r--r--searx/plugins/https_rules/Googleplex.com.xml16
-rw-r--r--searx/plugins/https_rules/OpenStreetMap.xml15
-rw-r--r--searx/plugins/https_rules/Rawgithub.com.xml14
-rw-r--r--searx/plugins/https_rules/Soundcloud.xml101
-rw-r--r--searx/plugins/https_rules/ThePirateBay.xml36
-rw-r--r--searx/plugins/https_rules/Torproject.xml18
-rw-r--r--searx/plugins/https_rules/Twitter.xml169
-rw-r--r--searx/plugins/https_rules/Vimeo.xml75
-rw-r--r--searx/plugins/https_rules/WikiLeaks.xml13
-rw-r--r--searx/plugins/https_rules/Wikimedia.xml107
-rw-r--r--searx/plugins/https_rules/Yahoo.xml2450
-rw-r--r--searx/plugins/https_rules/YouTube.xml46
-rw-r--r--searx/plugins/infinite_scroll.py8
-rw-r--r--searx/plugins/open_results_on_new_tab.py24
-rw-r--r--searx/plugins/search_on_category_select.py23
-rw-r--r--searx/plugins/self_info.py46
-rw-r--r--searx/plugins/tracker_url_remover.py44
-rw-r--r--searx/plugins/vim_hotkeys.py10
-rw-r--r--searx/poolrequests.py112
-rw-r--r--searx/preferences.py304
-rw-r--r--searx/query.py162
-rw-r--r--searx/results.py306
-rw-r--r--searx/search.py432
-rw-r--r--searx/settings.yml686
-rw-r--r--searx/settings_robot.yml41
-rw-r--r--searx/static/plugins/css/infinite_scroll.css16
-rw-r--r--searx/static/plugins/css/vim_hotkeys.css26
-rw-r--r--searx/static/plugins/js/infinite_scroll.js18
-rw-r--r--searx/static/plugins/js/open_results_on_new_tab.js3
-rw-r--r--searx/static/plugins/js/search_on_category_select.js24
-rw-r--r--searx/static/plugins/js/vim_hotkeys.js336
-rw-r--r--searx/static/themes/courgette/img/favicon.pngbin0 -> 2039 bytes
-rw-r--r--searx/static/themes/courgette/img/preference-icon.pngbin0 -> 1315 bytes
-rw-r--r--searx/static/themes/courgette/img/search-icon.pngbin0 -> 3270 bytes
-rw-r--r--searx/static/themes/courgette/img/searx-mobile.pngbin0 -> 9415 bytes
-rw-r--r--searx/static/themes/courgette/img/searx.pngbin0 -> 3902 bytes
-rw-r--r--searx/static/themes/courgette/img/searx_logo.svg203
-rw-r--r--searx/static/themes/courgette/js/searx.js45
-rw-r--r--searx/static/themes/courgette/less/style-rtl.less42
-rw-r--r--searx/static/themes/courgette/less/style.less691
-rw-r--r--searx/static/themes/legacy/img/favicon.pngbin0 -> 2039 bytes
-rw-r--r--searx/static/themes/legacy/img/preference-icon.pngbin0 -> 532 bytes
-rw-r--r--searx/static/themes/legacy/img/search-icon.pngbin0 -> 2329 bytes
-rw-r--r--searx/static/themes/legacy/img/searx.pngbin0 -> 3902 bytes
-rw-r--r--searx/static/themes/legacy/img/searx_logo.svg203
-rw-r--r--searx/static/themes/legacy/js/searx.js49
-rw-r--r--searx/static/themes/legacy/less/autocompleter.less61
-rw-r--r--searx/static/themes/legacy/less/code.less83
-rw-r--r--searx/static/themes/legacy/less/definitions.less119
-rw-r--r--searx/static/themes/legacy/less/mixins.less27
-rw-r--r--searx/static/themes/legacy/less/search.less68
-rw-r--r--searx/static/themes/legacy/less/style-rtl.less11
-rw-r--r--searx/static/themes/legacy/less/style.less739
-rw-r--r--searx/static/themes/oscar/.gitignore1
-rw-r--r--searx/static/themes/oscar/README.rst17
-rw-r--r--searx/static/themes/oscar/gruntfile.js90
-rw-r--r--searx/static/themes/oscar/img/favicon.pngbin0 -> 1853 bytes
-rw-r--r--searx/static/themes/oscar/img/icons/README.md2
-rw-r--r--searx/static/themes/oscar/img/loader.gifbin0 -> 8314 bytes
-rw-r--r--searx/static/themes/oscar/img/logo_searx_a.pngbin0 -> 9557 bytes
-rw-r--r--searx/static/themes/oscar/img/logo_searx_a_n.pngbin0 -> 9704 bytes
-rw-r--r--searx/static/themes/oscar/img/map/layers-2x.pngbin0 -> 1763 bytes
-rw-r--r--searx/static/themes/oscar/img/map/layers.pngbin0 -> 1142 bytes
-rw-r--r--searx/static/themes/oscar/img/map/marker-icon-2x-green.pngbin0 -> 3753 bytes
-rw-r--r--searx/static/themes/oscar/img/map/marker-icon-2x-orange.pngbin0 -> 3691 bytes
-rw-r--r--searx/static/themes/oscar/img/map/marker-icon-2x-red.pngbin0 -> 3692 bytes
-rw-r--r--searx/static/themes/oscar/img/map/marker-icon-2x.pngbin0 -> 4033 bytes
-rw-r--r--searx/static/themes/oscar/img/map/marker-icon-green.pngbin0 -> 1696 bytes
-rw-r--r--searx/static/themes/oscar/img/map/marker-icon-orange.pngbin0 -> 1714 bytes
-rw-r--r--searx/static/themes/oscar/img/map/marker-icon-red.pngbin0 -> 1690 bytes
-rw-r--r--searx/static/themes/oscar/img/map/marker-icon.pngbin0 -> 1747 bytes
-rw-r--r--searx/static/themes/oscar/img/map/marker-shadow.pngbin0 -> 797 bytes
-rw-r--r--searx/static/themes/oscar/img/searx_logo.pngbin0 -> 10611 bytes
-rw-r--r--searx/static/themes/oscar/js/searx_src/00_requirejs_config.js23
-rw-r--r--searx/static/themes/oscar/js/searx_src/autocompleter.js37
-rw-r--r--searx/static/themes/oscar/js/searx_src/element_modifiers.js99
-rw-r--r--searx/static/themes/oscar/js/searx_src/leaflet_map.js167
-rw-r--r--searx/static/themes/oscar/less/logicodev/advanced.less49
-rw-r--r--searx/static/themes/oscar/less/logicodev/checkbox.less9
-rw-r--r--searx/static/themes/oscar/less/logicodev/code.less103
-rw-r--r--searx/static/themes/oscar/less/logicodev/cursor.less8
-rw-r--r--searx/static/themes/oscar/less/logicodev/footer.less30
-rw-r--r--searx/static/themes/oscar/less/logicodev/infobox.less37
-rw-r--r--searx/static/themes/oscar/less/logicodev/navbar.less31
-rw-r--r--searx/static/themes/oscar/less/logicodev/onoff.less57
-rw-r--r--searx/static/themes/oscar/less/logicodev/oscar.less21
-rw-r--r--searx/static/themes/oscar/less/logicodev/results.less168
-rw-r--r--searx/static/themes/oscar/less/logicodev/search.less79
-rw-r--r--searx/static/themes/oscar/less/logicodev/variables.less13
-rw-r--r--searx/static/themes/oscar/less/pointhi/advanced.less49
-rw-r--r--searx/static/themes/oscar/less/pointhi/checkbox.less9
-rw-r--r--searx/static/themes/oscar/less/pointhi/code.less79
-rw-r--r--searx/static/themes/oscar/less/pointhi/cursor.less8
-rw-r--r--searx/static/themes/oscar/less/pointhi/footer.less19
-rw-r--r--searx/static/themes/oscar/less/pointhi/infobox.less11
-rw-r--r--searx/static/themes/oscar/less/pointhi/navbar.less20
-rw-r--r--searx/static/themes/oscar/less/pointhi/onoff.less57
-rw-r--r--searx/static/themes/oscar/less/pointhi/oscar.less19
-rw-r--r--searx/static/themes/oscar/less/pointhi/results.less101
-rw-r--r--searx/static/themes/oscar/less/pointhi/search.less32
-rw-r--r--searx/static/themes/oscar/package.json16
-rw-r--r--searx/static/themes/pix-art/img/favicon.pngbin0 -> 2039 bytes
-rw-r--r--searx/static/themes/pix-art/img/preference-icon-pixel.pngbin0 -> 242 bytes
-rw-r--r--searx/static/themes/pix-art/img/search-icon-pixel.pngbin0 -> 204 bytes
-rw-r--r--searx/static/themes/pix-art/img/searx-pixel-small.pngbin0 -> 236 bytes
-rw-r--r--searx/static/themes/pix-art/img/searx-pixel.pngbin0 -> 435 bytes
-rw-r--r--searx/static/themes/pix-art/js/searx.js141
-rw-r--r--searx/static/themes/pix-art/less/definitions.less119
-rw-r--r--searx/static/themes/pix-art/less/mixins.less27
-rw-r--r--searx/static/themes/pix-art/less/search.less57
-rw-r--r--searx/static/themes/pix-art/less/style.less451
-rw-r--r--searx/templates/__common__/about.html62
-rw-r--r--searx/templates/__common__/opensearch.xml28
-rw-r--r--searx/templates/__common__/opensearch_response_rss.xml29
-rw-r--r--searx/templates/courgette/404.html9
-rw-r--r--searx/templates/courgette/about.html5
-rw-r--r--searx/templates/courgette/base.html43
-rw-r--r--searx/templates/courgette/categories.html9
-rw-r--r--searx/templates/courgette/color.css34
-rw-r--r--searx/templates/courgette/github_ribbon.html3
-rw-r--r--searx/templates/courgette/index.html17
-rw-r--r--searx/templates/courgette/preferences.html132
-rw-r--r--searx/templates/courgette/result_templates/code.html11
-rw-r--r--searx/templates/courgette/result_templates/default.html13
-rw-r--r--searx/templates/courgette/result_templates/images.html6
-rw-r--r--searx/templates/courgette/result_templates/map.html13
-rw-r--r--searx/templates/courgette/result_templates/torrent.html13
-rw-r--r--searx/templates/courgette/result_templates/videos.html10
-rw-r--r--searx/templates/courgette/results.html87
-rw-r--r--searx/templates/courgette/search.html7
-rw-r--r--searx/templates/courgette/stats.html22
-rw-r--r--searx/templates/legacy/404.html9
-rw-r--r--searx/templates/legacy/about.html5
-rw-r--r--searx/templates/legacy/base.html38
-rw-r--r--searx/templates/legacy/categories.html10
-rw-r--r--searx/templates/legacy/github_ribbon.html3
-rw-r--r--searx/templates/legacy/index.html18
-rw-r--r--searx/templates/legacy/infobox.html51
-rw-r--r--searx/templates/legacy/preferences.html129
-rw-r--r--searx/templates/legacy/result_templates/code.html11
-rw-r--r--searx/templates/legacy/result_templates/default.html6
-rw-r--r--searx/templates/legacy/result_templates/images.html6
-rw-r--r--searx/templates/legacy/result_templates/map.html13
-rw-r--r--searx/templates/legacy/result_templates/torrent.html13
-rw-r--r--searx/templates/legacy/result_templates/videos.html6
-rw-r--r--searx/templates/legacy/results.html100
-rw-r--r--searx/templates/legacy/search.html8
-rw-r--r--searx/templates/legacy/stats.html22
-rw-r--r--searx/templates/oscar/404.html9
-rw-r--r--searx/templates/oscar/about.html5
-rw-r--r--searx/templates/oscar/advanced.html16
-rw-r--r--searx/templates/oscar/base.html107
-rw-r--r--searx/templates/oscar/categories.html13
-rw-r--r--searx/templates/oscar/index.html22
-rw-r--r--searx/templates/oscar/infobox.html35
-rw-r--r--searx/templates/oscar/languages.html12
-rw-r--r--searx/templates/oscar/macros.html88
-rw-r--r--searx/templates/oscar/messages/first_time.html8
-rw-r--r--searx/templates/oscar/messages/no_cookies.html5
-rw-r--r--searx/templates/oscar/messages/no_data_available.html5
-rw-r--r--searx/templates/oscar/messages/no_results.html9
-rw-r--r--searx/templates/oscar/messages/save_settings_successfull.html9
-rw-r--r--searx/templates/oscar/messages/unknow_error.html9
-rw-r--r--searx/templates/oscar/navbar.html9
-rw-r--r--searx/templates/oscar/preferences.html292
-rw-r--r--searx/templates/oscar/result_templates/code.html18
-rw-r--r--searx/templates/oscar/result_templates/default.html31
-rw-r--r--searx/templates/oscar/result_templates/images.html39
-rw-r--r--searx/templates/oscar/result_templates/map.html72
-rw-r--r--searx/templates/oscar/result_templates/torrent.html25
-rw-r--r--searx/templates/oscar/result_templates/videos.html27
-rw-r--r--searx/templates/oscar/results.html145
-rw-r--r--searx/templates/oscar/search.html24
-rw-r--r--searx/templates/oscar/search_full.html18
-rw-r--r--searx/templates/oscar/stats.html33
-rw-r--r--searx/templates/oscar/time-range.html17
-rw-r--r--searx/templates/pix-art/404.html9
-rw-r--r--searx/templates/pix-art/about.html4
-rw-r--r--searx/templates/pix-art/base.html35
-rw-r--r--searx/templates/pix-art/index.html12
-rw-r--r--searx/templates/pix-art/preferences.html82
-rw-r--r--searx/templates/pix-art/result_templates/default.html7
-rw-r--r--searx/templates/pix-art/result_templates/images.html6
-rw-r--r--searx/templates/pix-art/results.html32
-rw-r--r--searx/templates/pix-art/search.html9
-rw-r--r--searx/templates/pix-art/stats.html22
-rw-r--r--searx/testing.py98
-rw-r--r--searx/translations/bg/LC_MESSAGES/messages.po843
-rw-r--r--searx/translations/cs/LC_MESSAGES/messages.po842
-rw-r--r--searx/translations/de/LC_MESSAGES/messages.po852
-rw-r--r--searx/translations/de_DE/LC_MESSAGES/messages.po844
-rw-r--r--searx/translations/el_GR/LC_MESSAGES/messages.po842
-rw-r--r--searx/translations/en/LC_MESSAGES/messages.po695
-rw-r--r--searx/translations/eo/LC_MESSAGES/messages.po844
-rw-r--r--searx/translations/es/LC_MESSAGES/messages.po850
-rw-r--r--searx/translations/fi/LC_MESSAGES/messages.po842
-rw-r--r--searx/translations/fr/LC_MESSAGES/messages.po849
-rw-r--r--searx/translations/he/LC_MESSAGES/messages.po846
-rw-r--r--searx/translations/hu/LC_MESSAGES/messages.po845
-rw-r--r--searx/translations/it/LC_MESSAGES/messages.po846
-rw-r--r--searx/translations/ja/LC_MESSAGES/messages.po848
-rw-r--r--searx/translations/nl/LC_MESSAGES/messages.po844
-rw-r--r--searx/translations/pt/LC_MESSAGES/messages.po842
-rw-r--r--searx/translations/pt_BR/LC_MESSAGES/messages.po844
-rw-r--r--searx/translations/ro/LC_MESSAGES/messages.po842
-rw-r--r--searx/translations/ru/LC_MESSAGES/messages.po845
-rw-r--r--searx/translations/sk/LC_MESSAGES/messages.po842
-rw-r--r--searx/translations/sv/LC_MESSAGES/messages.po842
-rw-r--r--searx/translations/tr/LC_MESSAGES/messages.po843
-rw-r--r--searx/translations/uk/LC_MESSAGES/messages.po843
-rw-r--r--searx/translations/zh_CN/LC_MESSAGES/messages.po843
-rw-r--r--searx/url_utils.py28
-rw-r--r--searx/utils.py314
-rw-r--r--searx/version.py26
-rw-r--r--searx/webapp.py886
-rw-r--r--setup.py74
-rw-r--r--tests/__init__.py0
-rw-r--r--tests/robot/__init__.py75
-rw-r--r--tests/test_robot.py23
-rw-r--r--tests/unit/__init__.py0
-rw-r--r--tests/unit/engines/__init__.py0
-rw-r--r--tests/unit/engines/seedpeer_fixture.html110
-rw-r--r--tests/unit/engines/test_archlinux.py106
-rw-r--r--tests/unit/engines/test_bing.py120
-rw-r--r--tests/unit/engines/test_bing_images.py84
-rw-r--r--tests/unit/engines/test_bing_news.py146
-rw-r--r--tests/unit/engines/test_blekko_images.py71
-rw-r--r--tests/unit/engines/test_btdigg.py384
-rw-r--r--tests/unit/engines/test_currency_convert.py43
-rw-r--r--tests/unit/engines/test_dailymotion.py111
-rw-r--r--tests/unit/engines/test_deezer.py57
-rw-r--r--tests/unit/engines/test_deviantart.py95
-rw-r--r--tests/unit/engines/test_digbt.py61
-rw-r--r--tests/unit/engines/test_digg.py101
-rw-r--r--tests/unit/engines/test_doku.py79
-rw-r--r--tests/unit/engines/test_duckduckgo.py100
-rw-r--r--tests/unit/engines/test_duckduckgo_definitions.py254
-rw-r--r--tests/unit/engines/test_duckduckgo_images.py72
-rw-r--r--tests/unit/engines/test_dummy.py26
-rw-r--r--tests/unit/engines/test_faroo.py116
-rw-r--r--tests/unit/engines/test_fdroid.py49
-rw-r--r--tests/unit/engines/test_flickr.py142
-rw-r--r--tests/unit/engines/test_flickr_noapi.py329
-rw-r--r--tests/unit/engines/test_framalibre.py103
-rw-r--r--tests/unit/engines/test_frinkiac.py50
-rw-r--r--tests/unit/engines/test_gigablast.py119
-rw-r--r--tests/unit/engines/test_github.py61
-rw-r--r--tests/unit/engines/test_google.py236
-rw-r--r--tests/unit/engines/test_google_images.py42
-rw-r--r--tests/unit/engines/test_google_news.py50
-rw-r--r--tests/unit/engines/test_ina.py64
-rw-r--r--tests/unit/engines/test_kickass.py397
-rw-r--r--tests/unit/engines/test_mediawiki.py130
-rw-r--r--tests/unit/engines/test_mixcloud.py67
-rw-r--r--tests/unit/engines/test_nyaa.py66
-rw-r--r--tests/unit/engines/test_openstreetmap.py199
-rw-r--r--tests/unit/engines/test_pdbe.py109
-rw-r--r--tests/unit/engines/test_photon.py166
-rw-r--r--tests/unit/engines/test_piratebay.py166
-rw-r--r--tests/unit/engines/test_qwant.py338
-rw-r--r--tests/unit/engines/test_reddit.py71
-rw-r--r--tests/unit/engines/test_scanr_structures.py175
-rw-r--r--tests/unit/engines/test_searchcode_code.py75
-rw-r--r--tests/unit/engines/test_searchcode_doc.py70
-rw-r--r--tests/unit/engines/test_seedpeer.py51
-rw-r--r--tests/unit/engines/test_soundcloud.py192
-rw-r--r--tests/unit/engines/test_spotify.py124
-rw-r--r--tests/unit/engines/test_stackoverflow.py106
-rw-r--r--tests/unit/engines/test_startpage.py140
-rw-r--r--tests/unit/engines/test_subtitleseeker.py174
-rw-r--r--tests/unit/engines/test_swisscows.py155
-rw-r--r--tests/unit/engines/test_tokyotoshokan.py110
-rw-r--r--tests/unit/engines/test_torrentz.py91
-rw-r--r--tests/unit/engines/test_twitter.py502
-rw-r--r--tests/unit/engines/test_vimeo.py36
-rw-r--r--tests/unit/engines/test_wikidata.py503
-rw-r--r--tests/unit/engines/test_wikipedia.py259
-rw-r--r--tests/unit/engines/test_wolframalpha_api.py166
-rw-r--r--tests/unit/engines/test_wolframalpha_noapi.py224
-rw-r--r--tests/unit/engines/test_www1x.py57
-rw-r--r--tests/unit/engines/test_www500px.py34
-rw-r--r--tests/unit/engines/test_yacy.py96
-rw-r--r--tests/unit/engines/test_yahoo.py179
-rw-r--r--tests/unit/engines/test_yahoo_news.py149
-rw-r--r--tests/unit/engines/test_youtube_api.py111
-rw-r--r--tests/unit/engines/test_youtube_noapi.py174
-rw-r--r--tests/unit/test_answerers.py16
-rw-r--r--tests/unit/test_plugins.py85
-rw-r--r--tests/unit/test_preferences.py124
-rw-r--r--tests/unit/test_results.py41
-rw-r--r--tests/unit/test_search.py10
-rw-r--r--tests/unit/test_utils.py105
-rw-r--r--tests/unit/test_webapp.py158
-rw-r--r--tox.ini2
-rw-r--r--utils/fabfile.py117
-rw-r--r--utils/fetch_currencies.py161
-rw-r--r--utils/fetch_languages.py189
-rw-r--r--utils/google_search.py35
-rwxr-xr-xutils/standalone_searx.py101
-rwxr-xr-xutils/update-translations.sh15
425 files changed, 63460 insertions, 0 deletions
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..4f50efc
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,20 @@
+[run]
+branch = True
+source =
+ searx/engines
+ searx/__init__.py
+ searx/autocomplete.py
+ searx/https_rewrite.py
+ searx/languages.py
+ searx/search.py
+ searx/testing.py
+ searx/utils.py
+ searx/webapp.py
+
+[report]
+show_missing = True
+exclude_lines =
+ if __name__ == .__main__.:
+
+[html]
+directory = coverage
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cd6d7e9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+.coverage
+coverage/
+.installed.cfg
+engines.cfg
+env
+robot_log.html
+robot_output.xml
+robot_report.html
+test_basic/
+setup.cfg
+
+*.pyc
+*/*.pyc
+*~
+
+node_modules/
+
+.tx/
diff --git a/.landscape.yaml b/.landscape.yaml
new file mode 100644
index 0000000..1bb3977
--- /dev/null
+++ b/.landscape.yaml
@@ -0,0 +1,3 @@
+strictness: high
+ignore-paths:
+ - bootstrap.py
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..b6017cd
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,37 @@
+sudo: false
+cache:
+ - pip
+ - npm
+ - directories:
+ - $HOME/.cache/pip
+addons:
+ firefox: "latest"
+language: python
+python:
+ - "2.7"
+ - "3.6"
+before_install:
+ - "export DISPLAY=:99.0"
+ - "sh -e /etc/init.d/xvfb start"
+ - npm install less grunt-cli
+ - ( cd searx/static/themes/oscar;npm install; cd - )
+ - mkdir -p ~/drivers; export PATH=~/drivers:$PATH;
+ - GECKODRIVER_URL="https://github.com/mozilla/geckodriver/releases/download/v0.11.1/geckodriver-v0.11.1-linux64.tar.gz";
+ - FILE=`mktemp`; wget "$GECKODRIVER_URL" -qO $FILE && tar xz -C ~/drivers -f $FILE geckodriver; rm $FILE; chmod 777 ~/drivers/geckodriver;
+install:
+ - ./manage.sh update_dev_packages
+ - pip install coveralls
+script:
+ - ./manage.sh styles
+ - ./manage.sh grunt_build
+ - ./manage.sh tests
+after_success:
+ - ./manage.sh py_test_coverage
+ - coveralls
+notifications:
+ irc:
+ channels:
+ - "irc.freenode.org#searx"
+ template:
+ - "%{repository}/#%{build_number}/%{branch} (%{author}): %{message} %{build_url}"
+ on_success: change
diff --git a/AUTHORS.rst b/AUTHORS.rst
new file mode 100644
index 0000000..9d27dd8
--- /dev/null
+++ b/AUTHORS.rst
@@ -0,0 +1,69 @@
+Searx was created by Adam Tauber and is maintained by Adam Tauber, Alexandre Flament and Noémi Ványi.
+
+Major contributing authors:
+
+- Adam Tauber <asciimoo@gmail.com> `@asciimoo <https://github.com/asciimoo>`_
+- Matej Cotman
+- Thomas Pointhuber
+- Alexandre Flament `@dalf <https://github.com/dalf>`_
+- @Cqoicebordel
+- Noémi Ványi
+- Marc Abonce Seguin @a01200356
+
+People who have submitted patches/translates, reported bugs, consulted features or
+generally made searx better:
+
+- Laszlo Hammerl
+- Stefan Marsiske
+- Gabor Nagy
+- @pw3t
+- @rhapsodhy
+- András Veres-Szentkirályi
+- Benjamin Sonntag
+- @HLFH
+- @TheRadialActive
+- @Okhin
+- André Koot
+- Alejandro León Aznar
+- rike
+- dp
+- Martin Zimmermann
+- @courgette
+- @kernc
+- @Reventl0v
+- Caner Başaran
+- Benjamin Sonntag
+- @opi
+- @dimqua
+- Giorgos Logiotatidis
+- Luc Didry
+- Niklas Haas
+- @underr
+- Emmanuel Benazera
+- @GreenLunar
+- Kang-min Liu
+- Kirill Isakov
+- Guilhem Bonnefille
+- @jibe-b
+- Christian Pietsch @pietsch
+- @Maxqia
+- Ashutosh Das @pyprism
+- YuLun Shih @imZack
+- Dmitry Mikhirev @mikhirev
+- David A Roberts `@davidar <https://github.com/davidar>`_
+- Jan Verbeek @blyxxyz
+- Ammar Najjar @ammarnajjar
+- @stepshal
+- François Revol @mmuman
+- Harry Wood @harry-wood
+- Thomas Renard @threnard
+- Pydo `<https://github.com/pydo>`_
+- Athemis `<https://github.com/Athemis>`_
+- Stefan Antoni `<http://stefan.antoni.io>`
+- @firebovine
+- Lorenzo J. Lucchini @luccoj
+- @eig8phei
+- Joachim Cherqui
+- @maxigas
+- Jannik Winkel @kiney
+- @juanitobananas
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
new file mode 100644
index 0000000..7cfa62f
--- /dev/null
+++ b/CHANGELOG.rst
@@ -0,0 +1,252 @@
+0.12.0 2017.06.04
+=================
+
+- Python3 compatibility
+- New engines
+
+ - 1337x.to (files, music, video)
+ - Semantic Scholar (science)
+ - Library Genesis (general)
+ - Framalibre (IT)
+ - Free Software Directory (IT)
+- More compact result UI (oscar theme)
+- Configurable static content and template path
+- Spelling suggestions
+- Multiple engine fixes (duckduckgo, bing, swisscows, yahoo news, bing news, twitter, bing images)
+- Reduced static image size
+- Docker updates
+- Translation updates
+
+
+Special thanks to `NLNet <https://nlnet.nl>`__ for sponsoring multiple features of this release.
+
+
+0.11.0 2017.01.10
+=================
+
+- New engines
+
+ - Protein Data Bank Europe (science)
+ - Voat.co (general, social media)
+ - Online Etimology Dictionary (science)
+ - CCC tv (video, it)
+ - Searx (all categories - can rotate multiple other instances)
+- Answerer functionality (see answerer section on /preferences)
+- Local answerers
+
+ - Statistical functions
+ - Random value generator
+- Result proxy support (with `morty <https://github.com/asciimoo/morty>`__)
+- Extended time range filter
+- Improved search language support
+- Multiple engine fixes (digbt, 500px, google news, ixquick, bing, kickass, google play movies, habrahabr, yandex)
+- Minor UI improvements
+- Suggestion support for JSON engine
+- Result and query escaping fixes
+- Configurable HTTP server version
+- More robust search error handling
+- Faster webapp initialization in debug mode
+- Search module refactor
+- Translation updates
+
+
+0.10.0 2016.09.06
+=================
+
+- New engines
+
+ - Archive.is (general)
+ - INA (videos)
+ - Scanr (science)
+ - Google Scholar (science)
+ - Crossref (science)
+ - Openrepos (files)
+ - Microsoft Academic Search Engine (science)
+ - Hoogle (it)
+ - Diggbt (files)
+ - Dictzone (general - dictionary)
+ - Translated (general - translation)
+- New Plugins
+
+ - Infinite scroll on results page
+ - DOAI rewrite
+- Full theme redesign
+- Display the number of results
+- Filter searches by date range
+- Instance config API endpoint
+- Dependency version updates
+- Socks proxy support for outgoing requests
+- 404 page
+
+
+News
+~~~~
+
+@kvch joined the maintainer team
+
+
+0.9.0 2016.05.24
+================
+
+- New search category: science
+- New engines
+
+ - Wolframalpha (science)
+ - Frinkiac (images)
+ - Arch Linux (it)
+ - BASE - Bielefeld Academic Search Engine (science)
+ - Dokuwiki (general)
+ - Nyaa.se (files, images, music, video)
+ - Reddit (general, images, news, social media)
+ - Torrentz.eu (files, music, video)
+ - Tokyo Toshokan (files, music, video)
+ - F-Droid (files)
+ - Erowid (general)
+ - Bitbucket (it)
+ - GitLab (it)
+ - Geektimes (it)
+ - Habrahabr (it)
+- New plugins
+
+ - Open links in new tab
+ - Vim hotkeys for better navigation
+- Wikipedia/Mediawiki engine improvements
+- Configurable instance name
+- Configurable connection pool size
+- Fixed broken google engine
+- Better docker image
+- Images in standard results
+- Fixed and refactored user settings (Warning: backward incompatibility - you have to reset your custom engine preferences)
+- Suspending engines on errors
+- Simplified development/deployment tooling
+- Translation updates
+- Multilingual autocompleter
+- Qwant autocompleter backend
+
+
+0.8.1 2015.12.22
+================
+
+- More efficient result parsing
+- Rewritten google engine to prevent app crashes
+- Other engine fixes/tweaks
+
+ - Bing news
+ - Btdigg
+ - Gigablast
+ - Google images
+ - Startpage
+
+
+News
+~~~~
+
+New documentation page is available: https://asciimoo.github.io/searx
+
+
+0.8.0 2015.09.08
+================
+
+- New engines
+
+ - Blekko (image)
+ - Gigablast (general)
+ - Spotify (music)
+ - Swisscows (general, images)
+ - Qwant (general, images, news, social media)
+- Plugin system
+- New plugins
+
+ - HTTPS rewrite
+ - Search on cagetory select
+ - User information
+ - Tracker url part remover
+- Multiple outgoing IP and HTTP/HTTPS proxy support
+- New autocompleter: startpage
+- New theme: pix-art
+- Settings file structure change
+- Fabfile, docker deployment
+- Optional safesearch result filter
+- Force HTTPS in engines if possible
+- Disabled HTTP referrer on outgoing links
+- Display cookie information
+- Prettier search URLs
+- Right-to-left text handling in themes
+- Translation updates (New locales: Chinese, Hebrew, Portuguese, Romanian)
+
+
+New dependencies
+~~~~~~~~~~~~~~~~
+
+- pyopenssl
+- ndg-httpsclient
+- pyasn1
+- pyasn1-modules
+- certifi
+
+
+News
+~~~~
+
+@dalf joined the maintainer "team"
+
+
+0.7.0 2015.02.03
+================
+
+- New engines
+
+ - Digg
+ - Google Play Store
+ - Deezer
+ - Btdigg
+ - Mixcloud
+ - 1px
+- Image proxy
+- Search speed improvements
+- Autocompletition of engines, shortcuts and supported languages
+- Translation updates (New locales: Turkish, Russian)
+- Default theme changed to oscar
+- Settings option to disable engines by default
+- UI code cleanup and restructure
+- Engine tests
+- Multiple engine bug fixes and tweaks
+- Config option to set default interface locale
+- Flexible result template handling
+- Application logging and sophisticated engine exception tracebacks
+- Kickass torrent size display (oscar theme)
+
+
+New dependencies
+~~~~~~~~~~~~~~~~
+
+- pygments - http://pygments.org/
+
+
+0.6.0 - 2014.12.25
+==================
+
+- Changelog added
+- New engines
+
+ - Flickr (api)
+ - Subtitleseeker
+ - photon
+ - 500px
+ - Searchcode
+ - Searchcode doc
+ - Kickass torrent
+- Precise search request timeout handling
+- Better favicon support
+- Stricter config parsing
+- Translation updates
+- Multiple ui fixes
+- Flickr (noapi) engine fix
+- Pep8 fixes
+
+
+News
+~~~~
+
+Health status of searx instances and engines: http://stats.searx.oe5tpo.com
+(source: https://github.com/pointhi/searx_stats)
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..fc2767a
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,53 @@
+FROM alpine:3.5
+MAINTAINER searx <https://github.com/asciimoo/searx>
+LABEL description "A privacy-respecting, hackable metasearch engine."
+
+ENV BASE_URL=False IMAGE_PROXY=False
+EXPOSE 8888
+WORKDIR /usr/local/searx
+CMD ["/sbin/tini","--","/usr/local/searx/run.sh"]
+
+RUN adduser -D -h /usr/local/searx -s /bin/sh searx searx \
+ && echo '#!/bin/sh' >> run.sh \
+ && echo 'sed -i "s|base_url : False|base_url : $BASE_URL|g" searx/settings.yml' >> run.sh \
+ && echo 'sed -i "s/image_proxy : False/image_proxy : $IMAGE_PROXY/g" searx/settings.yml' >> run.sh \
+ && echo 'sed -i "s/ultrasecretkey/`openssl rand -hex 16`/g" searx/settings.yml' >> run.sh \
+ && echo 'python searx/webapp.py' >> run.sh \
+ && chmod +x run.sh
+
+COPY requirements.txt ./requirements.txt
+
+RUN echo "@commuedge http://nl.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories \
+ && apk -U add \
+ build-base \
+ python \
+ python-dev \
+ py-pip \
+ libxml2 \
+ libxml2-dev \
+ libxslt \
+ libxslt-dev \
+ libffi-dev \
+ openssl \
+ openssl-dev \
+ ca-certificates \
+ tini@commuedge \
+ && pip install --no-cache -r requirements.txt \
+ && apk del \
+ build-base \
+ python-dev \
+ libffi-dev \
+ openssl-dev \
+ libxslt-dev \
+ libxml2-dev \
+ openssl-dev \
+ ca-certificates \
+ && rm -f /var/cache/apk/*
+
+COPY . .
+
+RUN chown -R searx:searx *
+
+USER searx
+
+RUN sed -i "s/127.0.0.1/0.0.0.0/g" searx/settings.yml
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..dba13ed
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..a0bb12f
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,45 @@
+searx
+=====
+
+A privacy-respecting, hackable `metasearch
+engine <https://en.wikipedia.org/wiki/Metasearch_engine>`__.
+
+List of `running
+instances <https://github.com/asciimoo/searx/wiki/Searx-instances>`__.
+
+See the `documentation <https://asciimoo.github.io/searx>`__ and the `wiki <https://github.com/asciimoo/searx/wiki>`__ for more information.
+
+|Flattr searx|
+
+Installation
+~~~~~~~~~~~~
+
+- clone source:
+ ``git clone https://github.com/asciimoo/searx.git && cd searx``
+- install dependencies: ``./manage.sh update_packages``
+- edit your
+ `settings.yml <https://github.com/asciimoo/searx/blob/master/searx/settings.yml>`__
+ (set your ``secret_key``!)
+- run ``python searx/webapp.py`` to start the application
+
+For all the details, follow this `step by step
+installation <https://github.com/asciimoo/searx/wiki/Installation>`__
+
+Bugs
+~~~~
+
+Bugs or suggestions? Visit the `issue
+tracker <https://github.com/asciimoo/searx/issues>`__.
+
+`License <https://github.com/asciimoo/searx/blob/master/LICENSE>`__
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+More about searx
+~~~~~~~~~~~~~~~~
+
+- `ohloh <https://www.ohloh.net/p/searx/>`__
+- `twitter <https://twitter.com/Searx_engine>`__
+- IRC: #searx @ freenode
+
+.. |Flattr searx| image:: http://api.flattr.com/button/flattr-badge-large.png
+ :target: https://flattr.com/submit/auto?user_id=asciimoo&url=https://github.com/asciimoo/searx&title=searx&language=&tags=github&category=software
diff --git a/babel.cfg b/babel.cfg
new file mode 100644
index 0000000..f0234b3
--- /dev/null
+++ b/babel.cfg
@@ -0,0 +1,3 @@
+[python: **.py]
+[jinja2: **/templates/**.html]
+extensions=jinja2.ext.autoescape,jinja2.ext.with_
diff --git a/examples/basic_engine.py b/examples/basic_engine.py
new file mode 100644
index 0000000..c7d02af
--- /dev/null
+++ b/examples/basic_engine.py
@@ -0,0 +1,25 @@
+
+categories = ['general'] # optional
+
+
+def request(query, params):
+ '''pre-request callback
+ params<dict>:
+ method : POST/GET
+ headers : {}
+ data : {} # if method == POST
+ url : ''
+ category: 'search category'
+ pageno : 1 # number of the requested page
+ '''
+
+ params['url'] = 'https://host/%s' % query
+
+ return params
+
+
+def response(resp):
+ '''post-response callback
+ resp: requests response object
+ '''
+ return [{'url': '', 'title': '', 'content': ''}]
diff --git a/manage.sh b/manage.sh
new file mode 100755
index 0000000..2466f25
--- /dev/null
+++ b/manage.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+
+BASE_DIR=$(dirname "`readlink -f "$0"`")
+PYTHONPATH=$BASE_DIR
+SEARX_DIR="$BASE_DIR/searx"
+ACTION=$1
+
+update_packages() {
+ pip install -r "$BASE_DIR/requirements.txt"
+}
+
+update_dev_packages() {
+ update_packages
+ pip install -r "$BASE_DIR/requirements-dev.txt"
+}
+
+check_geckodriver() {
+ echo '[!] Checking geckodriver'
+ set -e
+ geckodriver -V 2>1 > /dev/null || NOTFOUND=1
+ set +e
+ if [ -z $NOTFOUND ]; then
+ return
+ fi
+ GECKODRIVER_VERSION="v0.11.1"
+ PLATFORM=`python -c "import platform; print platform.system().lower(), platform.architecture()[0]"`
+ case $PLATFORM in
+ "linux 32bit" | "linux2 32bit") ARCH="linux32";;
+ "linux 64bit" | "linux2 64bit") ARCH="linux64";;
+ "windows 32 bit") ARCH="win32";;
+ "windows 64 bit") ARCH="win64";;
+ "mac 64bit") ARCH="macos";;
+ esac
+ GECKODRIVER_URL="https://github.com/mozilla/geckodriver/releases/download/$GECKODRIVER_VERSION/geckodriver-$GECKODRIVER_VERSION-$ARCH.tar.gz";
+ if [ -z "$VIRTUAL_ENV" ]; then
+ echo "geckodriver can't be installed because VIRTUAL_ENV is not set, you should download it from\n $GECKODRIVER_URL"
+ exit
+ else
+ echo "Installing $VIRTUAL_ENV from\n $GECKODRIVER_URL"
+ FILE=`mktemp`
+ wget "$GECKODRIVER_URL" -qO $FILE && tar xz -C $VIRTUAL_ENV/bin/ -f $FILE geckodriver
+ rm $FILE
+ chmod 777 $VIRTUAL_ENV/bin/geckodriver
+ fi
+}
+
+pep8_check() {
+ echo '[!] Running pep8 check'
+ # ignored rules:
+ # E402 module level import not at top of file
+ # W503 line break before binary operator
+ pep8 --max-line-length=120 --ignore "E402,W503" "$SEARX_DIR" "$BASE_DIR/tests"
+}
+
+unit_tests() {
+ echo '[!] Running unit tests'
+ python -m nose2 -s "$BASE_DIR/tests/unit"
+}
+
+py_test_coverage() {
+ echo '[!] Running python test coverage'
+ PYTHONPATH=`pwd` python -m nose2 -C --coverage "$SEARX_DIR" -s "$BASE_DIR/tests/unit"
+ coverage report
+ coverage html
+}
+
+robot_tests() {
+ echo '[!] Running robot tests'
+ PYTHONPATH=`pwd` python "$SEARX_DIR/testing.py" robot
+}
+
+tests() {
+ set -e
+ pep8_check
+ unit_tests
+ check_geckodriver
+ robot_tests
+ set +e
+}
+
+build_style() {
+ lessc -x "$BASE_DIR/searx/static/$1" "$BASE_DIR/searx/static/$2"
+}
+
+styles() {
+ echo '[!] Building styles'
+ build_style themes/legacy/less/style.less themes/legacy/css/style.css
+ build_style themes/legacy/less/style-rtl.less themes/legacy/css/style-rtl.css
+ build_style themes/courgette/less/style.less themes/courgette/css/style.css
+ build_style themes/courgette/less/style-rtl.less themes/courgette/css/style-rtl.css
+ build_style less/bootstrap/bootstrap.less css/bootstrap.min.css
+ build_style themes/oscar/less/pointhi/oscar.less themes/oscar/css/pointhi.min.css
+ build_style themes/oscar/less/logicodev/oscar.less themes/oscar/css/logicodev.min.css
+ build_style themes/pix-art/less/style.less themes/pix-art/css/style.css
+}
+
+grunt_build() {
+ grunt --gruntfile "$SEARX_DIR/static/themes/oscar/gruntfile.js"
+}
+
+locales() {
+ pybabel compile -d "$SEARX_DIR/translations"
+}
+
+help() {
+ [ -z "$1" ] || printf "Error: $1\n"
+ echo "Searx manage.sh help
+
+Commands
+========
+ grunt_build - Build js files
+ help - This text
+ locales - Compile locales
+ pep8_check - Pep8 validation
+ py_test_coverage - Unit test coverage
+ robot_tests - Run selenium tests
+ styles - Build less files
+ tests - Run all python tests (pep8, unit, robot)
+ unit_tests - Run unit tests
+ update_dev_packages - Check & update development and production dependency changes
+ update_packages - Check & update dependency changes
+ check_geckodriver - Check & download geckodriver (required for robot_tests)
+"
+}
+
+[ "$(command -V "$ACTION" | grep ' function$')" = "" ] \
+ && help "action not found" \
+ || $ACTION
diff --git a/requirements-dev.txt b/requirements-dev.txt
new file mode 100644
index 0000000..691a1e7
--- /dev/null
+++ b/requirements-dev.txt
@@ -0,0 +1,10 @@
+babel==2.3.4
+mock==2.0.0
+nose2[coverage-plugin]
+pep8==1.7.0
+plone.testing==5.0.0
+splinter==0.7.5
+transifex-client==0.12.2
+unittest2==1.1.0
+zope.testrunner==4.5.1
+selenium==3.0.1
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..292b6af
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,10 @@
+certifi==2017.1.23
+flask==0.12
+flask-babel==0.11.1
+lxml==3.7.3
+idna==2.5
+pygments==2.1.3
+pyopenssl==16.2.0
+python-dateutil==2.6.0
+pyyaml==3.12
+requests[socks]==2.13.0
diff --git a/searx/__init__.py b/searx/__init__.py
new file mode 100644
index 0000000..a57588c
--- /dev/null
+++ b/searx/__init__.py
@@ -0,0 +1,89 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
+'''
+
+import certifi
+import logging
+from os import environ
+from os.path import realpath, dirname, join, abspath, isfile
+from ssl import OPENSSL_VERSION_INFO, OPENSSL_VERSION
+try:
+ from yaml import load
+except:
+ from sys import exit, stderr
+ stderr.write('[E] install pyyaml\n')
+ exit(2)
+
+searx_dir = abspath(dirname(__file__))
+engine_dir = dirname(realpath(__file__))
+
+
+def check_settings_yml(file_name):
+ if isfile(file_name):
+ return file_name
+ else:
+ return None
+
+# find location of settings.yml
+if 'SEARX_SETTINGS_PATH' in environ:
+ # if possible set path to settings using the
+ # enviroment variable SEARX_SETTINGS_PATH
+ settings_path = check_settings_yml(environ['SEARX_SETTINGS_PATH'])
+else:
+ # if not, get it from searx code base or last solution from /etc/searx
+ settings_path = check_settings_yml(join(searx_dir, 'settings.yml')) or check_settings_yml('/etc/searx/settings.yml')
+
+if not settings_path:
+ raise Exception('settings.yml not found')
+
+# load settings
+with open(settings_path) as settings_yaml:
+ settings = load(settings_yaml)
+
+'''
+enable debug if
+the environnement variable SEARX_DEBUG is 1 or true
+(whatever the value in settings.yml)
+or general.debug=True in settings.yml
+
+disable debug if
+the environnement variable SEARX_DEBUG is 0 or false
+(whatever the value in settings.yml)
+or general.debug=False in settings.yml
+'''
+searx_debug_env = environ.get('SEARX_DEBUG', '').lower()
+if searx_debug_env == 'true' or searx_debug_env == '1':
+ searx_debug = True
+elif searx_debug_env == 'false' or searx_debug_env == '0':
+ searx_debug = False
+else:
+ searx_debug = settings.get('general', {}).get('debug')
+
+if searx_debug:
+ logging.basicConfig(level=logging.DEBUG)
+else:
+ logging.basicConfig(level=logging.WARNING)
+
+logger = logging.getLogger('searx')
+logger.debug('read configuration from %s', settings_path)
+# Workaround for openssl versions <1.0.2
+# https://github.com/certifi/python-certifi/issues/26
+if OPENSSL_VERSION_INFO[0:3] < (1, 0, 2):
+ if hasattr(certifi, 'old_where'):
+ environ['REQUESTS_CA_BUNDLE'] = certifi.old_where()
+ logger.warning('You are using an old openssl version({0}), please upgrade above 1.0.2!'.format(OPENSSL_VERSION))
+
+logger.info('Initialisation done')
diff --git a/searx/answerers/__init__.py b/searx/answerers/__init__.py
new file mode 100644
index 0000000..444316f
--- /dev/null
+++ b/searx/answerers/__init__.py
@@ -0,0 +1,50 @@
+from os import listdir
+from os.path import realpath, dirname, join, isdir
+from sys import version_info
+from searx.utils import load_module
+from collections import defaultdict
+
+if version_info[0] == 3:
+ unicode = str
+
+
+answerers_dir = dirname(realpath(__file__))
+
+
+def load_answerers():
+ answerers = []
+ for filename in listdir(answerers_dir):
+ if not isdir(join(answerers_dir, filename)) or filename.startswith('_'):
+ continue
+ module = load_module('answerer.py', join(answerers_dir, filename))
+ if not hasattr(module, 'keywords') or not isinstance(module.keywords, tuple) or not len(module.keywords):
+ exit(2)
+ answerers.append(module)
+ return answerers
+
+
+def get_answerers_by_keywords(answerers):
+ by_keyword = defaultdict(list)
+ for answerer in answerers:
+ for keyword in answerer.keywords:
+ for keyword in answerer.keywords:
+ by_keyword[keyword].append(answerer.answer)
+ return by_keyword
+
+
+def ask(query):
+ results = []
+ query_parts = list(filter(None, query.query.split()))
+
+ if query_parts[0].decode('utf-8') not in answerers_by_keywords:
+ return results
+
+ for answerer in answerers_by_keywords[query_parts[0].decode('utf-8')]:
+ result = answerer(query)
+ if result:
+ results.append(result)
+ return results
+
+
+answerers = load_answerers()
+answerers_by_keywords = get_answerers_by_keywords(answerers)
diff --git a/searx/answerers/random/answerer.py b/searx/answerers/random/answerer.py
new file mode 100644
index 0000000..f2b8bf3
--- /dev/null
+++ b/searx/answerers/random/answerer.py
@@ -0,0 +1,55 @@
+import random
+import string
+import sys
+from flask_babel import gettext
+
+# required answerer attribute
+# specifies which search query keywords triggers this answerer
+keywords = ('random',)
+
+random_int_max = 2**31
+
+if sys.version_info[0] == 2:
+ random_string_letters = string.lowercase + string.digits + string.uppercase
+else:
+ unicode = str
+ random_string_letters = string.ascii_lowercase + string.digits + string.ascii_uppercase
+
+
+def random_string():
+ return u''.join(random.choice(random_string_letters)
+ for _ in range(random.randint(8, 32)))
+
+
+def random_float():
+ return unicode(random.random())
+
+
+def random_int():
+ return unicode(random.randint(-random_int_max, random_int_max))
+
+
+random_types = {b'string': random_string,
+ b'int': random_int,
+ b'float': random_float}
+
+
+# required answerer function
+# can return a list of results (any result type) for a given query
+def answer(query):
+ parts = query.query.split()
+ if len(parts) != 2:
+ return []
+
+ if parts[1] not in random_types:
+ return []
+
+ return [{'answer': random_types[parts[1]]()}]
+
+
+# required answerer function
+# returns information about the answerer
+def self_info():
+ return {'name': gettext('Random value generator'),
+ 'description': gettext('Generate different random values'),
+ 'examples': [u'random {}'.format(x) for x in random_types]}
diff --git a/searx/answerers/statistics/answerer.py b/searx/answerers/statistics/answerer.py
new file mode 100644
index 0000000..73dd25c
--- /dev/null
+++ b/searx/answerers/statistics/answerer.py
@@ -0,0 +1,55 @@
+from sys import version_info
+from functools import reduce
+from operator import mul
+
+from flask_babel import gettext
+
+if version_info[0] == 3:
+ unicode = str
+
+keywords = ('min',
+ 'max',
+ 'avg',
+ 'sum',
+ 'prod')
+
+
+# required answerer function
+# can return a list of results (any result type) for a given query
+def answer(query):
+ parts = query.query.split()
+
+ if len(parts) < 2:
+ return []
+
+ try:
+ args = list(map(float, parts[1:]))
+ except:
+ return []
+
+ func = parts[0]
+ answer = None
+
+ if func == b'min':
+ answer = min(args)
+ elif func == b'max':
+ answer = max(args)
+ elif func == b'avg':
+ answer = sum(args) / len(args)
+ elif func == b'sum':
+ answer = sum(args)
+ elif func == b'prod':
+ answer = reduce(mul, args, 1)
+
+ if answer is None:
+ return []
+
+ return [{'answer': unicode(answer)}]
+
+
+# required answerer function
+# returns information about the answerer
+def self_info():
+ return {'name': gettext('Statistics functions'),
+ 'description': gettext('Compute {functions} of the arguments').format(functions='/'.join(keywords)),
+ 'examples': ['avg 123 548 2.04 24.2']}
diff --git a/searx/autocomplete.py b/searx/autocomplete.py
new file mode 100644
index 0000000..de0623a
--- /dev/null
+++ b/searx/autocomplete.py
@@ -0,0 +1,201 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
+'''
+
+
+from lxml import etree
+from json import loads
+from searx import settings
+from searx.languages import language_codes
+from searx.engines import (
+ categories, engines, engine_shortcuts
+)
+from searx.poolrequests import get as http_get
+
+try:
+ from urllib import urlencode
+except:
+ from urllib.parse import urlencode
+
+
+def get(*args, **kwargs):
+ if 'timeout' not in kwargs:
+ kwargs['timeout'] = settings['outgoing']['request_timeout']
+
+ return http_get(*args, **kwargs)
+
+
+def searx_bang(full_query):
+ '''check if the searchQuery contain a bang, and create fitting autocompleter results'''
+ # check if there is a query which can be parsed
+ if len(full_query.getSearchQuery()) == 0:
+ return []
+
+ results = []
+
+ # check if current query stats with !bang
+ first_char = full_query.getSearchQuery()[0]
+ if first_char == '!' or first_char == '?':
+ if len(full_query.getSearchQuery()) == 1:
+ # show some example queries
+ # TODO, check if engine is not avaliable
+ results.append(first_char + "images")
+ results.append(first_char + "wikipedia")
+ results.append(first_char + "osm")
+ else:
+ engine_query = full_query.getSearchQuery()[1:]
+
+ # check if query starts with categorie name
+ for categorie in categories:
+ if categorie.startswith(engine_query):
+ results.append(first_char + '{categorie}'.format(categorie=categorie))
+
+ # check if query starts with engine name
+ for engine in engines:
+ if engine.startswith(engine_query.replace('_', ' ')):
+ results.append(first_char + '{engine}'.format(engine=engine.replace(' ', '_')))
+
+ # check if query starts with engine shortcut
+ for engine_shortcut in engine_shortcuts:
+ if engine_shortcut.startswith(engine_query):
+ results.append(first_char + '{engine_shortcut}'.format(engine_shortcut=engine_shortcut))
+
+ # check if current query stats with :bang
+ elif first_char == ':':
+ if len(full_query.getSearchQuery()) == 1:
+ # show some example queries
+ results.append(":en")
+ results.append(":en_us")
+ results.append(":english")
+ results.append(":united_kingdom")
+ else:
+ engine_query = full_query.getSearchQuery()[1:]
+
+ for lc in language_codes:
+ lang_id, lang_name, country, english_name = map(unicode.lower, lc)
+
+ # check if query starts with language-id
+ if lang_id.startswith(engine_query):
+ if len(engine_query) <= 2:
+ results.append(u':{lang_id}'.format(lang_id=lang_id.split('-')[0]))
+ else:
+ results.append(u':{lang_id}'.format(lang_id=lang_id))
+
+ # check if query starts with language name
+ if lang_name.startswith(engine_query) or english_name.startswith(engine_query):
+ results.append(u':{lang_name}'.format(lang_name=lang_name))
+
+ # check if query starts with country
+ if country.startswith(engine_query.replace('_', ' ')):
+ results.append(u':{country}'.format(country=country.replace(' ', '_')))
+
+ # remove duplicates
+ result_set = set(results)
+
+ # remove results which are already contained in the query
+ for query_part in full_query.query_parts:
+ if query_part in result_set:
+ result_set.remove(query_part)
+
+ # convert result_set back to list
+ return list(result_set)
+
+
+def dbpedia(query, lang):
+ # dbpedia autocompleter, no HTTPS
+ autocomplete_url = 'http://lookup.dbpedia.org/api/search.asmx/KeywordSearch?'
+
+ response = get(autocomplete_url + urlencode(dict(QueryString=query)))
+
+ results = []
+
+ if response.ok:
+ dom = etree.fromstring(response.content)
+ results = dom.xpath('//a:Result/a:Label//text()',
+ namespaces={'a': 'http://lookup.dbpedia.org/'})
+
+ return results
+
+
+def duckduckgo(query, lang):
+ # duckduckgo autocompleter
+ url = 'https://ac.duckduckgo.com/ac/?{0}&type=list'
+
+ resp = loads(get(url.format(urlencode(dict(q=query)))).text)
+ if len(resp) > 1:
+ return resp[1]
+ return []
+
+
+def google(query, lang):
+ # google autocompleter
+ autocomplete_url = 'https://suggestqueries.google.com/complete/search?client=toolbar&'
+
+ response = get(autocomplete_url + urlencode(dict(hl=lang, q=query)))
+
+ results = []
+
+ if response.ok:
+ dom = etree.fromstring(response.text)
+ results = dom.xpath('//suggestion/@data')
+
+ return results
+
+
+def startpage(query, lang):
+ # startpage autocompleter
+ url = 'https://startpage.com/do/suggest?{query}'
+
+ resp = get(url.format(query=urlencode({'query': query}))).text.split('\n')
+ if len(resp) > 1:
+ return resp
+ return []
+
+
+def qwant(query, lang):
+ # qwant autocompleter (additional parameter : lang=en_en&count=xxx )
+ url = 'https://api.qwant.com/api/suggest?{query}'
+
+ resp = get(url.format(query=urlencode({'q': query, 'lang': lang})))
+
+ results = []
+
+ if resp.ok:
+ data = loads(resp.text)
+ if data['status'] == 'success':
+ for item in data['data']['items']:
+ results.append(item['value'])
+
+ return results
+
+
+def wikipedia(query, lang):
+ # wikipedia autocompleter
+ url = 'https://' + lang + '.wikipedia.org/w/api.php?action=opensearch&{0}&limit=10&namespace=0&format=json'
+
+ resp = loads(get(url.format(urlencode(dict(search=query)))).text)
+ if len(resp) > 1:
+ return resp[1]
+ return []
+
+
+backends = {'dbpedia': dbpedia,
+ 'duckduckgo': duckduckgo,
+ 'google': google,
+ 'startpage': startpage,
+ 'qwant': qwant,
+ 'wikipedia': wikipedia
+ }
diff --git a/searx/data/currencies.json b/searx/data/currencies.json
new file mode 100644
index 0000000..bfde5a7
--- /dev/null
+++ b/searx/data/currencies.json
@@ -0,0 +1,7655 @@
+{
+ "names": {
+ "francos franceses": [
+ "FRF"
+ ],
+ "bulgarischer lew": [
+ "BGN"
+ ],
+ "o\u0308rme\u0301ny dram": [
+ "AMD"
+ ],
+ "oekrai\u0308ense hryvnja": [
+ "UAH"
+ ],
+ "guatemalan quetzal": [
+ "GTQ"
+ ],
+ "ghana cedi": [
+ "GHS"
+ ],
+ "livre de sainte helene": [
+ "SHP"
+ ],
+ "papua new guinean kina": [
+ "PGK"
+ ],
+ "aud": [
+ "AUD"
+ ],
+ "\u20ab": [
+ "VND"
+ ],
+ "olasz li\u0301ra": [
+ "ITL"
+ ],
+ "aserbaidschan manat": [
+ "AZN"
+ ],
+ "ethiopian dollar": [
+ "ETB"
+ ],
+ "norwegische krone": [
+ "NOK"
+ ],
+ "papoea nieuw guinese kina": [
+ "PGK"
+ ],
+ "som uzbeko": [
+ "UZS"
+ ],
+ "yuan chino": [
+ "CNY"
+ ],
+ "nuevo dolar de taiwan": [
+ "TWD"
+ ],
+ "zweedse kronen": [
+ "SEK"
+ ],
+ "dollar des i\u0302les cai\u0308mans": [
+ "KYD"
+ ],
+ "do\u0301lar de singapur": [
+ "SGD"
+ ],
+ "gru\u0301z lari": [
+ "GEL"
+ ],
+ "escudo mozambiquen\u0303o": [
+ "MZE"
+ ],
+ "peso filipino": [
+ "PHP"
+ ],
+ "grivnia ucraniana": [
+ "UAH"
+ ],
+ "salamon szigeteki dolla\u0301r": [
+ "SBD"
+ ],
+ "barbados dollar": [
+ "BBD"
+ ],
+ "fuang": [
+ "THB"
+ ],
+ "dirham marroqui": [
+ "MAD"
+ ],
+ "sri lankan rupees": [
+ "LKR"
+ ],
+ "qindarka": [
+ "ALL"
+ ],
+ "dinaro macedone": [
+ "MKD"
+ ],
+ "togrog": [
+ "MNT"
+ ],
+ "q\u0259pik": [
+ "AZN"
+ ],
+ "special drawing rights": [
+ "XDR"
+ ],
+ "gibralta\u0301ri font": [
+ "GIP"
+ ],
+ "dinar bahraini": [
+ "BHD"
+ ],
+ "marokkaanse dirham": [
+ "MAD"
+ ],
+ "rouble sovie\u0301tique": [
+ "SUR"
+ ],
+ "tanzanian shilling": [
+ "TZS"
+ ],
+ "libra de siria": [
+ "SYP"
+ ],
+ "rand sudafricano": [
+ "ZAR"
+ ],
+ "seychelse roepie": [
+ "SCR"
+ ],
+ "seychelse roepia": [
+ "SCR"
+ ],
+ "forint": [
+ "HUF"
+ ],
+ "dinar algerino": [
+ "DZD"
+ ],
+ "roupie du sri lanka": [
+ "LKR"
+ ],
+ "katar riyal": [
+ "QAR"
+ ],
+ "schekalim": [
+ "ILS"
+ ],
+ "corona checoslovaca": [
+ "CSK"
+ ],
+ "baht tailande\u0301s": [
+ "THB"
+ ],
+ "nuevo peso": [
+ "ARS",
+ "UYU"
+ ],
+ "cuna croata": [
+ "HRK"
+ ],
+ "nieuwe israe\u0308lische shekel": [
+ "ILS"
+ ],
+ "nieuwe israelische shekel": [
+ "ILS"
+ ],
+ "dollar des i\u0302les fidji": [
+ "FJD"
+ ],
+ "nieuw zeelandse dollar": [
+ "NZD"
+ ],
+ "tanza\u0301niai shilling": [
+ "TZS"
+ ],
+ "gold franc": [
+ "XFO"
+ ],
+ "tongan pa`anga": [
+ "TOP"
+ ],
+ "francia frank": [
+ "FRF"
+ ],
+ "brl": [
+ "BRL"
+ ],
+ "isla\u0308ndische wa\u0308hrung": [
+ "ISK"
+ ],
+ "guyana dollar": [
+ "GYD"
+ ],
+ "dollaro australiano": [
+ "AUD"
+ ],
+ "nakfa e\u0301rythre\u0301en": [
+ "ERN"
+ ],
+ "kap verde escudo": [
+ "CVE"
+ ],
+ "dinar iraqui\u0301": [
+ "IQD"
+ ],
+ "vietnamese dong": [
+ "VND"
+ ],
+ "neuer sol": [
+ "PEN"
+ ],
+ "peso de argentina": [
+ "ARS"
+ ],
+ "ddr mark": [
+ "DDM"
+ ],
+ "br$": [
+ "BND"
+ ],
+ "e\u0301szak koreai von": [
+ "KPW"
+ ],
+ "japanse yen": [
+ "JPY"
+ ],
+ "franco svizzero": [
+ "CHF"
+ ],
+ "afghani afgano": [
+ "AFN"
+ ],
+ "lira siriana": [
+ "SYP"
+ ],
+ "boliviano": [
+ "BOB"
+ ],
+ "vanuatui vatu": [
+ "VUV"
+ ],
+ "tnd": [
+ "TND"
+ ],
+ "manat turkmene": [
+ "TMT"
+ ],
+ "namibia dollar": [
+ "NAD"
+ ],
+ "ern": [
+ "ERN"
+ ],
+ "manat turkmeno": [
+ "TMT"
+ ],
+ "bds$": [
+ "BBD"
+ ],
+ "bhutaanse ngultrum": [
+ "BTN"
+ ],
+ "peso chilien": [
+ "CLP"
+ ],
+ "dolar jamaicano": [
+ "JMD"
+ ],
+ "bahamas dollar": [
+ "BSD"
+ ],
+ "eritrese nakfa": [
+ "ERN"
+ ],
+ "czk": [
+ "CZK"
+ ],
+ "engels pond": [
+ "GBP"
+ ],
+ "dolar de bermudas": [
+ "BMD"
+ ],
+ "taka bangladeshi\u0301": [
+ "BDT"
+ ],
+ "riyal del qatar": [
+ "QAR"
+ ],
+ "kuwaiti dinar": [
+ "KWD"
+ ],
+ "seychellen rupie": [
+ "SCR"
+ ],
+ "boli\u0301var ve\u0301ne\u0301zue\u0301lien": [
+ "VEF"
+ ],
+ "sri lanka rupie": [
+ "LKR"
+ ],
+ "do\u0301lar de brunei": [
+ "BND"
+ ],
+ "do\u0301lar de las islas salomo\u0301n": [
+ "SBD"
+ ],
+ "lak": [
+ "LAK"
+ ],
+ "sum uzbeko": [
+ "UZS"
+ ],
+ "kurus\u0327": [
+ "TRY"
+ ],
+ "iraqi dinar": [
+ "IQD"
+ ],
+ "livre sterling": [
+ "GBP"
+ ],
+ "angolai kwanza": [
+ "AOA"
+ ],
+ "lat": [
+ "LVL"
+ ],
+ "lek albanais": [
+ "ALL"
+ ],
+ "brunei dolla\u0301r": [
+ "BND"
+ ],
+ "tetri": [
+ "GEL"
+ ],
+ "sterlina sudsudanese": [
+ "SSP"
+ ],
+ "mo\u0308ngo\u0308": [
+ "MNT"
+ ],
+ "mexican un peso coinage": [
+ "MXN"
+ ],
+ "djiboutische frank": [
+ "DJF"
+ ],
+ "seychelle i ru\u0301pia": [
+ "SCR"
+ ],
+ "litauischer litas": [
+ "LTL"
+ ],
+ "zambiaanse kwacha": [
+ "ZMW"
+ ],
+ "maldi\u0301v szigeteki ru\u0301fia": [
+ "MVR"
+ ],
+ "dinar bahreini\u0301": [
+ "BHD"
+ ],
+ "barbadiaanse dollar": [
+ "BBD"
+ ],
+ "drachme": [
+ "GRD"
+ ],
+ "emalangeni": [
+ "SZL"
+ ],
+ "canadese dollar": [
+ "CAD"
+ ],
+ "mx$": [
+ "MXN"
+ ],
+ "chinesischer renminbi": [
+ "CNY"
+ ],
+ "macedonian denar": [
+ "MKD"
+ ],
+ "uic franc": [
+ "XFU"
+ ],
+ "won surcoreano": [
+ "KRW"
+ ],
+ "nuevo shekel": [
+ "ILS"
+ ],
+ "arubaanse florin": [
+ "AWG"
+ ],
+ "ruanda franc": [
+ "RWF"
+ ],
+ "franco burundes": [
+ "BIF"
+ ],
+ "makao\u0301i pataca": [
+ "MOP"
+ ],
+ "koruna c\u030ceska\u0301": [
+ "CZK"
+ ],
+ "dirham des e\u0301mirats": [
+ "AED"
+ ],
+ "mozambiki metical": [
+ "MZN"
+ ],
+ "panamanian balboa": [
+ "PAB"
+ ],
+ "syrian pound": [
+ "SYP"
+ ],
+ "ki\u0301nai ju\u0308an": [
+ "CNY"
+ ],
+ "mxn": [
+ "MXN"
+ ],
+ "dolar fijiano": [
+ "FJD"
+ ],
+ "a\u0308thiopischer birr": [
+ "ETB"
+ ],
+ "kirgiz szom": [
+ "KGS"
+ ],
+ "dinar du ye\u0301men du sud": [
+ "YER"
+ ],
+ "peso moneda nacional": [
+ "ARS"
+ ],
+ "scellino somalo": [
+ "SOS"
+ ],
+ "cseh korona": [
+ "CZK"
+ ],
+ "uruguayan peso": [
+ "UYU"
+ ],
+ "cl$": [
+ "CLP"
+ ],
+ "convertibele peso": [
+ "CUC"
+ ],
+ "britisches pfund": [
+ "GBP"
+ ],
+ "tonga dollar": [
+ "TOP"
+ ],
+ "peso cubain convertible": [
+ "CUC"
+ ],
+ "argentin peso": [
+ "ARS"
+ ],
+ "lira maltesa": [
+ "MTL"
+ ],
+ "bosnische konvertibilna marka": [
+ "BAM"
+ ],
+ "francs suisse": [
+ "CHF"
+ ],
+ "gourde haitienne": [
+ "HTG"
+ ],
+ "shilling somali": [
+ "SOS"
+ ],
+ "nt$": [
+ "TWD"
+ ],
+ "rp": [
+ "IDR"
+ ],
+ "dolar de las bahamas": [
+ "BSD"
+ ],
+ "rs": [
+ "BRL"
+ ],
+ "monnaie danoise": [
+ "DKK"
+ ],
+ "swaziland lilangeni": [
+ "SZL"
+ ],
+ "tugrig": [
+ "MNT"
+ ],
+ "gersh": [
+ "ETB"
+ ],
+ "rouble russe": [
+ "RUB"
+ ],
+ "tugrik": [
+ "MNT"
+ ],
+ "kip": [
+ "LAK"
+ ],
+ "dirham de los emiratos arabes unidos": [
+ "AED"
+ ],
+ "escudo capverdien": [
+ "CVE"
+ ],
+ "jemeni ria\u0301l": [
+ "YER"
+ ],
+ "fcfp": [
+ "XPF"
+ ],
+ "rwanda franc": [
+ "RWF"
+ ],
+ "bolivar venezolano": [
+ "VEF"
+ ],
+ "zambiai kwacha": [
+ "ZMW"
+ ],
+ "lev bulgaro": [
+ "BGN"
+ ],
+ "lev bulgare": [
+ "BGN"
+ ],
+ "nakfa eritreo": [
+ "ERN"
+ ],
+ "danish krone": [
+ "DKK"
+ ],
+ "monnaie britannique": [
+ "GBP"
+ ],
+ "r$": [
+ "BRL"
+ ],
+ "di\u0301rham marroqui\u0301": [
+ "MAD"
+ ],
+ "institut d'e\u0301mission d'outre mer": [
+ "XPF"
+ ],
+ "manat azerbaiyano": [
+ "AZN"
+ ],
+ "vietnami \u0111o\u0302\u0300ng": [
+ "VND"
+ ],
+ "riyal iraniano": [
+ "IRR"
+ ],
+ "franco guineano": [
+ "GNF"
+ ],
+ "fiorino arubano": [
+ "AWG"
+ ],
+ "rand": [
+ "ZAR"
+ ],
+ "schekel": [
+ "ILS"
+ ],
+ "sterlina di sant'elena": [
+ "SHP"
+ ],
+ "pound sterling": [
+ "GBP"
+ ],
+ "nacfa eritreo": [
+ "ERN"
+ ],
+ "dolar taiwanes": [
+ "TWD"
+ ],
+ "koruna c\u030ceska": [
+ "CZK"
+ ],
+ "\u20ad": [
+ "LAK"
+ ],
+ "kro\u0301na": [
+ "ISK"
+ ],
+ "denar macedonio": [
+ "MKD"
+ ],
+ "libra libanesa": [
+ "LBP"
+ ],
+ "dolar belicen\u0303o": [
+ "BZD"
+ ],
+ "lesotho\u0301i loti": [
+ "LSL"
+ ],
+ "colombian peso": [
+ "COP"
+ ],
+ "do\u0301lar de brune\u0301i": [
+ "BND"
+ ],
+ "szingapu\u0301ri dolla\u0301r": [
+ "SGD"
+ ],
+ "franc poincare": [
+ "XFO"
+ ],
+ "metical mozambiqueno": [
+ "MZN"
+ ],
+ "filipijnse peso": [
+ "PHP"
+ ],
+ "somoni": [
+ "TJS"
+ ],
+ "lempire hondurien": [
+ "HNL"
+ ],
+ "angolan kwanza": [
+ "AOA"
+ ],
+ "schwedenkrone": [
+ "SEK"
+ ],
+ "lire maltaise": [
+ "MTL"
+ ],
+ "balboa paname\u0301en": [
+ "PAB"
+ ],
+ "israelische lire": [
+ "ILS"
+ ],
+ "nzd": [
+ "NZD"
+ ],
+ "hryvnia ucraina": [
+ "UAH"
+ ],
+ "pataca": [
+ "MOP"
+ ],
+ "coronas suecas": [
+ "SEK"
+ ],
+ "cape verde escudo": [
+ "CVE"
+ ],
+ "rupia pakistani": [
+ "PKR"
+ ],
+ "hungarian forint": [
+ "HUF"
+ ],
+ "rupia pakistana": [
+ "PKR"
+ ],
+ "bs.": [
+ "BOB"
+ ],
+ "kopeken": [
+ "RUB"
+ ],
+ "dolar bahameno": [
+ "BSD"
+ ],
+ "de\u0301l koreai von": [
+ "KRW"
+ ],
+ "zo\u0308ld foki ko\u0308zta\u0301rsasa\u0301gi escudo": [
+ "CVE"
+ ],
+ "romanian leu": [
+ "RON"
+ ],
+ "etio\u0301p birr": [
+ "ETB"
+ ],
+ "indiai ru\u0301pia": [
+ "INR"
+ ],
+ "livre de gibraltar": [
+ "GIP"
+ ],
+ "pa\u2019anga": [
+ "TOP"
+ ],
+ "mexiko\u0301i pezo\u0301": [
+ "MXN"
+ ],
+ "tansania schilling": [
+ "TZS"
+ ],
+ "omanitische rial": [
+ "OMR"
+ ],
+ "rial omani\u0301": [
+ "OMR"
+ ],
+ "milandor": [
+ "RSD"
+ ],
+ "canadischer dollar": [
+ "CAD"
+ ],
+ "dollaro delle isole salomone": [
+ "SBD"
+ ],
+ "noorse kronen": [
+ "NOK"
+ ],
+ "samoan ta\u0304la\u0304": [
+ "WST"
+ ],
+ "aruban florin": [
+ "AWG"
+ ],
+ "iraakse dinar": [
+ "IQD"
+ ],
+ "malaysian ringgit": [
+ "MYR"
+ ],
+ "som usbeco": [
+ "UZS"
+ ],
+ "ariary": [
+ "MGA"
+ ],
+ "kyat": [
+ "MMK"
+ ],
+ "austral (moneda de argentina)": [
+ "ARA"
+ ],
+ "bruneise dollar": [
+ "BND"
+ ],
+ "leu romeno": [
+ "RON"
+ ],
+ "kolumbiai peso": [
+ "COP"
+ ],
+ "oezbeekse sum": [
+ "UZS"
+ ],
+ "burundese frank": [
+ "BIF"
+ ],
+ "austral (argentina)": [
+ "ARA"
+ ],
+ "riyal qatarien": [
+ "QAR"
+ ],
+ "quetzal guatemalteque": [
+ "GTQ"
+ ],
+ "taiwan dollar": [
+ "TWD"
+ ],
+ "dolar de namibia": [
+ "NAD"
+ ],
+ "indiase rupee": [
+ "INR"
+ ],
+ "dollaro delle figi": [
+ "FJD"
+ ],
+ "cfa franc beac": [
+ "XAF"
+ ],
+ "hrk": [
+ "HRK"
+ ],
+ "peso dominicano": [
+ "DOP"
+ ],
+ "sh.so.": [
+ "SOS"
+ ],
+ "kwacha zambiano": [
+ "ZMW"
+ ],
+ "\u0441\u043e\u043c": [
+ "KGS"
+ ],
+ "roupie nepalaise": [
+ "NPR"
+ ],
+ "bermudian dollar": [
+ "BMD"
+ ],
+ "cookinseln dollar": [
+ "NZD"
+ ],
+ "bam": [
+ "BAM"
+ ],
+ "kwanza angolen\u0303o": [
+ "AOA"
+ ],
+ "dinaro libico": [
+ "LYD"
+ ],
+ "malagasy ariary": [
+ "MGA"
+ ],
+ "dolar liberiano": [
+ "LRD"
+ ],
+ "falklandeilands pond": [
+ "FKP"
+ ],
+ "nouvelle livre turque": [
+ "TRY"
+ ],
+ "szovjet rubel": [
+ "SUR"
+ ],
+ "magyar forint": [
+ "HUF"
+ ],
+ "peso cubain": [
+ "CUP"
+ ],
+ "xfo": [
+ "XFO"
+ ],
+ "pakiszta\u0301ni ru\u0301pia": [
+ "PKR"
+ ],
+ "georgische lari": [
+ "GEL"
+ ],
+ "loti": [
+ "LSL"
+ ],
+ "moldawischer leu": [
+ "MDL"
+ ],
+ "dollaro statunitense": [
+ "USD"
+ ],
+ "do\u0301lar bahame\u0301s": [
+ "BSD"
+ ],
+ "lat leto\u0301n": [
+ "LVL"
+ ],
+ "peso cileno": [
+ "CLP"
+ ],
+ "dinar libio": [
+ "LYD"
+ ],
+ "salomon dollar": [
+ "SBD"
+ ],
+ "rupia nepali": [
+ "NPR"
+ ],
+ "cop": [
+ "COP"
+ ],
+ "sri\u0301 lanka i ru\u0301pia": [
+ "LKR"
+ ],
+ "maloti": [
+ "LSL"
+ ],
+ "drtrigonbot:exchange rate data:hrk": [
+ "HRK"
+ ],
+ "franco ruandese": [
+ "RWF"
+ ],
+ "ils": [
+ "ILS"
+ ],
+ "bangladeshi taka": [
+ "BDT"
+ ],
+ "konvertierbarer peso": [
+ "CUC"
+ ],
+ "she\u0301kel": [
+ "ILS"
+ ],
+ "kwd": [
+ "KWD"
+ ],
+ "bahrain dinar": [
+ "BHD"
+ ],
+ "sfr.": [
+ "CHF"
+ ],
+ "livre syrienne": [
+ "SYP"
+ ],
+ "macao pataca": [
+ "MOP"
+ ],
+ "dolar de las islas caiman": [
+ "KYD"
+ ],
+ "dollaro del brunei": [
+ "BND"
+ ],
+ "chil$": [
+ "CLP"
+ ],
+ "honduranischer lempira": [
+ "HNL"
+ ],
+ "frf": [
+ "FRF"
+ ],
+ "nakfa": [
+ "ERN"
+ ],
+ "ghanese cedi": [
+ "GHS"
+ ],
+ "dalasi gambien": [
+ "GMD"
+ ],
+ "braziliaanse real": [
+ "BRL"
+ ],
+ "frw": [
+ "RWF"
+ ],
+ "libra falkland": [
+ "FKP"
+ ],
+ "algerijnse dinar": [
+ "DZD"
+ ],
+ "dinar du bahrei\u0308n": [
+ "BHD"
+ ],
+ "dinar serbio": [
+ "RSD"
+ ],
+ "rupias indias": [
+ "INR"
+ ],
+ "lesotho loti": [
+ "LSL"
+ ],
+ "do\u0301lar guyane\u0301s": [
+ "GYD"
+ ],
+ "mauritanian ouguiya": [
+ "MRO"
+ ],
+ "greek drachma": [
+ "GRD"
+ ],
+ "east caribbean dollar": [
+ "XCD"
+ ],
+ "dirham de emiratos a\u0301rabes unidos": [
+ "AED"
+ ],
+ "scellino tanzaniano": [
+ "TZS"
+ ],
+ "aurar": [
+ "ISK"
+ ],
+ "oost duitse mark": [
+ "DDM"
+ ],
+ "franc guine\u0301en": [
+ "GNF"
+ ],
+ "dollaro hongkonghese": [
+ "HKD"
+ ],
+ "sll": [
+ "SLL"
+ ],
+ "\u09f3": [
+ "BDT"
+ ],
+ "rial saudita": [
+ "SAR"
+ ],
+ "tzs": [
+ "TZS"
+ ],
+ "real bresilien": [
+ "BRL"
+ ],
+ "irakischer dinar": [
+ "IQD"
+ ],
+ "rupias": [
+ "INR"
+ ],
+ "sistema u\u0301nico de compensacio\u0301n regional": [
+ "XSU"
+ ],
+ "franco frances": [
+ "FRF"
+ ],
+ "costaricaanse colo\u0301n": [
+ "CRC"
+ ],
+ "lita lituano": [
+ "LTL"
+ ],
+ "\u20ae": [
+ "MNT"
+ ],
+ "\u0434\u043e\u043b\u0430\u0440": [
+ "MKD"
+ ],
+ "peso filippino": [
+ "PHP"
+ ],
+ "dolar neocelandes": [
+ "NZD"
+ ],
+ "to\u0308gro\u0308g": [
+ "MNT"
+ ],
+ "rupiah": [
+ "IDR"
+ ],
+ "zersto\u0308\u00dft sterling": [
+ "GBP"
+ ],
+ "can$": [
+ "CAD"
+ ],
+ "pgk": [
+ "PGK"
+ ],
+ "rupia india": [
+ "INR"
+ ],
+ "do\u0301lar belicen\u0303o": [
+ "BZD"
+ ],
+ "nis": [
+ "ILS"
+ ],
+ "letse lats": [
+ "LVL"
+ ],
+ "slrs": [
+ "LKR"
+ ],
+ "dominikanischer peso": [
+ "DOP"
+ ],
+ "emira\u0301tusi dirham": [
+ "AED"
+ ],
+ "litas lituano": [
+ "LTL"
+ ],
+ "litas lituana": [
+ "LTL"
+ ],
+ "rupia nepali\u0301": [
+ "NPR"
+ ],
+ "thb": [
+ "THB"
+ ],
+ "swazi lilangeni": [
+ "SZL"
+ ],
+ "yen": [
+ "JPY"
+ ],
+ "flori\u0301n hungaro": [
+ "HUF"
+ ],
+ "gourde hai\u0308tienne": [
+ "HTG"
+ ],
+ "yer": [
+ "YER"
+ ],
+ "argentinischer austral": [
+ "ARA"
+ ],
+ "bolivar ve\u0301ne\u0301zue\u0301lien": [
+ "VEF"
+ ],
+ "bolivianischer boliviano": [
+ "BOB"
+ ],
+ "sheqalim": [
+ "ILS"
+ ],
+ "\u20af": [
+ "GRD"
+ ],
+ "brits pond": [
+ "GBP"
+ ],
+ "sterlina sudanese": [
+ "SDG"
+ ],
+ "koruna cesko slovenska": [
+ "CSK"
+ ],
+ "argent chinois": [
+ "CNY"
+ ],
+ "libische dinar": [
+ "LYD"
+ ],
+ "libanese pond": [
+ "LBP"
+ ],
+ "burundian franc": [
+ "BIF"
+ ],
+ "nuevo sol peruano": [
+ "PEN"
+ ],
+ "singapore dollar": [
+ "SGD"
+ ],
+ "dollar hai\u0308tien": [
+ "HTG"
+ ],
+ "balboa panameen": [
+ "PAB"
+ ],
+ "aserbaidschanischer manat": [
+ "AZN"
+ ],
+ "co\u0301rdoba": [
+ "NIO"
+ ],
+ "sterlina delle falkland": [
+ "FKP"
+ ],
+ "peso messicano": [
+ "MXN"
+ ],
+ "millime": [
+ "TND"
+ ],
+ "omaanse rial": [
+ "OMR"
+ ],
+ "sjekel": [
+ "ILS"
+ ],
+ "vatu": [
+ "VUV"
+ ],
+ "colo\u0301n": [
+ "CRC"
+ ],
+ "bosnisch herzegowinische konvertible mark": [
+ "BAM"
+ ],
+ "dollar guyanien": [
+ "GYD"
+ ],
+ "cedi": [
+ "GHS"
+ ],
+ "rupia de mauricio": [
+ "MUR"
+ ],
+ "cny": [
+ "CNY"
+ ],
+ "pataca di macao": [
+ "MOP"
+ ],
+ "argentine austral": [
+ "ARA"
+ ],
+ "austral (wa\u0308hrung)": [
+ "ARA"
+ ],
+ "nuevo do\u0301lar taiwane\u0301s": [
+ "TWD"
+ ],
+ "dinar bahraini\u0301": [
+ "BHD"
+ ],
+ "som kirghizo": [
+ "KGS"
+ ],
+ "baht": [
+ "THB"
+ ],
+ "sri lanka rupee": [
+ "LKR"
+ ],
+ "zsenminpi": [
+ "CNY"
+ ],
+ "ryal saoudien": [
+ "SAR"
+ ],
+ "djibouti franc": [
+ "DJF"
+ ],
+ "pula del botswana": [
+ "BWP"
+ ],
+ "mauritiaanse rupee": [
+ "MUR"
+ ],
+ "syrische lira": [
+ "SYP"
+ ],
+ "centas": [
+ "LTL"
+ ],
+ "dollars": [
+ "USD"
+ ],
+ "guineai frank": [
+ "GNF"
+ ],
+ "panamai balboa": [
+ "PAB"
+ ],
+ "dollaro": [
+ "BBD",
+ "BZD"
+ ],
+ "us dollar": [
+ "USD"
+ ],
+ "ta\u0304la\u0304": [
+ "WST"
+ ],
+ "peso dominicain": [
+ "DOP"
+ ],
+ "\u4eba\u6c11\u5e01": [
+ "CNY"
+ ],
+ "libra de gibraltar": [
+ "GIP"
+ ],
+ "mark est allemand": [
+ "DDM"
+ ],
+ "libra jamaicana": [
+ "JMD"
+ ],
+ "eritrean nakfa": [
+ "ERN"
+ ],
+ "som": [
+ "KGS",
+ "UZS"
+ ],
+ "sol": [
+ "PEN"
+ ],
+ "bath": [
+ "THB"
+ ],
+ "do\u0301lar surinames": [
+ "SRD"
+ ],
+ "srilankaanse roepie": [
+ "LKR"
+ ],
+ "oma\u0301ni ria\u0301l": [
+ "OMR"
+ ],
+ "dolar guyane\u0301s": [
+ "GYD"
+ ],
+ "dollaro delle bahamas": [
+ "BSD"
+ ],
+ "georgischer lari": [
+ "GEL"
+ ],
+ "sa\u0303o tome\u0301 e\u0301s pri\u0301ncipe i dobra": [
+ "STD"
+ ],
+ "namibische dollar": [
+ "NAD"
+ ],
+ "lira turca": [
+ "TRY"
+ ],
+ "austral": [
+ "ARA"
+ ],
+ "tune\u0301ziai dina\u0301r": [
+ "TND"
+ ],
+ "pyg": [
+ "PYG"
+ ],
+ "\u060b": [
+ "AFN"
+ ],
+ "tajikistani somoni": [
+ "TJS"
+ ],
+ "fiji dollar": [
+ "FJD"
+ ],
+ "south african rand": [
+ "ZAR"
+ ],
+ "seychelse rupee": [
+ "SCR"
+ ],
+ "anciens francs": [
+ "FRF"
+ ],
+ "nuevo sheqel": [
+ "ILS"
+ ],
+ "amerikai dolla\u0301r": [
+ "USD"
+ ],
+ "do\u0301lar canadiense": [
+ "CAD"
+ ],
+ "turkmenistan manat": [
+ "TMT"
+ ],
+ "litas": [
+ "LTL"
+ ],
+ "peso cubano": [
+ "CUP"
+ ],
+ "maltese lira": [
+ "MTL"
+ ],
+ "azn": [
+ "AZN"
+ ],
+ "maltese lire": [
+ "MTL"
+ ],
+ "sve\u0301d korona": [
+ "SEK"
+ ],
+ "konvertibilna marka": [
+ "BAM"
+ ],
+ "peso": [
+ "COP",
+ "DOP",
+ "MXN"
+ ],
+ "franse frank": [
+ "FRF"
+ ],
+ "mexiko\u0301i peso": [
+ "MXN"
+ ],
+ "pesos": [
+ "MXN"
+ ],
+ "shilling kenyan": [
+ "KES"
+ ],
+ "iqd": [
+ "IQD"
+ ],
+ "colo\u0301n costaricain": [
+ "CRC"
+ ],
+ "dinar soudanais": [
+ "SDG"
+ ],
+ "peso colombien": [
+ "COP"
+ ],
+ "renminbi cinese": [
+ "CNY"
+ ],
+ "mark der deutschen notenbank": [
+ "DDM"
+ ],
+ "re\u0301al": [
+ "BRL"
+ ],
+ "mauritius rupie": [
+ "MUR"
+ ],
+ "tongai pa'anga": [
+ "TOP"
+ ],
+ "deutsche mark der deutschen notenbank": [
+ "DDM"
+ ],
+ "ddr geld": [
+ "DDM"
+ ],
+ "usbekistan som": [
+ "UZS"
+ ],
+ "trinidad und tobago dollar": [
+ "TTD"
+ ],
+ "guyanai dolla\u0301r": [
+ "GYD"
+ ],
+ "do\u0301lar de bermuda": [
+ "BMD"
+ ],
+ "peso convertible": [
+ "CUC"
+ ],
+ "livre britannique": [
+ "GBP"
+ ],
+ "italienische lira": [
+ "ITL"
+ ],
+ "italienische lire": [
+ "ITL"
+ ],
+ "kenyai shilling": [
+ "KES"
+ ],
+ "li\u0301biai dina\u0301r": [
+ "LYD"
+ ],
+ "nuevo dolar taiwanes": [
+ "TWD"
+ ],
+ "fijan dollar": [
+ "FJD"
+ ],
+ "uruguayischer peso": [
+ "UYU"
+ ],
+ "neuseela\u0308ndischer dollar": [
+ "NZD"
+ ],
+ "csendes o\u0301cea\u0301ni valutako\u0308zo\u0308sse\u0301gi frank": [
+ "XPF"
+ ],
+ "poisha": [
+ "BDT"
+ ],
+ "lat letton": [
+ "LVL"
+ ],
+ "n$": [
+ "NAD"
+ ],
+ "rwandan franc": [
+ "RWF"
+ ],
+ "lempira": [
+ "HNL"
+ ],
+ "speciale trekkingsrechten": [
+ "XDR"
+ ],
+ "maldivian rufiyaa": [
+ "MVR"
+ ],
+ "rwandan frank": [
+ "RWF"
+ ],
+ "israe\u0308lische lire": [
+ "ILS"
+ ],
+ "afgani": [
+ "AFN"
+ ],
+ "rol": [
+ "RON"
+ ],
+ "u+20bd": [
+ "RUB"
+ ],
+ "s\u0192": [
+ "SRD"
+ ],
+ "dinar bahreini": [
+ "BHD"
+ ],
+ "tongai pa\u02bbanga": [
+ "TOP"
+ ],
+ "franco suizo": [
+ "CHF"
+ ],
+ "marco convertible": [
+ "BAM"
+ ],
+ "forint hongrois": [
+ "HUF"
+ ],
+ "som kirguis": [
+ "KGS"
+ ],
+ "israeli new shekel": [
+ "ILS"
+ ],
+ "som kirguiz": [
+ "KGS"
+ ],
+ "albanischer lek": [
+ "ALL"
+ ],
+ "vef": [
+ "VEF"
+ ],
+ "kongo franc": [
+ "CDF"
+ ],
+ "mexicaanse peso": [
+ "MXN"
+ ],
+ "argentine peso": [
+ "ARS"
+ ],
+ "guatemaltekischer quetzal": [
+ "GTQ"
+ ],
+ "novo kwanza": [
+ "AOA"
+ ],
+ "zuid soedanese pond": [
+ "SSP"
+ ],
+ "horva\u0301t kuna": [
+ "HRK"
+ ],
+ "dolar neozelandes": [
+ "NZD"
+ ],
+ "tu\u0308rkme\u0301n manat": [
+ "TMT"
+ ],
+ "lilangeni sign": [
+ "SZL"
+ ],
+ "new taiwan dollar": [
+ "TWD"
+ ],
+ "swazische lilangeni": [
+ "SZL"
+ ],
+ "stotinki": [
+ "BGN"
+ ],
+ "\u0111o\u0302\u0300ng vietnamita": [
+ "VND"
+ ],
+ "franco burunde\u0301s": [
+ "BIF"
+ ],
+ "stotinka": [
+ "BGN"
+ ],
+ "cordoba nicaragu\u0308ense": [
+ "NIO"
+ ],
+ "lebanese pound": [
+ "LBP"
+ ],
+ "flori\u0301n aruben\u0303o": [
+ "AWG"
+ ],
+ "algerian dinar": [
+ "DZD"
+ ],
+ "dinar jordano": [
+ "JOD"
+ ],
+ "rial saoudien": [
+ "SAR"
+ ],
+ "litva\u0301n litas": [
+ "LTL"
+ ],
+ "scellino ugandese": [
+ "UGX"
+ ],
+ "zai\u0308re": [
+ "CDF"
+ ],
+ "florin d\u2019aruba": [
+ "AWG"
+ ],
+ "grivnia ucraina": [
+ "UAH"
+ ],
+ "sambischer kwacha": [
+ "ZMW"
+ ],
+ "filler": [
+ "HUF"
+ ],
+ "ringgit": [
+ "MYR"
+ ],
+ "rupia del pakistan": [
+ "PKR"
+ ],
+ "nieuwe turkse lira": [
+ "TRY"
+ ],
+ "chilei peso": [
+ "CLP"
+ ],
+ "iranian rial": [
+ "IRR"
+ ],
+ "tadzjiekse somoni": [
+ "TJS"
+ ],
+ "metical mozambiquen\u0303o": [
+ "MZN"
+ ],
+ "sterlina inglese": [
+ "GBP"
+ ],
+ "mauritian rupee": [
+ "MUR"
+ ],
+ "dinaro del bahrain": [
+ "BHD"
+ ],
+ "venezuelai boli\u0301var": [
+ "VEF"
+ ],
+ "ruma\u0308nischer ban": [
+ "RON"
+ ],
+ "dolar de las islas salomo\u0301n": [
+ "SBD"
+ ],
+ "roepiah": [
+ "IDR"
+ ],
+ "dinaro serbo": [
+ "RSD"
+ ],
+ "riyal catari\u0301": [
+ "QAR"
+ ],
+ "dollaro surinamese": [
+ "SRD"
+ ],
+ "libra sursudanesa": [
+ "SSP"
+ ],
+ "south sudanese pound": [
+ "SSP"
+ ],
+ "boli\u0301var venezuelano": [
+ "VEF"
+ ],
+ "shilling ke\u0301nyan": [
+ "KES"
+ ],
+ "dolar suriname\u0301s": [
+ "SRD"
+ ],
+ "bolivares fuertes": [
+ "VEF"
+ ],
+ "francos suizos": [
+ "CHF"
+ ],
+ "botsuanischer pula": [
+ "BWP"
+ ],
+ "nieuwe israe\u0308lische sjekel": [
+ "ILS"
+ ],
+ "bahama dollar": [
+ "BSD"
+ ],
+ "sierra leonischer leone": [
+ "SLL"
+ ],
+ "bob": [
+ "BOB"
+ ],
+ "botswana pula": [
+ "BWP"
+ ],
+ "nepa\u0301li ru\u0301pia": [
+ "NPR"
+ ],
+ "dollaro taiwanese": [
+ "TWD"
+ ],
+ "dolar de belice": [
+ "BZD"
+ ],
+ "sierra leonean leone": [
+ "SLL"
+ ],
+ "franco gibutiano": [
+ "DJF"
+ ],
+ "franco": [
+ "RWF",
+ "DJF",
+ "CDF",
+ "XPF"
+ ],
+ "nouveau dollar de tai\u0308wan": [
+ "TWD"
+ ],
+ "libras esterlinas": [
+ "GBP"
+ ],
+ "paraguayischer guarani\u0301": [
+ "PYG"
+ ],
+ "drachme (antike)": [
+ "GRD"
+ ],
+ "\u20b1": [
+ "PHP"
+ ],
+ "s\u20a3": [
+ "CHF"
+ ],
+ "csehszlova\u0301k korona": [
+ "CSK"
+ ],
+ "lithuanian litas": [
+ "LTL"
+ ],
+ "malagassische ariary": [
+ "MGA"
+ ],
+ "afl.": [
+ "AWG"
+ ],
+ "flori\u0301n hu\u0301ngaro": [
+ "HUF"
+ ],
+ "izraeli u\u0301j se\u0301kel": [
+ "ILS"
+ ],
+ "nigeriaanse naira": [
+ "NGN"
+ ],
+ "kazakhstani tenge": [
+ "KZT"
+ ],
+ "south korean won": [
+ "KRW"
+ ],
+ "dollar de hong kong": [
+ "HKD"
+ ],
+ "su\u0308dkoreanischer won": [
+ "KRW"
+ ],
+ "peso mejicano": [
+ "MXN"
+ ],
+ "won nordcoreano": [
+ "KPW"
+ ],
+ "mark der ddr": [
+ "DDM"
+ ],
+ "tschechische krone": [
+ "CZK"
+ ],
+ "solomon islands dollar": [
+ "SBD"
+ ],
+ "boli\u0301viai boliviano": [
+ "BOB"
+ ],
+ "costaricaanse colon": [
+ "CRC"
+ ],
+ "jemen rial": [
+ "YER"
+ ],
+ "mga": [
+ "MGA"
+ ],
+ "kyd": [
+ "KYD"
+ ],
+ "mauritaanse ouguiya": [
+ "MRO"
+ ],
+ "gambiaanse dalasi": [
+ "GMD"
+ ],
+ "gibraltar pound": [
+ "GIP"
+ ],
+ "tsjechoslowaakse kroon": [
+ "CSK"
+ ],
+ "gourde": [
+ "HTG"
+ ],
+ "corona sueca": [
+ "SEK"
+ ],
+ "colon costaricano": [
+ "CRC"
+ ],
+ "franc congolais": [
+ "CDF"
+ ],
+ "florin arubeno": [
+ "AWG"
+ ],
+ "kaapverdische escudo": [
+ "CVE"
+ ],
+ "venezolaanse bolivar": [
+ "VEF"
+ ],
+ "s/": [
+ "PEN"
+ ],
+ "dolar de nueva zelanda": [
+ "NZD"
+ ],
+ "do\u0301lar suriname\u0301s": [
+ "SRD"
+ ],
+ "francs suisses": [
+ "CHF"
+ ],
+ "s$": [
+ "SGD"
+ ],
+ "italiaanse lire": [
+ "ITL"
+ ],
+ "italiaanse lira": [
+ "ITL"
+ ],
+ "bahreinse dinar": [
+ "BHD"
+ ],
+ "sr": [
+ "SAR"
+ ],
+ "corona": [
+ "SEK"
+ ],
+ "font sterling": [
+ "GBP"
+ ],
+ "peso chileno": [
+ "CLP"
+ ],
+ "tala": [
+ "WST"
+ ],
+ "libra gibraltarena": [
+ "GIP"
+ ],
+ "saoedi arabische riyal": [
+ "SAR"
+ ],
+ "guinese frank": [
+ "GNF"
+ ],
+ "dracma (moderna)": [
+ "GRD"
+ ],
+ "franco de burundi": [
+ "BIF"
+ ],
+ "thaise baht": [
+ "THB"
+ ],
+ "koruna": [
+ "CZK"
+ ],
+ "koruna ceska": [
+ "CZK"
+ ],
+ "dram armeno": [
+ "AMD"
+ ],
+ "st. helena pfund": [
+ "SHP"
+ ],
+ "lek albanese": [
+ "ALL"
+ ],
+ "trinidad en tobagodollar": [
+ "TTD"
+ ],
+ "cuban peso": [
+ "CUP"
+ ],
+ "gtq": [
+ "GTQ"
+ ],
+ "djf": [
+ "DJF"
+ ],
+ "east german mark": [
+ "DDM"
+ ],
+ "yuan cinese": [
+ "CNY"
+ ],
+ "jordaanse dinar": [
+ "JOD"
+ ],
+ "guinean franc": [
+ "GNF"
+ ],
+ "szoma\u0301liai shilling": [
+ "SOS"
+ ],
+ "nok": [
+ "NOK"
+ ],
+ "do\u0301lar de namibia": [
+ "NAD"
+ ],
+ "shilingi": [
+ "TZS"
+ ],
+ "franco yibuti": [
+ "DJF"
+ ],
+ "rufiyah": [
+ "MVR"
+ ],
+ "col$": [
+ "COP"
+ ],
+ "rufiyaa": [
+ "MVR"
+ ],
+ "tt$": [
+ "TTD"
+ ],
+ "cheli\u0301n": [
+ "UGX",
+ "TZS",
+ "SOS"
+ ],
+ "gryvnia": [
+ "UAH"
+ ],
+ "cfp franc": [
+ "XPF"
+ ],
+ "real brasiliano": [
+ "BRL"
+ ],
+ "cfp frank": [
+ "XPF"
+ ],
+ "taka bengalese": [
+ "BDT"
+ ],
+ "ngwee": [
+ "ZMW"
+ ],
+ "metical mozambicano": [
+ "MZN"
+ ],
+ "lempira honduregna": [
+ "HNL"
+ ],
+ "libra malvinense": [
+ "FKP"
+ ],
+ "nuevo she\u0301quel": [
+ "ILS"
+ ],
+ "rial omanais": [
+ "OMR"
+ ],
+ "arg$": [
+ "ARS"
+ ],
+ "nicaraguanischer co\u0301rdoba": [
+ "NIO"
+ ],
+ "colon costaricien": [
+ "CRC"
+ ],
+ "drtrigonbot:exchange rate data:dkk": [
+ "DKK"
+ ],
+ "goldfranken": [
+ "XFO"
+ ],
+ "roupie indienne": [
+ "INR"
+ ],
+ "afghani": [
+ "AFN"
+ ],
+ "franc cfp": [
+ "XPF"
+ ],
+ "seychelle szigeteki ru\u0301pia": [
+ "SCR"
+ ],
+ "franco ruandes": [
+ "RWF"
+ ],
+ "pesification": [
+ "ARS"
+ ],
+ "dirham des emirats arabes unis": [
+ "AED"
+ ],
+ "$can": [
+ "CAD"
+ ],
+ "franc cfa": [
+ "XAF",
+ "XOF"
+ ],
+ "nepalese roepie": [
+ "NPR"
+ ],
+ "lwei": [
+ "AOA"
+ ],
+ "nuovo peso argentino": [
+ "ARS"
+ ],
+ "indonesian rupiah": [
+ "IDR"
+ ],
+ "guatemalai quetzal": [
+ "GTQ"
+ ],
+ "dolar de singapur": [
+ "SGD"
+ ],
+ "peso de me\u0301xico": [
+ "MXN"
+ ],
+ "surinamese guilder": [
+ "SRG"
+ ],
+ "nigerian naira": [
+ "NGN"
+ ],
+ "peso philippin": [
+ "PHP"
+ ],
+ "mongoolse tugrik": [
+ "MNT"
+ ],
+ "franc pacifique": [
+ "XPF"
+ ],
+ "haitianischer gourde": [
+ "HTG"
+ ],
+ "jemenitische rial": [
+ "YER"
+ ],
+ "do\u0301lar": [
+ "USD",
+ "FJD"
+ ],
+ "kolumbianischer peso": [
+ "COP"
+ ],
+ "co\u0301rdoba nicaraguense": [
+ "NIO"
+ ],
+ "dollar ne\u0301oze\u0301landais": [
+ "NZD"
+ ],
+ "meticais": [
+ "MZN"
+ ],
+ "uqiya": [
+ "MRO"
+ ],
+ "grivnia": [
+ "UAH"
+ ],
+ "lakhs": [
+ "BDT"
+ ],
+ "zar": [
+ "ZAR"
+ ],
+ "bahamian dollar": [
+ "BSD"
+ ],
+ "qa\u0308pik": [
+ "AZN"
+ ],
+ "ukp": [
+ "GBP"
+ ],
+ "paraguayaanse guarani\u0301": [
+ "PYG"
+ ],
+ "mauritiusi ru\u0301pia": [
+ "MUR"
+ ],
+ "philippinischer peso": [
+ "PHP"
+ ],
+ "kambodschanischer riel": [
+ "KHR"
+ ],
+ "huf": [
+ "HUF"
+ ],
+ "dollar de singapour": [
+ "SGD"
+ ],
+ "dom$": [
+ "DOP"
+ ],
+ "dinar du kowei\u0308t": [
+ "KWD"
+ ],
+ "australian dollar": [
+ "AUD"
+ ],
+ "namibian dollar": [
+ "NAD"
+ ],
+ "arubaanse gulden": [
+ "AWG"
+ ],
+ "drachme moderne grecque": [
+ "GRD"
+ ],
+ "dinar kowe\u0301itien": [
+ "KWD"
+ ],
+ "nieuwe israelische sheqel": [
+ "ILS"
+ ],
+ "salyn": [
+ "THB"
+ ],
+ "moldova\u0301n lej": [
+ "MDL"
+ ],
+ "nepalesische rupie": [
+ "NPR"
+ ],
+ "marka convertible": [
+ "BAM"
+ ],
+ "bulgarian lev": [
+ "BGN"
+ ],
+ "tengue": [
+ "KZT"
+ ],
+ "currency of somalia": [
+ "SOS"
+ ],
+ "franc franc\u0327ais": [
+ "FRF"
+ ],
+ "do\u0301lar bahames": [
+ "BSD"
+ ],
+ "som de kirguistan": [
+ "KGS"
+ ],
+ "kip laotiano": [
+ "LAK"
+ ],
+ "sar": [
+ "SAR"
+ ],
+ "ngultrum butane\u0301s": [
+ "BTN"
+ ],
+ "birr etiope": [
+ "ETB"
+ ],
+ "fening": [
+ "BAM"
+ ],
+ "dominicaanse peso": [
+ "DOP"
+ ],
+ "taka": [
+ "BDT"
+ ],
+ "\u20b2": [
+ "PYG"
+ ],
+ "do\u0301lar neozelandes": [
+ "NZD"
+ ],
+ "rial ye\u0301me\u0301nite": [
+ "YER"
+ ],
+ "sterlina sud sudanese": [
+ "SSP"
+ ],
+ "dolar de bermuda": [
+ "BMD"
+ ],
+ "dollar taiwanais": [
+ "TWD"
+ ],
+ "afghanis": [
+ "AFN"
+ ],
+ "uyu": [
+ "UYU"
+ ],
+ "cordoba": [
+ "NIO"
+ ],
+ "bahamaanse dollar": [
+ "BSD"
+ ],
+ "\u0111ong": [
+ "VND"
+ ],
+ "baiza": [
+ "OMR"
+ ],
+ "kazachse tenge": [
+ "KZT"
+ ],
+ "vietnamesischer \u0111o\u0302\u0300ng": [
+ "VND"
+ ],
+ "dollar de brunei": [
+ "BND"
+ ],
+ "dollar du belize": [
+ "BZD"
+ ],
+ "jordanian dinar": [
+ "JOD"
+ ],
+ "nuevo sol peruviano": [
+ "PEN"
+ ],
+ "livre turque": [
+ "TRY"
+ ],
+ "fidschi dollar": [
+ "FJD"
+ ],
+ "franco cfa de africa central": [
+ "XAF"
+ ],
+ "kyrgyzstani som": [
+ "KGS"
+ ],
+ "dolar taiwane\u0301s": [
+ "TWD"
+ ],
+ "quetzales": [
+ "GTQ"
+ ],
+ "pa\u0301pua u\u0301j guineai kina": [
+ "PGK"
+ ],
+ "won nord core\u0301en": [
+ "KPW"
+ ],
+ "couronne danoise": [
+ "DKK"
+ ],
+ "nuevo do\u0301lar de taiwa\u0301n": [
+ "TWD"
+ ],
+ "uruguay peso": [
+ "UYU"
+ ],
+ "boli\u0301vares fuertes": [
+ "VEF"
+ ],
+ "rupia de pakistan": [
+ "PKR"
+ ],
+ "lilangeni": [
+ "SZL"
+ ],
+ "rupia dell'india": [
+ "INR"
+ ],
+ "libra esterlina": [
+ "GBP"
+ ],
+ "koruna ceska\u0301": [
+ "CZK"
+ ],
+ "\u20b3": [
+ "ARA"
+ ],
+ "co\u0301rdoba nicaragu\u0308ense": [
+ "NIO"
+ ],
+ "hongaarse forint": [
+ "HUF"
+ ],
+ "loti lesothan": [
+ "LSL"
+ ],
+ "baht thailandese": [
+ "THB"
+ ],
+ "real brasileno": [
+ "BRL"
+ ],
+ "katari ria\u0301l": [
+ "QAR"
+ ],
+ "uzbekistani som": [
+ "UZS"
+ ],
+ "armenischer dram": [
+ "AMD"
+ ],
+ "jorda\u0301n dina\u0301r": [
+ "JOD"
+ ],
+ "bulgaarse lev": [
+ "BGN"
+ ],
+ "hondurasi lempira": [
+ "HNL"
+ ],
+ "do\u0302\u0300ng vietnamita": [
+ "VND"
+ ],
+ "gel": [
+ "GEL"
+ ],
+ "trinidad en tobago dollar": [
+ "TTD"
+ ],
+ "rupia de maldivas": [
+ "MVR"
+ ],
+ "do\u0301lar liberiano": [
+ "LRD"
+ ],
+ "vanuatuaanse vatu": [
+ "VUV"
+ ],
+ "libe\u0301riai dolla\u0301r": [
+ "LRD"
+ ],
+ "colon costarricense": [
+ "CRC"
+ ],
+ "dobra di sa\u0303o tome\u0301 e pri\u0301ncipe": [
+ "STD"
+ ],
+ "croatian kuna": [
+ "HRK"
+ ],
+ "nouveau sol": [
+ "PEN"
+ ],
+ "wo\u0306n norcoreano": [
+ "KPW"
+ ],
+ "de\u0301l afrikai rand": [
+ "ZAR"
+ ],
+ "dolar bermuden\u0303o": [
+ "BMD"
+ ],
+ "tu\u0308rkische lira": [
+ "TRY"
+ ],
+ "rmb": [
+ "CNY"
+ ],
+ "ringgit malese": [
+ "MYR"
+ ],
+ "marco de la republica democra\u0301tica alemana": [
+ "DDM"
+ ],
+ "j$": [
+ "JMD"
+ ],
+ "lire turque": [
+ "TRY"
+ ],
+ "tunisian dinar": [
+ "TND"
+ ],
+ "falkland pfund": [
+ "FKP"
+ ],
+ "pakistani rupee": [
+ "PKR"
+ ],
+ "central african cfa franc": [
+ "XAF"
+ ],
+ "rouble": [
+ "SUR"
+ ],
+ "ytl": [
+ "TRY"
+ ],
+ "trinidad e\u0301s tobago\u0301 i dolla\u0301r": [
+ "TTD"
+ ],
+ "orosz rubel": [
+ "RUB"
+ ],
+ "dollar de surinam": [
+ "SRD"
+ ],
+ "franco delle comore": [
+ "KMF"
+ ],
+ "so\u02bbm": [
+ "UZS"
+ ],
+ "franse franc": [
+ "FRF"
+ ],
+ "kuna croata": [
+ "HRK"
+ ],
+ "droits de tirage spe\u0301ciaux": [
+ "XDR"
+ ],
+ "kuna croate": [
+ "HRK"
+ ],
+ "dinar de kuwait": [
+ "KWD"
+ ],
+ "dschibuti franc": [
+ "DJF"
+ ],
+ "guinea franc": [
+ "GNF"
+ ],
+ "kwacha zambese": [
+ "ZMW"
+ ],
+ "guatemalteekse quetzal": [
+ "GTQ"
+ ],
+ "chelin keniano": [
+ "KES"
+ ],
+ "livre libanaise": [
+ "LBP"
+ ],
+ "dkk": [
+ "DKK"
+ ],
+ "ouguiya della mauritana": [
+ "MRO"
+ ],
+ "kaaimaneilandse dollar": [
+ "KYD"
+ ],
+ "drtrigonbot:exchange rate data:ltl": [
+ "LTL"
+ ],
+ "comorese frank": [
+ "KMF"
+ ],
+ "us $": [
+ "USD"
+ ],
+ "lats lettone": [
+ "LVL"
+ ],
+ "griwna": [
+ "UAH"
+ ],
+ "qatari riyal": [
+ "QAR"
+ ],
+ "colon": [
+ "CRC"
+ ],
+ "franc germinal": [
+ "FRF",
+ "XFO"
+ ],
+ "roupie ne\u0301palaise": [
+ "NPR"
+ ],
+ "dollar jamai\u0308cain": [
+ "JMD"
+ ],
+ "mark": [
+ "DDM"
+ ],
+ "indische rupie": [
+ "INR"
+ ],
+ "angolese kwanza": [
+ "AOA"
+ ],
+ "dollar de fidji": [
+ "FJD"
+ ],
+ "khr": [
+ "KHR"
+ ],
+ "krona": [
+ "SEK"
+ ],
+ "dollaro di trinidad e tobago": [
+ "TTD"
+ ],
+ "krone": [
+ "DKK"
+ ],
+ "szoma\u0301li shilling": [
+ "SOS"
+ ],
+ "rupia indiana": [
+ "INR"
+ ],
+ "bolivar fuerte": [
+ "VEF"
+ ],
+ "euro\u0301": [
+ "EUR"
+ ],
+ "rupia de indonesia": [
+ "IDR"
+ ],
+ "libra gibraltaren\u0303a": [
+ "GIP"
+ ],
+ "indonesische rupiah": [
+ "IDR"
+ ],
+ "panamaischer balboa": [
+ "PAB"
+ ],
+ "ethiopian birr": [
+ "ETB"
+ ],
+ "kubai konvertibilis peso": [
+ "CUC"
+ ],
+ "clp": [
+ "CLP"
+ ],
+ "florin d'aruba": [
+ "AWG"
+ ],
+ "dolar bahames": [
+ "BSD"
+ ],
+ "ouguiya mauritanien": [
+ "MRO"
+ ],
+ "salomonen dollar": [
+ "SBD"
+ ],
+ "chavito": [
+ "CUC"
+ ],
+ "kanadai dolla\u0301r": [
+ "CAD"
+ ],
+ "britische pfund": [
+ "GBP"
+ ],
+ "singaporese dollar": [
+ "SGD"
+ ],
+ "chinese renminbi": [
+ "CNY"
+ ],
+ "saudische riyal": [
+ "SAR"
+ ],
+ "neuer taiwan dollar": [
+ "TWD"
+ ],
+ "do\u0301lar taiwanes": [
+ "TWD"
+ ],
+ "keniaanse shilling": [
+ "KES"
+ ],
+ "do\u0301lar de bahamas": [
+ "BSD"
+ ],
+ "bhutanese ngultrum": [
+ "BTN"
+ ],
+ "corona noruega": [
+ "NOK"
+ ],
+ "dollaro giamaicano": [
+ "JMD"
+ ],
+ "afgani afgano": [
+ "AFN"
+ ],
+ "pab": [
+ "PAB"
+ ],
+ "aruba florin": [
+ "AWG"
+ ],
+ "tajikistani ruble": [
+ "TJR"
+ ],
+ "franzo\u0308sischer franc": [
+ "FRF"
+ ],
+ "lira italiana": [
+ "ITL"
+ ],
+ "$ can": [
+ "CAD"
+ ],
+ "marco de la rda": [
+ "DDM"
+ ],
+ "ostkaribische wa\u0308hrungsunion": [
+ "XCD"
+ ],
+ "naf": [
+ "ANG"
+ ],
+ "drtrigonbot:exchange rate data:jpy": [
+ "JPY"
+ ],
+ "afghaanse afghani": [
+ "AFN"
+ ],
+ "peruviaanse sol": [
+ "PEN"
+ ],
+ "livre de sainte he\u0301le\u0300ne": [
+ "SHP"
+ ],
+ "sa\u0303o tome\u0301 and pri\u0301ncipe dobra": [
+ "STD"
+ ],
+ "co\u0301rdoba oro": [
+ "NIO"
+ ],
+ "moneda nacional": [
+ "CUP"
+ ],
+ "macanese pataca": [
+ "MOP"
+ ],
+ "couronne tcheque": [
+ "CZK"
+ ],
+ "chelin ugande\u0301s": [
+ "UGX"
+ ],
+ "peso cubano convertible": [
+ "CUC"
+ ],
+ "eritreai nakfa": [
+ "ERN"
+ ],
+ "ira\u0301ni ria\u0301l": [
+ "IRR"
+ ],
+ "dollar canadien": [
+ "CAD"
+ ],
+ "litouwse litas": [
+ "LTL"
+ ],
+ "venezuelan boli\u0301var": [
+ "VEF"
+ ],
+ "lib$": [
+ "LRD"
+ ],
+ "cheli\u0301n keniata": [
+ "KES"
+ ],
+ "riyal saoudien": [
+ "SAR"
+ ],
+ "usbekistan sum": [
+ "UZS"
+ ],
+ "chelin keniata": [
+ "KES"
+ ],
+ "peso cubano convertibile": [
+ "CUC"
+ ],
+ "euros": [
+ "EUR"
+ ],
+ "dollar des bermudes": [
+ "BMD"
+ ],
+ "liberianischer dollar": [
+ "LRD"
+ ],
+ "peso convertibile": [
+ "CUC"
+ ],
+ "grd": [
+ "GRD"
+ ],
+ "tschechoslowakische krone": [
+ "CSK"
+ ],
+ "tongan pa'anga": [
+ "TOP"
+ ],
+ "szamoai tala": [
+ "WST"
+ ],
+ "namibischer dollar": [
+ "NAD"
+ ],
+ "manat azerbai\u0308djanais": [
+ "AZN"
+ ],
+ "real": [
+ "BRL"
+ ],
+ "tanzanian shilingi": [
+ "TZS"
+ ],
+ "dollar liberien": [
+ "LRD"
+ ],
+ "do\u0301lar neocelandes": [
+ "NZD"
+ ],
+ "do\u0301lar taiwane\u0301s": [
+ "TWD"
+ ],
+ "dinaro del bahrein": [
+ "BHD"
+ ],
+ "florin hu\u0301ngaro": [
+ "HUF"
+ ],
+ "zambian kwacha": [
+ "ZMW"
+ ],
+ "dracma greca": [
+ "GRD"
+ ],
+ "italian lira": [
+ "ITL"
+ ],
+ "antilliaanse gulden": [
+ "ANG"
+ ],
+ "som uzbeco": [
+ "UZS"
+ ],
+ "yuan renminbi": [
+ "CNY"
+ ],
+ "tenge kazajo": [
+ "KZT"
+ ],
+ "dolar trinitense": [
+ "TTD"
+ ],
+ "dollaro bahamense": [
+ "BSD"
+ ],
+ "yeni kurus\u0327": [
+ "TRY"
+ ],
+ "brunei dollar": [
+ "BND"
+ ],
+ "lek albanes": [
+ "ALL"
+ ],
+ "yua\u0301n chino": [
+ "CNY"
+ ],
+ "\u20adn": [
+ "LAK"
+ ],
+ "som kirgui\u0301s": [
+ "KGS"
+ ],
+ "britse pond": [
+ "GBP"
+ ],
+ "\u20b4": [
+ "UAH"
+ ],
+ "nuevo dolar de taiwa\u0301n": [
+ "TWD"
+ ],
+ "dominican peso": [
+ "DOP"
+ ],
+ "mosambikanischer escudo": [
+ "MZE"
+ ],
+ "do\u0301lar de las islas caima\u0301n": [
+ "KYD"
+ ],
+ "gibraltar pfund": [
+ "GIP"
+ ],
+ "lats leto\u0301n": [
+ "LVL"
+ ],
+ "kanadische dollar": [
+ "CAD"
+ ],
+ "srd": [
+ "SRD"
+ ],
+ "sre": [
+ "SCR"
+ ],
+ "comore i frank": [
+ "KMF"
+ ],
+ "peso colombiano": [
+ "COP"
+ ],
+ "leke\u0308": [
+ "ALL"
+ ],
+ "\u0433\u0440\u0438\u0432\u043d\u044f": [
+ "UAH"
+ ],
+ "alu chip": [
+ "DDM"
+ ],
+ "kanadischer dollar": [
+ "CAD"
+ ],
+ "suriname dollar": [
+ "SRD"
+ ],
+ "corona ceca": [
+ "CZK"
+ ],
+ "serbischer dinar": [
+ "RSD"
+ ],
+ "dollar de brune\u0301i": [
+ "BND"
+ ],
+ "denar": [
+ "MKD"
+ ],
+ "dinar macedonio": [
+ "MKD"
+ ],
+ "lira maltese": [
+ "MTL"
+ ],
+ "frans geld": [
+ "FRF"
+ ],
+ "naira nigeriana": [
+ "NGN"
+ ],
+ "nuevo do\u0301lar taiwanes": [
+ "TWD"
+ ],
+ "dollaro neozelandese": [
+ "NZD"
+ ],
+ "dinar bahrei\u0308nien": [
+ "BHD"
+ ],
+ "zweedse kroon": [
+ "SEK"
+ ],
+ "swedish krona": [
+ "SEK"
+ ],
+ "new israeli shekel": [
+ "ILS"
+ ],
+ "leu moldave": [
+ "MDL"
+ ],
+ "rupia de nepal": [
+ "NPR"
+ ],
+ "leu moldavo": [
+ "MDL"
+ ],
+ "fidzsi dolla\u0301r": [
+ "FJD"
+ ],
+ "pula": [
+ "BWP"
+ ],
+ "drachmai": [
+ "GRD"
+ ],
+ "marco bosnio": [
+ "BAM"
+ ],
+ "roupie seychelloise": [
+ "SCR"
+ ],
+ "u\u0308zbe\u0301g szom": [
+ "UZS"
+ ],
+ "tanzanian schilling": [
+ "TZS"
+ ],
+ "gib\u00a3": [
+ "GIP"
+ ],
+ "lett lat": [
+ "LVL"
+ ],
+ "kc\u030cs": [
+ "CSK"
+ ],
+ "mark der deutschen demokratischen republik": [
+ "DDM"
+ ],
+ "yeni tu\u0308rk liras\u0131": [
+ "TRY"
+ ],
+ "\u3012": [
+ "KZT"
+ ],
+ "bosnische convertibele mark": [
+ "BAM"
+ ],
+ "libra siria": [
+ "SYP"
+ ],
+ "peso oro": [
+ "DOP"
+ ],
+ "rupia indonesia": [
+ "IDR"
+ ],
+ "pakistaanse rupee": [
+ "PKR"
+ ],
+ "riel cambogiano": [
+ "KHR"
+ ],
+ "haitian gourde": [
+ "HTG"
+ ],
+ "tschechische wa\u0308hrung": [
+ "CZK"
+ ],
+ "bosnia and herzegovina convertible mark": [
+ "BAM"
+ ],
+ "francs franc\u0327ais": [
+ "FRF"
+ ],
+ "griechische drachme": [
+ "GRD"
+ ],
+ "nuovo sol": [
+ "PEN"
+ ],
+ "swiss franc": [
+ "CHF"
+ ],
+ "swiss frank": [
+ "CHF"
+ ],
+ "somoni tayiko": [
+ "TJS"
+ ],
+ "rial yemeni\u0301": [
+ "YER"
+ ],
+ "nueva lira turca": [
+ "TRY"
+ ],
+ "engelse pond": [
+ "GBP"
+ ],
+ "chelin tanzano": [
+ "TZS"
+ ],
+ "peso de repu\u0301blica dominicana": [
+ "DOP"
+ ],
+ "dalasi gambese": [
+ "GMD"
+ ],
+ "nicaraguaanse co\u0301rdoba": [
+ "NIO"
+ ],
+ "lira libanese": [
+ "LBP"
+ ],
+ "baht tailandes": [
+ "THB"
+ ],
+ "khoum": [
+ "MRO"
+ ],
+ "lek albane\u0301s": [
+ "ALL"
+ ],
+ "botswanischer pula": [
+ "BWP"
+ ],
+ "dinar mace\u0301donien": [
+ "MKD"
+ ],
+ "dollar": [
+ "USD"
+ ],
+ "dolar bahame\u0301s": [
+ "BSD"
+ ],
+ "\u20ac": [
+ "EUR"
+ ],
+ "dollar singapourien": [
+ "SGD"
+ ],
+ "israe\u0308lische sjekel": [
+ "ILS"
+ ],
+ "wo\u0306n surcoreano": [
+ "KRW"
+ ],
+ "ukra\u0301n hrivnya": [
+ "UAH"
+ ],
+ "dinar algerien": [
+ "DZD"
+ ],
+ "cedi ghanese": [
+ "GHS"
+ ],
+ "cfa franc bceao": [
+ "XOF"
+ ],
+ "scr": [
+ "SCR"
+ ],
+ "\u0442\u04e9\u0433\u0440\u04e9\u0433": [
+ "MNT"
+ ],
+ "izlandi korona": [
+ "ISK"
+ ],
+ "englisches pfund": [
+ "GBP"
+ ],
+ "ws$": [
+ "WST"
+ ],
+ "wikipedia:raadsel/netties20070405": [
+ "GRD"
+ ],
+ "dolar neozelande\u0301s": [
+ "NZD"
+ ],
+ "samoanischer tala": [
+ "WST"
+ ],
+ "syrisch pond": [
+ "SYP"
+ ],
+ "caymaneilandse dollar": [
+ "KYD"
+ ],
+ "cordoba oro": [
+ "NIO"
+ ],
+ "kina papuana": [
+ "PGK"
+ ],
+ "szent ilona i font": [
+ "SHP"
+ ],
+ "sudanese pound": [
+ "SDG"
+ ],
+ "gourde haitiano": [
+ "HTG"
+ ],
+ "dollar hongkongais": [
+ "HKD"
+ ],
+ "haiti gourde": [
+ "HTG"
+ ],
+ "eyrir": [
+ "ISK"
+ ],
+ "australes": [
+ "ARA"
+ ],
+ "livres turques": [
+ "TRY"
+ ],
+ "dollar barbadien": [
+ "BBD"
+ ],
+ "congolese franc": [
+ "CDF"
+ ],
+ "wst": [
+ "WST"
+ ],
+ "t$": [
+ "TOP"
+ ],
+ "congolese frank": [
+ "CDF"
+ ],
+ "nafka": [
+ "ERN"
+ ],
+ "dansk krone": [
+ "DKK"
+ ],
+ "jordanischer dinar": [
+ "JOD"
+ ],
+ "dolar de bahamas": [
+ "BSD"
+ ],
+ "brasilianischer real": [
+ "BRL"
+ ],
+ "nz$": [
+ "NZD"
+ ],
+ "leone sierra le\u0301onais": [
+ "SLL"
+ ],
+ "tunesische dinar": [
+ "TND"
+ ],
+ "do\u0301lar namibio": [
+ "NAD"
+ ],
+ "$ca": [
+ "CAD"
+ ],
+ "bengalese taka": [
+ "BDT"
+ ],
+ "dollar fidjien": [
+ "FJD"
+ ],
+ "ungarischer forint": [
+ "HUF"
+ ],
+ "dinar serbe": [
+ "RSD"
+ ],
+ "do\u0301lar de trinidad y tobago": [
+ "TTD"
+ ],
+ "belize dollar": [
+ "BZD"
+ ],
+ "sum": [
+ "UZS"
+ ],
+ "franc rwandais": [
+ "RWF"
+ ],
+ "dinar jordanien": [
+ "JOD"
+ ],
+ "moldauischer leu": [
+ "MDL"
+ ],
+ "dolar de las islas salomon": [
+ "SBD"
+ ],
+ "lire italienne": [
+ "ITL"
+ ],
+ "ang": [
+ "ANG"
+ ],
+ "\u0e3f": [
+ "THB"
+ ],
+ "sucre": [
+ "XSU"
+ ],
+ "kzt": [
+ "KZT"
+ ],
+ "kronor": [
+ "SEK"
+ ],
+ "somalische shilling": [
+ "SOS"
+ ],
+ "dollaro namibiano": [
+ "NAD"
+ ],
+ "omanischer rial": [
+ "OMR"
+ ],
+ "do\u0301lar bermuden\u0303o": [
+ "BMD"
+ ],
+ "marka": [
+ "BAM"
+ ],
+ "marco convertibile": [
+ "BAM"
+ ],
+ "rublo ruso": [
+ "RUB"
+ ],
+ "uae dirham": [
+ "AED"
+ ],
+ "vae dirham": [
+ "AED"
+ ],
+ "ngultrum del bhutan": [
+ "BTN"
+ ],
+ "samoaanse tala": [
+ "WST"
+ ],
+ "maltesische lira": [
+ "MTL"
+ ],
+ "couronne norvegienne": [
+ "NOK"
+ ],
+ "franc burundais": [
+ "BIF"
+ ],
+ "flori\u0301n arubeno": [
+ "AWG"
+ ],
+ "georgian kupon lari": [
+ "GEL"
+ ],
+ "dollar de trinidad et tobago": [
+ "TTD"
+ ],
+ "t\u0323a\u0304ka\u0304": [
+ "BDT"
+ ],
+ "tonga pa\u02bbanga": [
+ "TOP"
+ ],
+ "dinar kuwaiti": [
+ "KWD"
+ ],
+ "kenia schilling": [
+ "KES"
+ ],
+ "\u20a1": [
+ "CRC"
+ ],
+ "guarani paraguayen": [
+ "PYG"
+ ],
+ "lats letton": [
+ "LVL"
+ ],
+ "quetzal guate\u0301malte\u0300que": [
+ "GTQ"
+ ],
+ "netherlands antillean guilder": [
+ "ANG"
+ ],
+ "balboa panamen\u0303o": [
+ "PAB"
+ ],
+ "dolar de brune\u0301i": [
+ "BND"
+ ],
+ "sheqel": [
+ "ILS"
+ ],
+ "escudo capoverdiano": [
+ "CVE"
+ ],
+ "boli\u0301var fuerte": [
+ "VEF"
+ ],
+ "franco della guinea": [
+ "GNF"
+ ],
+ "boli\u0301var": [
+ "VEF"
+ ],
+ "lilangeni swazilandais": [
+ "SZL"
+ ],
+ "dracma griega moderna": [
+ "GRD"
+ ],
+ "tenge kazako": [
+ "KZT"
+ ],
+ "tenge kazakh": [
+ "KZT"
+ ],
+ "mexican centavo": [
+ "MXN"
+ ],
+ "peso uruguaiano": [
+ "UYU"
+ ],
+ "franco cfp": [
+ "XPF"
+ ],
+ "so'm": [
+ "UZS"
+ ],
+ "drtrigonbot:exchange rate data:chf": [
+ "CHF"
+ ],
+ "konvertible mark": [
+ "BAM"
+ ],
+ "nouveau manat aze\u0301ri": [
+ "AZN"
+ ],
+ "nordjemenitischer rial": [
+ "YER"
+ ],
+ "bolivares": [
+ "VEF"
+ ],
+ "\u043b\u0435\u0432": [
+ "BGN"
+ ],
+ "deg": [
+ "XDR"
+ ],
+ "guarani paraguaiano": [
+ "PYG"
+ ],
+ "scellino keniano": [
+ "KES"
+ ],
+ "f$": [
+ "FJD"
+ ],
+ "couronne islandaise": [
+ "ISK"
+ ],
+ "dollar de la barbade": [
+ "BBD"
+ ],
+ "macause pataca": [
+ "MOP"
+ ],
+ "do\u0301lar bermudeno": [
+ "BMD"
+ ],
+ "isk": [
+ "ISK"
+ ],
+ "west african cfa franc": [
+ "XOF"
+ ],
+ "armeense dram": [
+ "AMD"
+ ],
+ "renminbi yuan": [
+ "CNY"
+ ],
+ "aussie dollar": [
+ "AUD"
+ ],
+ "franco francese": [
+ "FRF"
+ ],
+ "tetradrachmon": [
+ "GRD"
+ ],
+ "dinar irakien": [
+ "IQD"
+ ],
+ "tongan pa\u02bbanga": [
+ "TOP"
+ ],
+ "fr": [
+ "FRF"
+ ],
+ "ft": [
+ "HUF"
+ ],
+ "nuevo sol": [
+ "PEN"
+ ],
+ "peso convertible argentino": [
+ "ARS"
+ ],
+ "ff": [
+ "FRF"
+ ],
+ "dollar de taiwan": [
+ "TWD"
+ ],
+ "azerbaijani manat": [
+ "AZN"
+ ],
+ "dirham": [
+ "AED"
+ ],
+ "antillen gulden": [
+ "ANG"
+ ],
+ "lari ge\u0301orgien": [
+ "GEL"
+ ],
+ "fijian dollar": [
+ "FJD"
+ ],
+ "mark convertible de bosnie herze\u0301govine": [
+ "BAM"
+ ],
+ "nuovo siclo israeliano": [
+ "ILS"
+ ],
+ "bhuta\u0301ni ngultrum": [
+ "BTN"
+ ],
+ "guarani\u0301 paraguayen": [
+ "PYG"
+ ],
+ "jamaican dollar": [
+ "JMD"
+ ],
+ "rupia": [
+ "LKR",
+ "SCR",
+ "INR",
+ "NPR"
+ ],
+ "dinar libyen": [
+ "LYD"
+ ],
+ "dinaro giordano": [
+ "JOD"
+ ],
+ "paraguayan guarani\u0301": [
+ "PYG"
+ ],
+ "maldivische rufiyaa": [
+ "MVR"
+ ],
+ "marokkanischer dirham": [
+ "MAD"
+ ],
+ "franco pacifico": [
+ "XPF"
+ ],
+ "lats": [
+ "LVL"
+ ],
+ "forinto": [
+ "HUF"
+ ],
+ "dollar be\u0301lizien": [
+ "BZD"
+ ],
+ "forints": [
+ "HUF"
+ ],
+ "do\u0301lar bahameno": [
+ "BSD"
+ ],
+ "hrywen": [
+ "UAH"
+ ],
+ "roupie pakistanaise": [
+ "PKR"
+ ],
+ "rwf": [
+ "RWF"
+ ],
+ "iraanse rial": [
+ "IRR"
+ ],
+ "chetrum": [
+ "BTN"
+ ],
+ "do\u0301lar de las bahamas": [
+ "BSD"
+ ],
+ "lesothischer loti": [
+ "LSL"
+ ],
+ "djiboutian franc": [
+ "DJF"
+ ],
+ "soviet ruble": [
+ "SUR"
+ ],
+ "madagascan ariary": [
+ "MGA"
+ ],
+ "hryvna": [
+ "UAH"
+ ],
+ "komoren franc": [
+ "KMF"
+ ],
+ "sterlina britannica": [
+ "GBP"
+ ],
+ "sonderziehungsrecht": [
+ "XDR"
+ ],
+ "jamaicai dolla\u0301r": [
+ "JMD"
+ ],
+ "sierra leone i leone": [
+ "SLL"
+ ],
+ "laoszi kip": [
+ "LAK"
+ ],
+ "ma\u0301ltai li\u0301ra": [
+ "MTL"
+ ],
+ "dolar de fiji": [
+ "FJD"
+ ],
+ "dirham de los emiratos a\u0301rabes unidos": [
+ "AED"
+ ],
+ "dollaro della namibia": [
+ "NAD"
+ ],
+ "vn\u0111": [
+ "VND"
+ ],
+ "dollar des carai\u0308bes orientales": [
+ "XCD"
+ ],
+ "kelet karibi dolla\u0301r": [
+ "XCD"
+ ],
+ "dinar argelino": [
+ "DZD"
+ ],
+ "dolar de barbados": [
+ "BBD"
+ ],
+ "sbd": [
+ "SBD"
+ ],
+ "saoedische riyal": [
+ "SAR"
+ ],
+ "dinar bareini\u0301": [
+ "BHD"
+ ],
+ "do\u0301lar de guyana": [
+ "GYD"
+ ],
+ "won norcoreano": [
+ "KPW"
+ ],
+ "dram arme\u0301nien": [
+ "AMD"
+ ],
+ "peso de me\u0301jico": [
+ "MXN"
+ ],
+ "kuna": [
+ "HRK"
+ ],
+ "kubanischer peso": [
+ "CUP"
+ ],
+ "sambia kwacha": [
+ "ZMW"
+ ],
+ "sri lankaanse roepie": [
+ "LKR"
+ ],
+ "neue tu\u0308rkische lira": [
+ "TRY"
+ ],
+ "algerischer dinar": [
+ "DZD"
+ ],
+ "hong kong dollar": [
+ "HKD"
+ ],
+ "$a": [
+ "ARP"
+ ],
+ "rupia nepalese": [
+ "NPR"
+ ],
+ "bhat": [
+ "THB"
+ ],
+ "maleisische ringgit": [
+ "MYR"
+ ],
+ "rupia nepalesa": [
+ "NPR"
+ ],
+ "tsjechische kroon": [
+ "CZK"
+ ],
+ "dong": [
+ "VND"
+ ],
+ "xof": [
+ "XOF"
+ ],
+ "chilean peso": [
+ "CLP"
+ ],
+ "nordkoreanischer won": [
+ "KPW"
+ ],
+ "soedanese pond": [
+ "SDG"
+ ],
+ "angol font": [
+ "GBP"
+ ],
+ "kip laosiano": [
+ "LAK"
+ ],
+ "dollaro delle barbados": [
+ "BBD"
+ ],
+ "gpb": [
+ "GBP"
+ ],
+ "nuovo dollaro taiwanese": [
+ "TWD"
+ ],
+ "pond sterling": [
+ "GBP"
+ ],
+ "nouveau shekel": [
+ "ILS"
+ ],
+ "libanees pond": [
+ "LBP"
+ ],
+ "kuvaiti dina\u0301r": [
+ "KWD"
+ ],
+ "kenyan shilling": [
+ "KES"
+ ],
+ "dolar bahamen\u0303o": [
+ "BSD"
+ ],
+ "surinaamse gulden": [
+ "SRG"
+ ],
+ "tschang": [
+ "THB"
+ ],
+ "north korean won": [
+ "KPW"
+ ],
+ "fiorino ungherese": [
+ "HUF"
+ ],
+ "franco yibuti\u0301": [
+ "DJF"
+ ],
+ "servische dinar": [
+ "RSD"
+ ],
+ "manat turkme\u0300ne": [
+ "TMT"
+ ],
+ "swiss franken": [
+ "CHF"
+ ],
+ "costa rica colo\u0301n": [
+ "CRC"
+ ],
+ "franco yibutiense": [
+ "DJF"
+ ],
+ "venezolaanse boli\u0301var": [
+ "VEF"
+ ],
+ "marco de la repu\u0301blica democratica alemana": [
+ "DDM"
+ ],
+ "karod": [
+ "NPR"
+ ],
+ "riyal": [
+ "SAR"
+ ],
+ "birr e\u0301thiopien": [
+ "ETB"
+ ],
+ "francs pacifique": [
+ "XPF"
+ ],
+ "rufiyaa delle maldive": [
+ "MVR"
+ ],
+ "libyan dinar": [
+ "LYD"
+ ],
+ "siclo israeliano": [
+ "ILS"
+ ],
+ "santomese dobra": [
+ "STD"
+ ],
+ "mauritiaanse roepie": [
+ "MUR"
+ ],
+ "srilankaanse rupee": [
+ "LKR"
+ ],
+ "sum uzbeco": [
+ "UZS"
+ ],
+ "laari": [
+ "MVR"
+ ],
+ "dolar de trinidad y tobago": [
+ "TTD"
+ ],
+ "austral argentino": [
+ "ARA"
+ ],
+ "do\u0301lar fijiano": [
+ "FJD"
+ ],
+ "bz$": [
+ "BZD"
+ ],
+ "argentijnse peso": [
+ "ARS"
+ ],
+ "vnd": [
+ "VND"
+ ],
+ "dong vietnamien": [
+ "VND"
+ ],
+ "ngultrum butanes": [
+ "BTN"
+ ],
+ "do\u0301lar del caribe este": [
+ "XCD"
+ ],
+ "pakistaanse roepie": [
+ "PKR"
+ ],
+ "drtrigonbot:exchange rate data:usd": [
+ "USD"
+ ],
+ "indone\u0301z ru\u0301pia": [
+ "IDR"
+ ],
+ "riyal dell'oman": [
+ "OMR"
+ ],
+ "gambiai dalasi": [
+ "GMD"
+ ],
+ "dollaro delle salomone": [
+ "SBD"
+ ],
+ "bermuda dollar": [
+ "BMD"
+ ],
+ "km": [
+ "BAM"
+ ],
+ "kr": [
+ "DKK"
+ ],
+ "mozambican escudo": [
+ "MZE"
+ ],
+ "samoan tala": [
+ "WST"
+ ],
+ "brazil real": [
+ "BRL"
+ ],
+ "dollaro della guyana": [
+ "GYD"
+ ],
+ "norve\u0301g korona": [
+ "NOK"
+ ],
+ "dobra di sao tome\u0301 e principe": [
+ "STD"
+ ],
+ "cdf": [
+ "CDF"
+ ],
+ "azerbeidzjaanse manat": [
+ "AZN"
+ ],
+ "droits de tirage speciaux": [
+ "XDR"
+ ],
+ "paanga": [
+ "TOP"
+ ],
+ "livre des i\u0302les malouines": [
+ "FKP"
+ ],
+ "ugx": [
+ "UGX"
+ ],
+ "holland antilla\u0301kbeli forint": [
+ "ANG"
+ ],
+ "\u20a3": [
+ "FRF"
+ ],
+ "costa rican colo\u0301n": [
+ "CRC"
+ ],
+ "roupie indone\u0301sienne": [
+ "IDR"
+ ],
+ "rd$": [
+ "DOP"
+ ],
+ "dollar australien": [
+ "AUD"
+ ],
+ "russian ruble": [
+ "RUB"
+ ],
+ "mianmari kjap": [
+ "MMK"
+ ],
+ "nicaraguan co\u0301rdoba": [
+ "NIO"
+ ],
+ "florin aruben\u0303o": [
+ "AWG"
+ ],
+ "rupie indiane": [
+ "INR"
+ ],
+ "florin arubain": [
+ "AWG"
+ ],
+ "dinar kuwaiti\u0301": [
+ "KWD"
+ ],
+ "hryvnya": [
+ "UAH"
+ ],
+ "tamil rupee": [
+ "LKR"
+ ],
+ "oegandese shilling": [
+ "UGX"
+ ],
+ "corona cecoslovacca": [
+ "CSK"
+ ],
+ "clp$": [
+ "CLP"
+ ],
+ "cheli\u0301n ugandes": [
+ "UGX"
+ ],
+ "kina": [
+ "PGK"
+ ],
+ "noord koreaanse won": [
+ "KPW"
+ ],
+ "chilenischer peso": [
+ "CLP"
+ ],
+ "uganda schilling": [
+ "UGX"
+ ],
+ "uruguayaanse peso": [
+ "UYU"
+ ],
+ "metical": [
+ "MZN"
+ ],
+ "\u0440\u0443\u0431": [
+ "RUB"
+ ],
+ "marokko\u0301i dirham": [
+ "MAD"
+ ],
+ "ars": [
+ "ARS"
+ ],
+ "iraki dina\u0301r": [
+ "IQD"
+ ],
+ "tugrik mongolo": [
+ "MNT"
+ ],
+ "soedanees pond": [
+ "SDG"
+ ],
+ "honduran lempira": [
+ "HNL"
+ ],
+ "rial dell'oman": [
+ "OMR"
+ ],
+ "sek": [
+ "SEK"
+ ],
+ "franc malgache": [
+ "MGA"
+ ],
+ "fille\u0301r": [
+ "HUF"
+ ],
+ "piso": [
+ "PHP"
+ ],
+ "cayman islands dollar": [
+ "KYD"
+ ],
+ "guyaanse dollar": [
+ "GYD"
+ ],
+ "won": [
+ "KRW"
+ ],
+ "barbadosi dolla\u0301r": [
+ "BBD"
+ ],
+ "bosnische inwisselbare mark": [
+ "BAM"
+ ],
+ "\u20b8": [
+ "KZT"
+ ],
+ "dollar neo zelandais": [
+ "NZD"
+ ],
+ "leone sierraleonese": [
+ "SLL"
+ ],
+ "franco comorano": [
+ "KMF"
+ ],
+ "guineese frank": [
+ "GNF"
+ ],
+ "renminbi": [
+ "CNY"
+ ],
+ "alba\u0301n lek": [
+ "ALL"
+ ],
+ "ethiopische birr": [
+ "ETB"
+ ],
+ "sterlina di sant\u2019elena": [
+ "SHP"
+ ],
+ "corona islandesa": [
+ "ISK"
+ ],
+ "corona islandese": [
+ "ISK"
+ ],
+ "dolar bermudeno": [
+ "BMD"
+ ],
+ "surinamese dollar": [
+ "SRD"
+ ],
+ "nicaraguaanse cordoba": [
+ "NIO"
+ ],
+ "loti lesothiano": [
+ "LSL"
+ ],
+ "australischer dollar": [
+ "AUD"
+ ],
+ "canadian dollar": [
+ "CAD"
+ ],
+ "yen giapponese": [
+ "JPY"
+ ],
+ "mongolian to\u0308gro\u0308g": [
+ "MNT"
+ ],
+ "chelin ugandes": [
+ "UGX"
+ ],
+ "chinese yuan": [
+ "CNY"
+ ],
+ "shilling somalien": [
+ "SOS"
+ ],
+ "hongkongse dollar": [
+ "HKD"
+ ],
+ "bolivar": [
+ "VEF"
+ ],
+ "riyal yemenita": [
+ "YER"
+ ],
+ "florin des antilles ne\u0301erlandaises": [
+ "ANG"
+ ],
+ "\u20b9": [
+ "INR"
+ ],
+ "xaf": [
+ "XAF"
+ ],
+ "philippine peso": [
+ "PHP"
+ ],
+ "afghan afghani": [
+ "AFN"
+ ],
+ "dominikai peso": [
+ "DOP"
+ ],
+ "zuid koreaanse won": [
+ "KRW"
+ ],
+ "cubaanse peso": [
+ "CUP"
+ ],
+ "nepalese rupee": [
+ "NPR"
+ ],
+ "kyat birmano": [
+ "MMK"
+ ],
+ "franc or": [
+ "XFO"
+ ],
+ "fiorino surinamese": [
+ "SRG"
+ ],
+ "czech koruna": [
+ "CZK"
+ ],
+ "verenigde arabische emiraten dirham": [
+ "AED"
+ ],
+ "tanzaniaanse shilling": [
+ "TZS"
+ ],
+ "rupia mauriziana": [
+ "MUR"
+ ],
+ "monnaie canadienne": [
+ "CAD"
+ ],
+ "do\u0301lar bruneano": [
+ "BND"
+ ],
+ "koruna c\u030cesko slovenska\u0301": [
+ "CSK"
+ ],
+ "pound": [
+ "GBP"
+ ],
+ "pounds sterling": [
+ "GBP"
+ ],
+ "jpy": [
+ "JPY"
+ ],
+ "bs$": [
+ "BSD"
+ ],
+ "pula botswanais": [
+ "BWP"
+ ],
+ "haitiaanse gourde": [
+ "HTG"
+ ],
+ "dinar de bahrein": [
+ "BHD"
+ ],
+ "dollar jamaicain": [
+ "JMD"
+ ],
+ "peso ley": [
+ "ARS"
+ ],
+ "do\u0301lares neozelandeses": [
+ "NZD"
+ ],
+ "ten\u030cn\u030ce": [
+ "TMT"
+ ],
+ "pondteken": [
+ "GBP"
+ ],
+ "\u5143": [
+ "CNY"
+ ],
+ "franc uic": [
+ "XFU"
+ ],
+ "syp": [
+ "SYP"
+ ],
+ "dzsibuti frank": [
+ "DJF"
+ ],
+ "dollar de la jamai\u0308que": [
+ "JMD"
+ ],
+ "dinaro tunisino": [
+ "TND"
+ ],
+ "yuan": [
+ "CNY"
+ ],
+ "sudanesisches pfund": [
+ "SDG"
+ ],
+ "euro": [
+ "EUR"
+ ],
+ "peruanischer nuevo sol": [
+ "PEN"
+ ],
+ "falkland pound": [
+ "FKP"
+ ],
+ "forint hungaro": [
+ "HUF"
+ ],
+ "couronne suedoise": [
+ "SEK"
+ ],
+ "peso uruguayen": [
+ "UYU"
+ ],
+ "nami\u0301biai dolla\u0301r": [
+ "NAD"
+ ],
+ "do\u0301lar bahamen\u0303o": [
+ "BSD"
+ ],
+ "leone": [
+ "SLL"
+ ],
+ "libanon pfund": [
+ "LBP"
+ ],
+ "riyal saudi": [
+ "SAR"
+ ],
+ "mozambican metical": [
+ "MZN"
+ ],
+ "dollaro liberiano": [
+ "LRD"
+ ],
+ "dolar de guyana": [
+ "GYD"
+ ],
+ "brazilian real": [
+ "BRL"
+ ],
+ "do\u0301lar de las islas caiman": [
+ "KYD"
+ ],
+ "$": [
+ "USD",
+ "MXN",
+ "ARS",
+ "CAD"
+ ],
+ "cup": [
+ "CUP"
+ ],
+ "real brasilen\u0303o": [
+ "BRL"
+ ],
+ "peso mexicain": [
+ "MXN"
+ ],
+ "cuc": [
+ "CUC"
+ ],
+ "\u0433\u0440\u043d": [
+ "UAH"
+ ],
+ "monnaie franc\u0327aise": [
+ "FRF"
+ ],
+ "guarani\u0301 de paraguay": [
+ "PYG"
+ ],
+ "pa\u02bbanga": [
+ "TOP"
+ ],
+ "marco": [
+ "DDM"
+ ],
+ "panamese balboa": [
+ "PAB"
+ ],
+ "dolar caimano": [
+ "KYD"
+ ],
+ "feninga": [
+ "BAM"
+ ],
+ "kazah tenge": [
+ "KZT"
+ ],
+ "na\u0192": [
+ "ANG"
+ ],
+ "belgian congolese franc": [
+ "CDF"
+ ],
+ "jamaika dollar": [
+ "JMD"
+ ],
+ "to\u0308ro\u0308k u\u0301j li\u0301ra": [
+ "TRY"
+ ],
+ "nige\u0301riai naira": [
+ "NGN"
+ ],
+ "oude metical": [
+ "MZN"
+ ],
+ "singapur dollar": [
+ "SGD"
+ ],
+ "b$": [
+ "BSD"
+ ],
+ "metical del mozambico": [
+ "MZN"
+ ],
+ "ariary malgascio": [
+ "MGA"
+ ],
+ "bolivar venezuelano": [
+ "VEF"
+ ],
+ "corona norvegese": [
+ "NOK"
+ ],
+ "s/.": [
+ "PEN"
+ ],
+ "franco del burundi": [
+ "BIF"
+ ],
+ "yemeni rial": [
+ "YER"
+ ],
+ "dirham de emiratos arabes unidos": [
+ "AED"
+ ],
+ "riel": [
+ "KHR"
+ ],
+ "venezolanischer boli\u0301var": [
+ "VEF"
+ ],
+ "de\u0301l szuda\u0301ni font": [
+ "SSP"
+ ],
+ "\u20a4": [
+ "ITL"
+ ],
+ "dolar de brunei": [
+ "BND"
+ ],
+ "colo\u0301n costaricano": [
+ "CRC"
+ ],
+ "dinaro kuwaitiano": [
+ "KWD"
+ ],
+ "re\u0301aux bre\u0301siliens": [
+ "BRL"
+ ],
+ "pen": [
+ "PEN"
+ ],
+ "indiase roepie": [
+ "INR"
+ ],
+ "rupia delle seychelles": [
+ "SCR"
+ ],
+ "lari": [
+ "GEL"
+ ],
+ "dollaro di barbados": [
+ "BBD"
+ ],
+ "xang": [
+ "THB"
+ ],
+ "taiwanese dollar": [
+ "TWD"
+ ],
+ "paraguayi guarani\u0301": [
+ "PYG"
+ ],
+ "cambodian riel": [
+ "KHR"
+ ],
+ "rub": [
+ "RUB"
+ ],
+ "dinaro algerino": [
+ "DZD"
+ ],
+ "bs": [
+ "BSD",
+ "BOB"
+ ],
+ "syrisches pfund": [
+ "SYP"
+ ],
+ "rial iranien": [
+ "IRR"
+ ],
+ "dollar namibien": [
+ "NAD"
+ ],
+ "couronne tche\u0301coslovaque": [
+ "CSK"
+ ],
+ "couronne tchecoslovaque": [
+ "CSK"
+ ],
+ "peruvian nuevo sol": [
+ "PEN"
+ ],
+ "lat leton": [
+ "LVL"
+ ],
+ "costa ricaanse colon": [
+ "CRC"
+ ],
+ "schweizer franken": [
+ "CHF"
+ ],
+ "dollar tai\u0308wanais": [
+ "TWD"
+ ],
+ "japanese yen": [
+ "JPY"
+ ],
+ "malediven rupie": [
+ "MVR"
+ ],
+ "arubaanse florijn": [
+ "AWG"
+ ],
+ "grivna": [
+ "UAH"
+ ],
+ "ostkaribischer dollar": [
+ "XCD"
+ ],
+ "mkd": [
+ "MKD"
+ ],
+ "\u00a5": [
+ "JPY"
+ ],
+ "ci$": [
+ "KYD"
+ ],
+ "yuans": [
+ "CNY"
+ ],
+ "xpf": [
+ "XPF"
+ ],
+ "lao kip": [
+ "LAK"
+ ],
+ "franco congoleno": [
+ "CDF"
+ ],
+ "marco bosnioherzegovino": [
+ "BAM"
+ ],
+ "sdr": [
+ "XDR"
+ ],
+ "dollaro del belize": [
+ "BZD"
+ ],
+ "peso argentino": [
+ "ARP"
+ ],
+ "dinaro iracheno": [
+ "IQD"
+ ],
+ "hongkong dollar": [
+ "HKD"
+ ],
+ "guarani\u0301 paraguaiano": [
+ "PYG"
+ ],
+ "flori\u0301n antillano neerlande\u0301s": [
+ "ANG"
+ ],
+ "dirham marocain": [
+ "MAD"
+ ],
+ "rial irani": [
+ "IRR"
+ ],
+ "peso d'uruguay": [
+ "UYU"
+ ],
+ "forinto hu\u0301ngaro": [
+ "HUF"
+ ],
+ "escudo cap verdien": [
+ "CVE"
+ ],
+ "mongol tugrik": [
+ "MNT"
+ ],
+ "gha\u0301nai cedi": [
+ "GHS"
+ ],
+ "do\u0301lar del caribe oriental": [
+ "XCD"
+ ],
+ "riyal saudita": [
+ "SAR"
+ ],
+ "omani rial": [
+ "OMR"
+ ],
+ "dinar tunisien": [
+ "TND"
+ ],
+ "cape verdean escudo": [
+ "CVE"
+ ],
+ "peso do\u0301lar": [
+ "ARS"
+ ],
+ "dolar namibio": [
+ "NAD"
+ ],
+ "lyd": [
+ "LYD"
+ ],
+ "sint heleens pond": [
+ "SHP"
+ ],
+ "nieuwe israe\u0308lische sheqel": [
+ "ILS"
+ ],
+ "laotiaanse kip": [
+ "LAK"
+ ],
+ "bolivian boliviano": [
+ "BOB"
+ ],
+ "kirgizische som": [
+ "KGS"
+ ],
+ "denaro macedone": [
+ "MKD"
+ ],
+ "swiss franco": [
+ "CHF"
+ ],
+ "birr eti\u0301ope": [
+ "ETB"
+ ],
+ "barbadian dollar": [
+ "BBD"
+ ],
+ "dolar canadiense": [
+ "CAD"
+ ],
+ "swiss francs": [
+ "CHF"
+ ],
+ "tonga pa`anga": [
+ "TOP"
+ ],
+ "dinar de bahrei\u0308n": [
+ "BHD"
+ ],
+ "dollar des iles salomon": [
+ "SBD"
+ ],
+ "dobra santotomense": [
+ "STD"
+ ],
+ "leu rumano": [
+ "RON"
+ ],
+ "lisente": [
+ "LSL"
+ ],
+ "manat turcomano": [
+ "TMT"
+ ],
+ "taka bangladeshi": [
+ "BDT"
+ ],
+ "dram": [
+ "AMD"
+ ],
+ "macedonische denar": [
+ "MKD"
+ ],
+ "israelische sjekel": [
+ "ILS"
+ ],
+ "dop": [
+ "DOP"
+ ],
+ "vanuatu vatu": [
+ "VUV"
+ ],
+ "dollar des i\u0302les salomon": [
+ "SBD"
+ ],
+ "franzo\u0308sischer franken": [
+ "FRF"
+ ],
+ "guarani": [
+ "PYG"
+ ],
+ "su\u0308dsudan pfund": [
+ "SSP"
+ ],
+ "roemeense leu": [
+ "RON"
+ ],
+ "mark convertible": [
+ "BAM"
+ ],
+ "franco de djibouti": [
+ "DJF"
+ ],
+ "ugandan shilling": [
+ "UGX"
+ ],
+ "pazifik franc": [
+ "XPF"
+ ],
+ "rublo tayiko": [
+ "TJR"
+ ],
+ "argentinischer peso": [
+ "ARS"
+ ],
+ "bahraini dinar": [
+ "BHD"
+ ],
+ "amerikaanse dollar": [
+ "USD"
+ ],
+ "franc comorien": [
+ "KMF"
+ ],
+ "dolar neocelande\u0301s": [
+ "NZD"
+ ],
+ "libra sudanesa": [
+ "SDG"
+ ],
+ "ugandai shilling": [
+ "UGX"
+ ],
+ "peso argentin": [
+ "ARS"
+ ],
+ "tugrik mongol": [
+ "MNT"
+ ],
+ "fiorino delle antille olandesi": [
+ "ANG"
+ ],
+ "hryvnia": [
+ "UAH"
+ ],
+ "ma\u0308tonya": [
+ "ETB"
+ ],
+ "dalasi": [
+ "GMD"
+ ],
+ "couronne tche\u0300que": [
+ "CZK"
+ ],
+ "lkr": [
+ "LKR"
+ ],
+ "clps": [
+ "CLP"
+ ],
+ "dolar surinames": [
+ "SRD"
+ ],
+ "kuwait dinar": [
+ "KWD"
+ ],
+ "ruma\u0308nischer leu": [
+ "RON"
+ ],
+ "do\u0301lar jamaicano": [
+ "JMD"
+ ],
+ "nuevo dolar taiwane\u0301s": [
+ "TWD"
+ ],
+ "venezolanischer bolivar": [
+ "VEF"
+ ],
+ "qatarese rial": [
+ "QAR"
+ ],
+ "do\u0301lar de surinam": [
+ "SRD"
+ ],
+ "livres sterling": [
+ "GBP"
+ ],
+ "g$": [
+ "GYD"
+ ],
+ "ruma\u0308nischer lei": [
+ "RON"
+ ],
+ "leone della sierra leone": [
+ "SLL"
+ ],
+ "manat azero": [
+ "AZN"
+ ],
+ "rwandese frank": [
+ "RWF"
+ ],
+ "ancien franc": [
+ "FRF"
+ ],
+ "naira": [
+ "NGN"
+ ],
+ "koruna ceskoslovenska": [
+ "CSK"
+ ],
+ "colo\u0301n costarricense": [
+ "CRC"
+ ],
+ "kubai peso": [
+ "CUP"
+ ],
+ "riel camboyano": [
+ "KHR"
+ ],
+ "pa'anga tongano": [
+ "TOP"
+ ],
+ "sri lankan rupee": [
+ "LKR"
+ ],
+ "hk$": [
+ "HKD"
+ ],
+ "dollar libe\u0301rien": [
+ "LRD"
+ ],
+ "pa'anga di tonga": [
+ "TOP"
+ ],
+ "norwegian krone": [
+ "NOK"
+ ],
+ "scudo capoverdiano": [
+ "CVE"
+ ],
+ "franco congolese": [
+ "CDF"
+ ],
+ "birr": [
+ "ETB"
+ ],
+ "schwedische krone": [
+ "SEK"
+ ],
+ "boliviano bolivien": [
+ "BOB"
+ ],
+ "bdt": [
+ "BTN"
+ ],
+ "do\u0301lar guyanes": [
+ "GYD"
+ ],
+ "lilangeni dello swaziland": [
+ "SZL"
+ ],
+ "libanesisches pfund": [
+ "LBP"
+ ],
+ "schottische pfund": [
+ "GBP"
+ ],
+ "griekse drachme": [
+ "GRD"
+ ],
+ "moldovan leu": [
+ "MDL"
+ ],
+ "lek": [
+ "ALL"
+ ],
+ "\u00a3": [
+ "GBP"
+ ],
+ "do\u0301lar australiano": [
+ "AUD"
+ ],
+ "lev": [
+ "BGN"
+ ],
+ "lew": [
+ "BGN"
+ ],
+ "uganda shilling": [
+ "UGX"
+ ],
+ "hkd": [
+ "HKD"
+ ],
+ "bd$": [
+ "BMD"
+ ],
+ "re\u0301al bre\u0301silien": [
+ "BRL"
+ ],
+ "tunesischer dinar": [
+ "TND"
+ ],
+ "austral (monnaie)": [
+ "ARA"
+ ],
+ "tongaanse pa'anga": [
+ "TOP"
+ ],
+ "couronne sue\u0301doise": [
+ "SEK"
+ ],
+ "franc de djibouti": [
+ "DJF"
+ ],
+ "madagaszka\u0301ri ariary": [
+ "MGA"
+ ],
+ "rupia mauricia": [
+ "MUR"
+ ],
+ "solomon dollar": [
+ "SBD"
+ ],
+ "kro\u0301nur": [
+ "ISK"
+ ],
+ "khoums": [
+ "MRO"
+ ],
+ "su\u0308dsudan pound": [
+ "SSP"
+ ],
+ "sgd": [
+ "SGD"
+ ],
+ "russischer rubel": [
+ "RUB"
+ ],
+ "usd": [
+ "USD"
+ ],
+ "livre des i\u0302les falkland": [
+ "FKP"
+ ],
+ "comorian franc": [
+ "KMF"
+ ],
+ "chf": [
+ "CHF"
+ ],
+ "ush": [
+ "UGX"
+ ],
+ "costa rica colon": [
+ "CRC"
+ ],
+ "rial yemenita": [
+ "YER"
+ ],
+ "marco bosniaco": [
+ "BAM"
+ ],
+ "rial yemenite": [
+ "YER"
+ ],
+ "brit font": [
+ "GBP"
+ ],
+ "tercera dracma griega": [
+ "GRD"
+ ],
+ "tala samoano": [
+ "WST"
+ ],
+ "manat azeri": [
+ "AZN"
+ ],
+ "santi\u0304ms": [
+ "LVL"
+ ],
+ "ostmark": [
+ "DDM"
+ ],
+ "f": [
+ "ANG"
+ ],
+ "nuova lira turca": [
+ "TRY"
+ ],
+ "zuid soedanees pond": [
+ "SSP"
+ ],
+ "turkish lira": [
+ "TRY"
+ ],
+ "rupia indonesiana": [
+ "IDR"
+ ],
+ "da\u0308nische krone": [
+ "DKK"
+ ],
+ "diritti speciali di prelievo": [
+ "XDR"
+ ],
+ "do\u0301lar de nueva zelanda": [
+ "NZD"
+ ],
+ "aluchip": [
+ "DDM"
+ ],
+ "peso uruguayo": [
+ "UYU"
+ ],
+ "xcd": [
+ "XCD"
+ ],
+ "nuevo do\u0301lar de taiwan": [
+ "TWD"
+ ],
+ "k.s.": [
+ "KGS"
+ ],
+ "dinars alge\u0301rien": [
+ "DZD"
+ ],
+ "russische roebel": [
+ "RUB"
+ ],
+ "afn": [
+ "AFN"
+ ],
+ "\u20a6": [
+ "NGN"
+ ],
+ "corona danese": [
+ "DKK"
+ ],
+ "corona danesa": [
+ "DKK"
+ ],
+ "moneda canadiense": [
+ "CAD"
+ ],
+ "ruandai frank": [
+ "RWF"
+ ],
+ "libra de santa helena": [
+ "SHP"
+ ],
+ "manat azeri\u0301": [
+ "AZN"
+ ],
+ "do\u0301lar de hong kong": [
+ "HKD"
+ ],
+ "armenian dram": [
+ "AMD"
+ ],
+ "tetradrachme": [
+ "GRD"
+ ],
+ "chileense peso": [
+ "CLP"
+ ],
+ "franchi svizzeri": [
+ "CHF"
+ ],
+ "boliviaanse boliviano": [
+ "BOB"
+ ],
+ "do\u0301lar de bermudas": [
+ "BMD"
+ ],
+ "colon costaricain": [
+ "CRC"
+ ],
+ "dollar bahame\u0301en": [
+ "BSD"
+ ],
+ "dollaro delle cayman": [
+ "KYD"
+ ],
+ "do\u0301lar neozelande\u0301s": [
+ "NZD"
+ ],
+ "riyal saudi\u0301": [
+ "SAR"
+ ],
+ "georgian lari": [
+ "GEL"
+ ],
+ "kiwi dollar": [
+ "NZD"
+ ],
+ "shekkel": [
+ "ILS"
+ ],
+ "si$": [
+ "SBD"
+ ],
+ "dobra santome\u0301en": [
+ "STD"
+ ],
+ "dolar neoze\u0301landes": [
+ "NZD"
+ ],
+ "fiorino di aruba": [
+ "AWG"
+ ],
+ "dobra": [
+ "STD"
+ ],
+ "british pound": [
+ "GBP"
+ ],
+ "to\u0308mling": [
+ "THB"
+ ],
+ "afg": [
+ "AFN"
+ ],
+ "thai ba\u0301t": [
+ "THB"
+ ],
+ "fu\u0308lo\u0308p szigeteki peso": [
+ "PHP"
+ ],
+ "noorse kroon": [
+ "NOK"
+ ],
+ "dollar de trinite\u0301 et tobago": [
+ "TTD"
+ ],
+ "tsh": [
+ "TZS"
+ ],
+ "lm": [
+ "MTL"
+ ],
+ "saudi arabische riyal": [
+ "SAR"
+ ],
+ "ausztra\u0301l dolla\u0301r": [
+ "AUD"
+ ],
+ "oekraiense hryvnja": [
+ "UAH"
+ ],
+ "deense kroon": [
+ "DKK"
+ ],
+ "eur": [
+ "EUR"
+ ],
+ "uruguayi peso": [
+ "UYU"
+ ],
+ "liberian dollar": [
+ "LRD"
+ ],
+ "livre sud soudanaise": [
+ "SSP"
+ ],
+ "do\u0301lar de fiji": [
+ "FJD"
+ ],
+ "dollar de la carai\u0308be orientale": [
+ "XCD"
+ ],
+ "franc poincare\u0301": [
+ "XFO"
+ ],
+ "gepik": [
+ "AZN"
+ ],
+ "fl\u00a3": [
+ "FKP"
+ ],
+ "mexican peso": [
+ "MXN"
+ ],
+ "diram": [
+ "TJS"
+ ],
+ "denar mace\u0301donien": [
+ "MKD"
+ ],
+ "hongkongi dolla\u0301r": [
+ "HKD"
+ ],
+ "belizaanse dollar": [
+ "BZD"
+ ],
+ "azeri manat": [
+ "AZN"
+ ],
+ "dong vietnamita": [
+ "VND"
+ ],
+ "rublo russo": [
+ "RUB"
+ ],
+ "dolar beliceno": [
+ "BZD"
+ ],
+ "su\u0308dsudanesisches pfund": [
+ "SSP"
+ ],
+ "dolar de las islas caima\u0301n": [
+ "KYD"
+ ],
+ "ec$": [
+ "XCD"
+ ],
+ "dirham degli emirati arabi uniti": [
+ "AED"
+ ],
+ "surinaamse dollar": [
+ "SRD"
+ ],
+ "franco cfa de africa occidental": [
+ "XOF"
+ ],
+ "french franc": [
+ "FRF"
+ ],
+ "\u0192": [
+ "ANG"
+ ],
+ "roma\u0301n lej": [
+ "RON"
+ ],
+ "pa'anga": [
+ "TOP"
+ ],
+ "dollaro dei caraibi orientali": [
+ "XCD"
+ ],
+ "tyiyn": [
+ "KGS"
+ ],
+ "cuban convertible peso": [
+ "CUC"
+ ],
+ "dirham des e\u0301mirats arabes unis": [
+ "AED"
+ ],
+ "japa\u0301n jen": [
+ "JPY"
+ ],
+ "kroatische kuna": [
+ "HRK"
+ ],
+ "sowjetischer rubel": [
+ "SUR"
+ ],
+ "won sudcoreano": [
+ "KRW"
+ ],
+ "chelin somali\u0301": [
+ "SOS"
+ ],
+ "santims": [
+ "LVL"
+ ],
+ "franc": [
+ "CHF",
+ "FRF"
+ ],
+ "halalas": [
+ "SAR"
+ ],
+ "sva\u0301jci frank": [
+ "CHF"
+ ],
+ "shekel": [
+ "ILS"
+ ],
+ "dinar kowei\u0308tien": [
+ "KWD"
+ ],
+ "l\u00a3": [
+ "LBP"
+ ],
+ "moroccan dirham": [
+ "MAD"
+ ],
+ "goldfranc": [
+ "XFO"
+ ],
+ "jod": [
+ "JOD"
+ ],
+ "oost carai\u0308bische dollar": [
+ "XCD"
+ ],
+ "ouguiya mauritana": [
+ "MRO"
+ ],
+ "cambodjaanse riel": [
+ "KHR"
+ ],
+ "taka bangladesi\u0301": [
+ "BDT"
+ ],
+ "ltl": [
+ "LTL"
+ ],
+ "lettischer lat": [
+ "LVL"
+ ],
+ "santi\u0304mu": [
+ "LVL"
+ ],
+ "marco de la repu\u0301blica democra\u0301tica alemana": [
+ "DDM"
+ ],
+ "franco di gibuti": [
+ "DJF"
+ ],
+ "santi\u0304mi": [
+ "LVL"
+ ],
+ "couronne norve\u0301gienne": [
+ "NOK"
+ ],
+ "libanoni font": [
+ "LBP"
+ ],
+ "belize i dolla\u0301r": [
+ "BZD"
+ ],
+ "da\u0301n korona": [
+ "DKK"
+ ],
+ "serbian dinar": [
+ "RSD"
+ ],
+ "rial omani": [
+ "OMR"
+ ],
+ "mark convertible bosniaque": [
+ "BAM"
+ ],
+ "dollar du be\u0301lize": [
+ "BZD"
+ ],
+ "pesos argentinos": [
+ "ARS"
+ ],
+ "lesothaanse loti": [
+ "LSL"
+ ],
+ "tu\u0308rk liras\u0131": [
+ "TRY"
+ ],
+ "kwacha zambien": [
+ "ZMW"
+ ],
+ "dollar trinidadien": [
+ "TTD"
+ ],
+ "moldavische leu": [
+ "MDL"
+ ],
+ "tughrik": [
+ "MNT"
+ ],
+ "leu roumain": [
+ "RON"
+ ],
+ "szva\u0301zifo\u0308ldi lilangeni": [
+ "SZL"
+ ],
+ "morocota": [
+ "VEF"
+ ],
+ "haitianische gourde": [
+ "HTG"
+ ],
+ "eritreischer nakfa": [
+ "ERN"
+ ],
+ "mongolischer to\u0308gro\u0308g": [
+ "MNT"
+ ],
+ "escudo di capo verde": [
+ "CVE"
+ ],
+ "zwitserse frank": [
+ "CHF"
+ ],
+ "afga\u0301n afga\u0301ni": [
+ "AFN"
+ ],
+ "neet": [
+ "GBP"
+ ],
+ "zwitserse franc": [
+ "CHF"
+ ],
+ "roupie mauricienne": [
+ "MUR"
+ ],
+ "do\u0301lar trinitense": [
+ "TTD"
+ ],
+ "marco de la republica democratica alemana": [
+ "DDM"
+ ],
+ "tongai pa\u2019anga": [
+ "TOP"
+ ],
+ "israeli new sheqel": [
+ "ILS"
+ ],
+ "bermudai dolla\u0301r": [
+ "BMD"
+ ],
+ "\u20ba": [
+ "TRY"
+ ],
+ "oost caribische dollar": [
+ "XCD"
+ ],
+ "ugandese shilling": [
+ "UGX"
+ ],
+ "derechos especiales de giro": [
+ "XDR"
+ ],
+ "rupaya": [
+ "INR"
+ ],
+ "suriname gulden": [
+ "SRD"
+ ],
+ "tajvani u\u0301j dolla\u0301r": [
+ "TWD"
+ ],
+ "costa rica i colo\u0301n": [
+ "CRC"
+ ],
+ "pakistanische rupie": [
+ "PKR"
+ ],
+ "irak dinar": [
+ "IQD"
+ ],
+ "alge\u0301riai dina\u0301r": [
+ "DZD"
+ ],
+ "perui u\u0301j sol": [
+ "PEN"
+ ],
+ "do\u0301lar caribe este": [
+ "XCD"
+ ],
+ "kurus": [
+ "TRY"
+ ],
+ "sfr": [
+ "CHF"
+ ],
+ "huard canadien": [
+ "CAD"
+ ],
+ "new zealand dollar": [
+ "NZD"
+ ],
+ "so\u0308m": [
+ "UZS"
+ ],
+ "awg": [
+ "AWG"
+ ],
+ "dollar de guyana": [
+ "GYD"
+ ],
+ "bosnya\u0301k konvertibilis ma\u0301rka": [
+ "BAM"
+ ],
+ "suriname i dolla\u0301r": [
+ "SRD"
+ ],
+ "ukrainische hrywnja": [
+ "UAH"
+ ],
+ "ngultrum": [
+ "BTN"
+ ],
+ "gde.": [
+ "HTG"
+ ],
+ "mexican nuevo peso": [
+ "MXN"
+ ],
+ "fjd": [
+ "FJD"
+ ],
+ "dolar jamaiquino": [
+ "JMD"
+ ],
+ "libyscher dinar": [
+ "LYD"
+ ],
+ "nuevo shequel": [
+ "ILS"
+ ],
+ "cheli\u0301n keniano": [
+ "KES"
+ ],
+ "dollar surinamien": [
+ "SRD"
+ ],
+ "rublo sovietico": [
+ "SUR"
+ ],
+ "kaiman dollar": [
+ "KYD"
+ ],
+ "dollar ne\u0301o ze\u0301landais": [
+ "NZD"
+ ],
+ "bolga\u0301r leva": [
+ "BGN"
+ ],
+ "cub$": [
+ "CUP"
+ ],
+ "szl": [
+ "SZL"
+ ],
+ "aruba gulden": [
+ "AWG"
+ ],
+ "mexikanischer peso": [
+ "MXN"
+ ],
+ "australische dollar": [
+ "AUD"
+ ],
+ "roupie indonesienne": [
+ "IDR"
+ ],
+ "albanese lek": [
+ "ALL"
+ ],
+ "lettische wa\u0308hrung": [
+ "LVL"
+ ],
+ "dollar ame\u0301ricain": [
+ "USD"
+ ],
+ "zo\u0308ld foki szigeteki escudo": [
+ "CVE"
+ ],
+ "saudi riyal": [
+ "SAR"
+ ],
+ "libra": [
+ "GBP"
+ ],
+ "isla\u0308ndische krone": [
+ "ISK"
+ ],
+ "saudi rial": [
+ "SAR"
+ ],
+ "dollaro della bermuda": [
+ "BMD"
+ ],
+ "macedo\u0301n de\u0301na\u0301r": [
+ "MKD"
+ ],
+ "kwanza": [
+ "AOA"
+ ],
+ "dollar du guyana": [
+ "GYD"
+ ],
+ "nuevo peso argentino": [
+ "ARS"
+ ],
+ "dollaro del suriname": [
+ "SRD"
+ ],
+ "ariary malgache": [
+ "MGA"
+ ],
+ "saint helena pound": [
+ "SHP"
+ ],
+ "kambodzsai riel": [
+ "KHR"
+ ],
+ "surinam dollar": [
+ "SRD"
+ ],
+ "ouguiya": [
+ "MRO"
+ ],
+ "mala\u0301j ringgit": [
+ "MYR"
+ ],
+ "united states dollar": [
+ "USD"
+ ],
+ "icelandic kro\u0301na": [
+ "ISK"
+ ],
+ "gbp": [
+ "GBP"
+ ],
+ "falkland szigeteki font": [
+ "FKP"
+ ],
+ "sa\u0303o tome\u0301ischer dobra": [
+ "STD"
+ ],
+ "kwanza angolano": [
+ "AOA"
+ ],
+ "scellino": [
+ "KES"
+ ],
+ "dollars canadiens": [
+ "CAD"
+ ],
+ "guarani\u0301": [
+ "PYG"
+ ],
+ "kwanza angolana": [
+ "AOA"
+ ],
+ "litas lituanien": [
+ "LTL"
+ ],
+ "kajma\u0301n szigeteki dolla\u0301r": [
+ "KYD"
+ ],
+ "som de kirguista\u0301n": [
+ "KGS"
+ ],
+ "btn": [
+ "BTN"
+ ],
+ "chelin somali": [
+ "SOS"
+ ],
+ "dracma griego moderno": [
+ "GRD"
+ ],
+ "hai\u0308tiaanse gourde": [
+ "HTG"
+ ],
+ "kc\u030c": [
+ "CZK"
+ ],
+ "peso de chile": [
+ "CLP"
+ ],
+ "mazedonischer denar": [
+ "MKD"
+ ],
+ "sierra leoonse leone": [
+ "SLL"
+ ],
+ "franco france\u0301s": [
+ "FRF"
+ ],
+ "marco della germania est": [
+ "DDM"
+ ],
+ "cordoba nicaraguense": [
+ "NIO"
+ ],
+ "do\u0301lar jamaiquino": [
+ "JMD"
+ ],
+ "cordoba nicaraguayen": [
+ "NIO"
+ ],
+ "rupia de pakista\u0301n": [
+ "PKR"
+ ],
+ "pfund sterling": [
+ "GBP"
+ ],
+ "dollar jamai\u0308quain": [
+ "JMD"
+ ],
+ "koruna c\u030ceskoslovenska\u0301": [
+ "CSK"
+ ],
+ "vatu di vanuatu": [
+ "VUV"
+ ],
+ "nicaraguai co\u0301rdoba": [
+ "NIO"
+ ],
+ "salu\u0308ng": [
+ "THB"
+ ],
+ "drachmon": [
+ "GRD"
+ ],
+ "somalia schilling": [
+ "SOS"
+ ],
+ "dinar iraqui": [
+ "IQD"
+ ],
+ "escudo": [
+ "CVE"
+ ],
+ "hrywni": [
+ "UAH"
+ ],
+ "libra de santa elena": [
+ "SHP"
+ ],
+ "couronnes tche\u0300ques": [
+ "CZK"
+ ],
+ "dolar fiyiano": [
+ "FJD"
+ ],
+ "\u20a9": [
+ "KRW"
+ ],
+ "rial iraniano": [
+ "IRR"
+ ],
+ "bbd": [
+ "BBD"
+ ],
+ "e\u0301szak i\u0301r font": [
+ "GBP"
+ ],
+ "$ ca": [
+ "CAD"
+ ],
+ "quid": [
+ "GBP"
+ ],
+ "ta\u0301dzsik szomoni": [
+ "TJS"
+ ],
+ "dram armenio": [
+ "AMD"
+ ],
+ "rupia singalese": [
+ "LKR"
+ ],
+ "botswaanse pula": [
+ "BWP"
+ ],
+ "co\u0301rdoba nicaraguayen": [
+ "NIO"
+ ],
+ "c$": [
+ "NIO",
+ "CAD"
+ ],
+ "oost caraibische dollar": [
+ "XCD"
+ ],
+ "guyanese dollar": [
+ "GYD"
+ ],
+ "indonesische roepia": [
+ "IDR"
+ ],
+ "corone ceche": [
+ "CZK"
+ ],
+ "franco cfa de a\u0301frica occidental": [
+ "XOF"
+ ],
+ "currency of mexico": [
+ "MXN"
+ ],
+ "kwanza reajustado": [
+ "AOA"
+ ],
+ "botswanai pula": [
+ "BWP"
+ ],
+ "reais": [
+ "BRL"
+ ],
+ "cve": [
+ "CVE"
+ ],
+ "flori\u0301n suriname\u0301s": [
+ "SRG"
+ ],
+ "franc djibouti": [
+ "DJF"
+ ],
+ "do\u0301lar beliceno": [
+ "BZD"
+ ],
+ "forint hu\u0301ngaro": [
+ "HUF"
+ ],
+ "iranischer rial": [
+ "IRR"
+ ],
+ "tenge": [
+ "KZT"
+ ],
+ "czechoslovak koruna": [
+ "CSK"
+ ],
+ "grivna ucraniana": [
+ "UAH"
+ ],
+ "dinar alge\u0301rien": [
+ "DZD"
+ ],
+ "rupia esrilanquesa": [
+ "LKR"
+ ],
+ "kyrgyz som": [
+ "KGS"
+ ],
+ "turkmeense manat": [
+ "TMT"
+ ],
+ "hryvnia ukrainienne": [
+ "UAH"
+ ],
+ "dollaro di singapore": [
+ "SGD"
+ ],
+ "dolar de belize": [
+ "BZD"
+ ],
+ "boli\u0301vares": [
+ "VEF"
+ ],
+ "sterlina di gibilterra": [
+ "GIP"
+ ],
+ "shilling ougandais": [
+ "UGX"
+ ],
+ "rupia pakistani\u0301": [
+ "PKR"
+ ],
+ "united arab emirates dirham": [
+ "AED"
+ ],
+ "php": [
+ "PHP"
+ ],
+ "\u03b4\u03c1": [
+ "GRD"
+ ],
+ "lari georgiano": [
+ "GEL"
+ ],
+ "kip laotien": [
+ "LAK"
+ ],
+ "uquiya": [
+ "MRO"
+ ],
+ "francs or": [
+ "XFO"
+ ],
+ "dinar": [
+ "TND",
+ "DZD"
+ ],
+ "shilling tanzanien": [
+ "TZS"
+ ],
+ "kongo\u0301i frank": [
+ "CDF"
+ ],
+ "franco de yibuti": [
+ "DJF"
+ ],
+ "dirham marroqui\u0301": [
+ "MAD"
+ ],
+ "florin hungaro": [
+ "HUF"
+ ],
+ "tyjyn": [
+ "KGS"
+ ],
+ "di\u0301rham de los emiratos a\u0301rabes unidos": [
+ "AED"
+ ],
+ "florin arubais": [
+ "AWG"
+ ],
+ "la couronne danoise": [
+ "DKK"
+ ],
+ "gambian dalasi": [
+ "GMD"
+ ],
+ "szuda\u0301ni font": [
+ "SDG"
+ ],
+ "corona svedese": [
+ "SEK"
+ ],
+ "colombiaanse peso": [
+ "COP"
+ ],
+ "dirham marocchino": [
+ "MAD"
+ ],
+ "won sud core\u0301en": [
+ "KRW"
+ ],
+ "seychellois rupee": [
+ "SCR"
+ ],
+ "gibraltarees pond": [
+ "GIP"
+ ],
+ "franc fort": [
+ "FRF"
+ ],
+ "schweizerfranken": [
+ "CHF"
+ ],
+ "livre soudanaise": [
+ "SDG"
+ ],
+ "manat aze\u0301ri": [
+ "AZN"
+ ],
+ "nuevo she\u0301kel": [
+ "ILS"
+ ],
+ "paraguayaanse guarani": [
+ "PYG"
+ ],
+ "trinidad and tobago dollar": [
+ "TTD"
+ ],
+ "tiyin": [
+ "UZS"
+ ],
+ "dollar de belize": [
+ "BZD"
+ ],
+ "nuovo siclo": [
+ "ILS"
+ ],
+ "pyas": [
+ "MMK"
+ ],
+ "liberiaanse dollar": [
+ "LRD"
+ ],
+ "quetzal guatemalteco": [
+ "GTQ"
+ ],
+ "naira nige\u0301rian": [
+ "NGN"
+ ],
+ "balboa panameno": [
+ "PAB"
+ ],
+ "indian rupee": [
+ "INR"
+ ],
+ "bahreini dina\u0301r": [
+ "BHD"
+ ],
+ "zuid afrikaanse rand": [
+ "ZAR"
+ ],
+ "roepia": [
+ "IDR"
+ ],
+ "dollar bermudien": [
+ "BMD"
+ ],
+ "loti del lesotho": [
+ "LSL"
+ ],
+ "hondurese lempira": [
+ "HNL"
+ ],
+ "tsjecho slowaakse kroon": [
+ "CSK"
+ ],
+ "do\u0301lar de barbados": [
+ "BBD"
+ ],
+ "su\u0308dafrikanischer rand": [
+ "ZAR"
+ ],
+ "szau\u0301di ria\u0301l": [
+ "SAR"
+ ],
+ "szerb dina\u0301r": [
+ "RSD"
+ ],
+ "roupie du ne\u0301pal": [
+ "NPR"
+ ],
+ "dinaro": [
+ "BHD"
+ ],
+ "balboa": [
+ "PAB"
+ ],
+ "rublo sovie\u0301tico": [
+ "SUR"
+ ],
+ "dollar de la caraibe orientale": [
+ "XCD"
+ ],
+ "dolar bruneano": [
+ "BND"
+ ],
+ "dollaro canadese": [
+ "CAD"
+ ],
+ "arubai florin": [
+ "AWG"
+ ],
+ "somali shilling": [
+ "SOS"
+ ],
+ "peso oro dominicano": [
+ "DOP"
+ ],
+ "bangladesi taka": [
+ "BDT"
+ ],
+ "lettischer lats": [
+ "LVL"
+ ],
+ "marco della repubblica democratica tedesca": [
+ "DDM"
+ ],
+ "ijslandse kroon": [
+ "ISK"
+ ],
+ "burundi franc": [
+ "BIF"
+ ],
+ "rand sud africain": [
+ "ZAR"
+ ],
+ "corone norvegesi": [
+ "NOK"
+ ],
+ "do\u0301lar caimano": [
+ "KYD"
+ ],
+ "burundi frank": [
+ "BIF"
+ ],
+ "new israeli sheqel": [
+ "ILS"
+ ],
+ "metical mozambicain": [
+ "MZN"
+ ],
+ "dolar de surinam": [
+ "SRD"
+ ],
+ "falkland islands pound": [
+ "FKP"
+ ],
+ "maurita\u0301niai ouguiya": [
+ "MRO"
+ ],
+ "u\u0301j ze\u0301landi dolla\u0301r": [
+ "NZD"
+ ],
+ "lats leton": [
+ "LVL"
+ ],
+ "somoni tagico": [
+ "TJS"
+ ],
+ "mozambikaanse metical": [
+ "MZN"
+ ],
+ "dollaro delle bermuda": [
+ "BMD"
+ ],
+ "dolar de hong kong": [
+ "HKD"
+ ],
+ "dollaro delle bermude": [
+ "BMD"
+ ],
+ "balboa panamense": [
+ "PAB"
+ ],
+ "roupie srilankaise": [
+ "LKR"
+ ],
+ "fya\u0308n": [
+ "THB"
+ ],
+ "dolar guyanes": [
+ "GYD"
+ ],
+ "franc suisse": [
+ "CHF"
+ ],
+ "rial irani\u0301": [
+ "IRR"
+ ],
+ "myanmarese kyat": [
+ "MMK"
+ ],
+ "costa ricaanse colo\u0301n": [
+ "CRC"
+ ],
+ "corona checa": [
+ "CZK"
+ ],
+ "thai baht": [
+ "THB"
+ ],
+ "djiboutiaanse frank": [
+ "DJF"
+ ],
+ "schkalim": [
+ "ILS"
+ ],
+ "\u17db": [
+ "KHR"
+ ],
+ "franc djiboutien": [
+ "DJF"
+ ],
+ "dinar koweitien": [
+ "KWD"
+ ],
+ "loonie": [
+ "CAD"
+ ],
+ "denari": [
+ "MKD"
+ ],
+ "lvl": [
+ "LVL"
+ ],
+ "hryvnja": [
+ "UAH"
+ ],
+ "lempira hondurien": [
+ "HNL"
+ ],
+ "franc guineen": [
+ "GNF"
+ ],
+ "seychelles rupee": [
+ "SCR"
+ ],
+ "leu rumeno": [
+ "RON"
+ ],
+ "dirham emirati": [
+ "AED"
+ ],
+ "co\u0301rdoba nicarague\u0301en": [
+ "NIO"
+ ],
+ "neuseeland dollar": [
+ "NZD"
+ ],
+ "\u20aa": [
+ "ILS"
+ ],
+ "bahamai dolla\u0301r": [
+ "BSD"
+ ],
+ "szi\u0301r font": [
+ "SYP"
+ ],
+ "nieuwe israelische sjekel": [
+ "ILS"
+ ],
+ "franc francais": [
+ "FRF"
+ ],
+ "jamaicaanse dollar": [
+ "JMD"
+ ],
+ "burmese kyat": [
+ "MMK"
+ ],
+ "do\u0301lar de belize": [
+ "BZD"
+ ],
+ "tunis dinar": [
+ "TND"
+ ],
+ "hrywnja": [
+ "UAH"
+ ],
+ "do\u0301lar de belice": [
+ "BZD"
+ ],
+ "koeweitse dinar": [
+ "KWD"
+ ],
+ "rial yemeni": [
+ "YER"
+ ],
+ "quetzal": [
+ "GTQ"
+ ],
+ "livres sterlings": [
+ "GBP"
+ ],
+ "hnl": [
+ "HNL"
+ ],
+ "franco cfa de a\u0301frica central": [
+ "XAF"
+ ],
+ "scellino keniota": [
+ "KES"
+ ]
+ },
+ "iso4217": {
+ "DZD": {
+ "fr": "Dinar alg\u00e9rien",
+ "en": "Algerian dinar",
+ "nl": "Algerijnse dinar",
+ "de": "Algerischer Dinar",
+ "it": "Dinaro algerino",
+ "hu": "alg\u00e9riai din\u00e1r",
+ "es": "Dinar argelino"
+ },
+ "NAD": {
+ "fr": "Dollar namibien",
+ "en": "Namibian dollar",
+ "nl": "Namibische dollar",
+ "de": "Namibia-Dollar",
+ "it": "Dollaro namibiano",
+ "hu": "Nam\u00edbiai doll\u00e1r",
+ "es": "D\u00f3lar namibio"
+ },
+ "GHS": {
+ "fr": "Cedi",
+ "en": "Ghana cedi",
+ "nl": "Ghanese cedi",
+ "de": "Cedi",
+ "it": "Cedi ghanese",
+ "hu": "Gh\u00e1nai cedi",
+ "es": "Cedi"
+ },
+ "BZD": {
+ "fr": "Dollar b\u00e9lizien",
+ "en": "Belize dollar",
+ "nl": "Belizaanse dollar",
+ "de": "Belize-Dollar",
+ "it": "Dollaro del Belize",
+ "hu": "Belize-i doll\u00e1r",
+ "es": "D\u00f3lar belice\u00f1o"
+ },
+ "BGN": {
+ "fr": "Lev bulgare",
+ "en": "Bulgarian lev",
+ "nl": "Bulgaarse lev",
+ "de": "Lew",
+ "it": "Lev bulgaro",
+ "hu": "bolg\u00e1r leva",
+ "es": "Lev"
+ },
+ "PAB": {
+ "fr": "Balboa",
+ "en": "Panamanian balboa",
+ "nl": "Panamese balboa",
+ "de": "Panamaischer Balboa",
+ "it": "Balboa panamense",
+ "hu": "Panamai balboa",
+ "es": "Balboa"
+ },
+ "BOB": {
+ "fr": "boliviano",
+ "en": "boliviano",
+ "nl": "Boliviaanse boliviano",
+ "de": "Boliviano",
+ "it": "boliviano",
+ "hu": "bol\u00edviai boliviano",
+ "es": "boliviano"
+ },
+ "DKK": {
+ "fr": "Couronne danoise",
+ "en": "Danish krone",
+ "nl": "Deense kroon",
+ "de": "D\u00e4nische Krone",
+ "it": "Corona danese",
+ "hu": "d\u00e1n korona",
+ "es": "Corona danesa"
+ },
+ "BWP": {
+ "fr": "Pula",
+ "en": "Botswana pula",
+ "nl": "Botswaanse pula",
+ "de": "Botswanischer Pula",
+ "it": "Pula del Botswana",
+ "hu": "Botswanai pula",
+ "es": "Pula"
+ },
+ "LBP": {
+ "fr": "livre libanaise",
+ "en": "Lebanese pound",
+ "nl": "Libanees pond",
+ "de": "Libanesisches Pfund",
+ "it": "Lira libanese",
+ "hu": "libanoni font",
+ "es": "Libra libanesa"
+ },
+ "TZS": {
+ "fr": "shilling tanzanien",
+ "en": "Tanzanian shilling",
+ "nl": "Tanzaniaanse shilling",
+ "de": "Tansania-Schilling",
+ "it": "Scellino tanzaniano",
+ "hu": "Tanz\u00e1niai shilling",
+ "es": "chel\u00edn"
+ },
+ "VND": {
+ "fr": "Dong",
+ "en": "Vietnamese dong",
+ "nl": "Vietnamese dong",
+ "de": "Vietnamesischer \u0110\u1ed3ng",
+ "it": "\u0110\u1ed3ng vietnamita",
+ "hu": "vietnami \u0111\u1ed3ng",
+ "es": "\u0111\u1ed3ng vietnamita"
+ },
+ "AOA": {
+ "fr": "Kwanza",
+ "en": "Angolan kwanza",
+ "nl": "Angolese kwanza",
+ "de": "Kwanza",
+ "it": "Kwanza angolano",
+ "hu": "angolai kwanza",
+ "es": "Kwanza angole\u00f1o"
+ },
+ "KHR": {
+ "fr": "Riel",
+ "en": "riel",
+ "nl": "Cambodjaanse riel",
+ "de": "Kambodschanischer Riel",
+ "it": "Riel cambogiano",
+ "hu": "kambodzsai riel",
+ "es": "Riel camboyano"
+ },
+ "MYR": {
+ "fr": "Ringgit",
+ "en": "Malaysian ringgit",
+ "nl": "Maleisische ringgit",
+ "de": "Ringgit",
+ "it": "Ringgit malese",
+ "hu": "mal\u00e1j ringgit",
+ "es": "Ringgit"
+ },
+ "KYD": {
+ "fr": "Dollar des \u00eeles Ca\u00efmans",
+ "en": "Cayman Islands dollar",
+ "nl": "Kaaimaneilandse dollar",
+ "de": "Kaiman-Dollar",
+ "it": "Dollaro delle Cayman",
+ "hu": "Kajm\u00e1n-szigeteki doll\u00e1r",
+ "es": "D\u00f3lar de las Islas Caim\u00e1n"
+ },
+ "LYD": {
+ "fr": "Dinar libyen",
+ "en": "Libyan dinar",
+ "nl": "Libische dinar",
+ "de": "Libyscher Dinar",
+ "it": "Dinaro libico",
+ "hu": "L\u00edbiai din\u00e1r",
+ "es": "Dinar libio"
+ },
+ "UAH": {
+ "fr": "Hryvnia",
+ "en": "hryvnia",
+ "nl": "Oekra\u00efense hryvnja",
+ "de": "Hrywnja",
+ "it": "Grivnia ucraina",
+ "hu": "ukr\u00e1n hrivnya",
+ "es": "Grivna"
+ },
+ "JOD": {
+ "fr": "Dinar jordanien",
+ "en": "Jordanian dinar",
+ "nl": "Jordaanse dinar",
+ "de": "Jordanischer Dinar",
+ "it": "Dinaro giordano",
+ "hu": "jord\u00e1n din\u00e1r",
+ "es": "Dinar jordano"
+ },
+ "SUR": {
+ "fr": "Rouble sovi\u00e9tique",
+ "en": "Soviet ruble",
+ "de": "Sowjetischer Rubel",
+ "it": "Rublo sovietico",
+ "hu": "Szovjet rubel",
+ "es": "Rublo sovi\u00e9tico"
+ },
+ "AWG": {
+ "fr": "Florin arubais",
+ "en": "Aruban florin",
+ "nl": "Arubaanse florin",
+ "de": "Aruba-Florin",
+ "it": "Fiorino arubano",
+ "hu": "Arubai florin",
+ "es": "Flor\u00edn arube\u00f1o"
+ },
+ "SAR": {
+ "fr": "Riyal saoudien",
+ "en": "Saudi riyal",
+ "nl": "Saoedi-Arabische riyal",
+ "de": "Saudi-Rial",
+ "it": "Riyal saudita",
+ "hu": "sza\u00fadi ri\u00e1l",
+ "es": "Riyal saud\u00ed"
+ },
+ "EUR": {
+ "fr": "euro",
+ "en": "euro",
+ "nl": "euro",
+ "de": "Euro",
+ "it": "euro",
+ "hu": "eur\u00f3",
+ "es": "euro"
+ },
+ "HKD": {
+ "fr": "Dollar de Hong Kong",
+ "en": "Hong Kong dollar",
+ "nl": "Hongkongse dollar",
+ "de": "Hongkong-Dollar",
+ "it": "dollaro hongkonghese",
+ "hu": "hongkongi doll\u00e1r",
+ "es": "D\u00f3lar de Hong Kong"
+ },
+ "SRG": {
+ "en": "Surinamese guilder",
+ "nl": "Surinaamse gulden",
+ "it": "Fiorino surinamese",
+ "es": "Flor\u00edn surinam\u00e9s"
+ },
+ "CHF": {
+ "fr": "Franc suisse",
+ "en": "Swiss franc",
+ "nl": "Zwitserse frank",
+ "de": "Schweizer Franken",
+ "it": "franco svizzero",
+ "hu": "sv\u00e1jci frank",
+ "es": "franco suizo"
+ },
+ "GIP": {
+ "fr": "Livre de Gibraltar",
+ "en": "Gibraltar pound",
+ "nl": "Gibraltarees pond",
+ "de": "Gibraltar-Pfund",
+ "it": "Sterlina di Gibilterra",
+ "hu": "Gibralt\u00e1ri font",
+ "es": "Libra gibraltare\u00f1a"
+ },
+ "ALL": {
+ "fr": "Lek",
+ "en": "lek",
+ "nl": "Albanese lek",
+ "de": "Albanischer Lek",
+ "it": "Lek albanese",
+ "hu": "alb\u00e1n lek",
+ "es": "Lek alban\u00e9s"
+ },
+ "MRO": {
+ "fr": "Ouguiya",
+ "en": "Mauritanian ouguiya",
+ "nl": "Mauritaanse ouguiya",
+ "de": "Ouguiya",
+ "it": "Ouguiya mauritana",
+ "hu": "Maurit\u00e1niai ouguiya",
+ "es": "Uquiya"
+ },
+ "HRK": {
+ "fr": "Kuna croate",
+ "en": "Croatian kuna",
+ "nl": "Kroatische kuna",
+ "de": "Kroatische Kuna",
+ "it": "Kuna croata",
+ "hu": "horv\u00e1t kuna",
+ "es": "Kuna croata"
+ },
+ "DJF": {
+ "fr": "franc Djibouti",
+ "en": "Djiboutian franc",
+ "nl": "Djiboutiaanse frank",
+ "de": "Dschibuti-Franc",
+ "it": "Franco gibutiano",
+ "hu": "Dzsibuti frank",
+ "es": "franco"
+ },
+ "THB": {
+ "fr": "Baht",
+ "en": "Thai baht",
+ "nl": "Thaise baht",
+ "de": "Baht",
+ "it": "Baht thailandese",
+ "hu": "thai b\u00e1t",
+ "es": "Baht tailand\u00e9s"
+ },
+ "XAF": {
+ "fr": "Franc CFA",
+ "en": "Central African CFA franc",
+ "de": "CFA-Franc BEAC",
+ "es": "Franco CFA de \u00c1frica Central"
+ },
+ "BND": {
+ "fr": "Dollar de Brunei",
+ "en": "Brunei dollar",
+ "nl": "Bruneise dollar",
+ "de": "Brunei-Dollar",
+ "it": "Dollaro del Brunei",
+ "hu": "brunei doll\u00e1r",
+ "es": "D\u00f3lar de Brun\u00e9i"
+ },
+ "VUV": {
+ "fr": "Vatu",
+ "en": "Vanuatu vatu",
+ "nl": "Vanuatuaanse vatu",
+ "de": "Vatu",
+ "it": "Vatu di Vanuatu",
+ "hu": "Vanuatui vatu",
+ "es": "Vatu"
+ },
+ "UYU": {
+ "fr": "Peso uruguayen",
+ "en": "Uruguayan peso",
+ "nl": "Uruguayaanse peso",
+ "de": "Uruguayischer Peso",
+ "it": "Peso uruguaiano",
+ "hu": "Uruguayi peso",
+ "es": "peso"
+ },
+ "NIO": {
+ "fr": "C\u00f3rdoba",
+ "en": "Nicaraguan c\u00f3rdoba",
+ "nl": "Nicaraguaanse c\u00f3rdoba",
+ "de": "C\u00f3rdoba Oro",
+ "it": "C\u00f3rdoba nicaraguense",
+ "hu": "Nicaraguai c\u00f3rdoba",
+ "es": "C\u00f3rdoba"
+ },
+ "LAK": {
+ "fr": "Kip laotien",
+ "en": "Lao kip",
+ "nl": "Laotiaanse kip",
+ "de": "Kip",
+ "it": "Kip laotiano",
+ "hu": "laoszi kip",
+ "es": "Kip laosiano"
+ },
+ "MZE": {
+ "de": "Mosambikanischer Escudo",
+ "en": "Mozambican escudo",
+ "es": "Escudo mozambique\u00f1o"
+ },
+ "SYP": {
+ "fr": "Livre syrienne",
+ "en": "Syrian pound",
+ "nl": "Syrisch pond",
+ "de": "Syrische Lira",
+ "it": "Lira siriana",
+ "hu": "Sz\u00edr font",
+ "es": "Libra siria"
+ },
+ "MAD": {
+ "fr": "Dirham marocain",
+ "en": "Moroccan dirham",
+ "nl": "Marokkaanse dirham",
+ "de": "Marokkanischer Dirham",
+ "it": "Dirham marocchino",
+ "hu": "Marokk\u00f3i dirham",
+ "es": "D\u00edrham marroqu\u00ed"
+ },
+ "MZN": {
+ "fr": "Metical",
+ "en": "Mozambican metical",
+ "nl": "Mozambikaanse metical",
+ "de": "Metical",
+ "it": "Metical mozambicano",
+ "hu": "Mozambiki metical",
+ "es": "Metical mozambique\u00f1o"
+ },
+ "SCR": {
+ "fr": "roupie seychelloise",
+ "en": "Seychellois rupee",
+ "nl": "Seychelse roepie",
+ "de": "Seychellen-Rupie",
+ "it": "Rupia delle Seychelles",
+ "hu": "Seychelle-i r\u00fapia",
+ "es": "rupia"
+ },
+ "ZAR": {
+ "fr": "rand",
+ "en": "South African rand",
+ "nl": "Zuid-Afrikaanse rand",
+ "de": "S\u00fcdafrikanischer Rand",
+ "it": "Rand sudafricano",
+ "hu": "D\u00e9l-afrikai rand",
+ "es": "Rand sudafricano"
+ },
+ "NPR": {
+ "fr": "Roupie n\u00e9palaise",
+ "en": "Nepalese rupee",
+ "nl": "Nepalese roepie",
+ "de": "Nepalesische Rupie",
+ "it": "Rupia nepalese",
+ "hu": "nep\u00e1li r\u00fapia",
+ "es": "Rupia nepal\u00ed"
+ },
+ "XSU": {
+ "fr": "Sucre",
+ "en": "SUCRE",
+ "nl": "SUCRE",
+ "es": "SUCRE",
+ "de": "SUCRE"
+ },
+ "NGN": {
+ "fr": "Naira",
+ "en": "Nigerian naira",
+ "nl": "Nigeriaanse naira",
+ "de": "Naira",
+ "it": "Naira nigeriana",
+ "hu": "Nig\u00e9riai naira",
+ "es": "Naira"
+ },
+ "CRC": {
+ "fr": "col\u00f3n",
+ "en": "Costa Rican col\u00f3n",
+ "nl": "Costa Ricaanse colon",
+ "de": "Costa-Rica-Col\u00f3n",
+ "it": "Col\u00f3n costaricano",
+ "hu": "Costa Rica-i col\u00f3n",
+ "es": "Col\u00f3n"
+ },
+ "AED": {
+ "fr": "Dirham des \u00c9mirats arabes unis",
+ "en": "United Arab Emirates dirham",
+ "nl": "VAE-Dirham",
+ "de": "VAE-Dirham",
+ "it": "Dirham degli Emirati Arabi Uniti",
+ "hu": "emir\u00e1tusi dirham",
+ "es": "D\u00edrham de los Emiratos \u00c1rabes Unidos"
+ },
+ "GBP": {
+ "fr": "livre sterling",
+ "en": "pound sterling",
+ "nl": "pond sterling",
+ "de": "Pfund Sterling",
+ "it": "sterlina britannica",
+ "hu": "font sterling",
+ "es": "libra esterlina"
+ },
+ "LKR": {
+ "fr": "roupie srilankaise",
+ "en": "Sri Lankan rupee",
+ "nl": "Sri Lankaanse roepie",
+ "de": "Sri-Lanka-Rupie",
+ "it": "Rupia singalese",
+ "hu": "Sr\u00ed Lanka-i r\u00fapia",
+ "es": "rupia"
+ },
+ "PKR": {
+ "fr": "Roupie pakistanaise",
+ "en": "Pakistani rupee",
+ "nl": "Pakistaanse roepie",
+ "de": "Pakistanische Rupie",
+ "it": "Rupia pakistana",
+ "hu": "pakiszt\u00e1ni r\u00fapia",
+ "es": "Rupia pakistan\u00ed"
+ },
+ "HUF": {
+ "fr": "Forint",
+ "en": "Hungarian forint",
+ "nl": "Hongaarse forint",
+ "de": "Forint",
+ "it": "Fiorino ungherese",
+ "hu": "magyar forint",
+ "es": "Forinto h\u00fangaro"
+ },
+ "SZL": {
+ "fr": "Lilangeni",
+ "en": "Swazi lilangeni",
+ "nl": "Swazische lilangeni",
+ "de": "Lilangeni",
+ "it": "Lilangeni dello Swaziland",
+ "hu": "Szv\u00e1zif\u00f6ldi lilangeni",
+ "es": "lilangeni"
+ },
+ "LSL": {
+ "fr": "Loti",
+ "en": "Lesotho loti",
+ "nl": "Lesothaanse loti",
+ "de": "Lesothischer Loti",
+ "it": "Loti lesothiano",
+ "hu": "Lesoth\u00f3i loti",
+ "es": "Loti"
+ },
+ "MNT": {
+ "fr": "Tugrik",
+ "en": "Mongolian t\u00f6gr\u00f6g",
+ "nl": "Mongoolse tugrik",
+ "de": "T\u00f6gr\u00f6g",
+ "it": "Tugrik mongolo",
+ "hu": "mongol tugrik",
+ "es": "Tugrik mongol"
+ },
+ "AMD": {
+ "fr": "Dram",
+ "en": "Armenian dram",
+ "nl": "Armeense dram",
+ "de": "Armenischer Dram",
+ "it": "Dram armeno",
+ "hu": "\u00f6rm\u00e9ny dram",
+ "es": "Dram armenio"
+ },
+ "UGX": {
+ "fr": "shilling ougandais",
+ "en": "Ugandan shilling",
+ "nl": "Oegandese shilling",
+ "de": "Uganda-Schilling",
+ "it": "Scellino ugandese",
+ "hu": "Ugandai shilling",
+ "es": "chel\u00edn"
+ },
+ "QAR": {
+ "fr": "Riyal qatarien",
+ "en": "Qatari riyal",
+ "nl": "Qatarese rial",
+ "de": "Katar-Riyal",
+ "it": "Riyal del Qatar",
+ "hu": "katari ri\u00e1l",
+ "es": "Riyal catar\u00ed"
+ },
+ "XDR": {
+ "fr": "Droits de tirage sp\u00e9ciaux",
+ "en": "Special drawing rights",
+ "nl": "Speciale trekkingsrechten",
+ "de": "Sonderziehungsrecht",
+ "it": "Diritti speciali di prelievo",
+ "hu": "SDR",
+ "es": "Derechos Especiales de Giro"
+ },
+ "ITL": {
+ "fr": "Lire italienne",
+ "en": "Italian lira",
+ "nl": "Italiaanse lire",
+ "de": "Italienische Lira",
+ "it": "lira italiana",
+ "hu": "Olasz l\u00edra",
+ "es": "Lira italiana"
+ },
+ "JMD": {
+ "fr": "Dollar jama\u00efcain",
+ "en": "Jamaican dollar",
+ "nl": "Jamaicaanse dollar",
+ "de": "Jamaika-Dollar",
+ "it": "Dollaro giamaicano",
+ "hu": "Jamaicai doll\u00e1r",
+ "es": "D\u00f3lar jamaiquino"
+ },
+ "GEL": {
+ "fr": "lari",
+ "en": "Georgian lari",
+ "nl": "Georgische lari",
+ "de": "Georgischer Lari",
+ "it": "Lari georgiano",
+ "hu": "gr\u00faz lari",
+ "es": "lari"
+ },
+ "SHP": {
+ "fr": "Livre de Sainte-H\u00e9l\u00e8ne",
+ "en": "Saint Helena pound",
+ "nl": "Sint-Heleens pond",
+ "de": "St.-Helena-Pfund",
+ "it": "Sterlina di Sant'Elena",
+ "hu": "Szent Ilona-i font",
+ "es": "Libra de Santa Elena"
+ },
+ "AFN": {
+ "fr": "Afghani",
+ "en": "Afghan afghani",
+ "nl": "Afghaanse afghani",
+ "de": "Afghani",
+ "it": "Afghani afgano",
+ "hu": "afg\u00e1n afg\u00e1ni",
+ "es": "Afgani afgano"
+ },
+ "MMK": {
+ "fr": "Kyat",
+ "en": "kyat",
+ "nl": "Myanmarese kyat",
+ "de": "Kyat",
+ "it": "Kyat birmano",
+ "hu": "mianmari kjap",
+ "es": "Kyat birmano"
+ },
+ "CSK": {
+ "fr": "couronne tch\u00e9coslovaque",
+ "en": "Czechoslovak koruna",
+ "nl": "Tsjecho-Slowaakse kroon",
+ "de": "Tschechoslowakische Krone",
+ "it": "Corona cecoslovacca",
+ "hu": "csehszlov\u00e1k korona",
+ "es": "Corona checoslovaca"
+ },
+ "KPW": {
+ "fr": "Won nord-cor\u00e9en",
+ "en": "North Korean won",
+ "nl": "Noord-Koreaanse won",
+ "de": "Nordkoreanischer Won",
+ "it": "Won nordcoreano",
+ "hu": "\u00e9szak-koreai von",
+ "es": "W\u014fn norcoreano"
+ },
+ "TRY": {
+ "fr": "Livre turque",
+ "en": "Turkish lira",
+ "nl": "Nieuwe Turkse lira",
+ "de": "T\u00fcrkische Lira",
+ "it": "Nuova lira turca",
+ "hu": "t\u00f6r\u00f6k \u00faj l\u00edra",
+ "es": "Lira turca"
+ },
+ "BDT": {
+ "fr": "Taka",
+ "en": "taka",
+ "nl": "Bengalese taka",
+ "de": "Taka",
+ "it": "Taka bengalese",
+ "hu": "bangladesi taka",
+ "es": "Taka banglades\u00ed"
+ },
+ "GRD": {
+ "fr": "Drachme moderne grecque",
+ "en": "Greek drachma",
+ "nl": "Drachme",
+ "de": "Griechische Drachme",
+ "it": "Dracma greca",
+ "es": "Dracma griega moderna"
+ },
+ "YER": {
+ "fr": "rial y\u00e9m\u00e9nite",
+ "en": "Yemeni rial",
+ "nl": "Jemenitische rial",
+ "de": "Jemen-Rial",
+ "it": "riyal yemenita",
+ "hu": "Jemeni ri\u00e1l",
+ "es": "rial yemen\u00ed"
+ },
+ "DDM": {
+ "fr": "Mark est-allemand",
+ "en": "East German mark",
+ "nl": "Oost-Duitse mark",
+ "de": "Mark",
+ "it": "Marco della Repubblica Democratica Tedesca",
+ "es": "Marco de la Rep\u00fablica Democr\u00e1tica Alemana"
+ },
+ "HTG": {
+ "fr": "Gourde",
+ "en": "Haitian gourde",
+ "nl": "Ha\u00eftiaanse gourde",
+ "de": "Gourde",
+ "it": "Gourde haitiano",
+ "hu": "haiti gourde",
+ "es": "Gourde"
+ },
+ "XOF": {
+ "fr": "Franc CFA",
+ "en": "West African CFA franc",
+ "de": "CFA-Franc BCEAO",
+ "es": "Franco CFA de \u00c1frica Occidental"
+ },
+ "MGA": {
+ "fr": "ariary",
+ "en": "Malagasy ariary",
+ "nl": "Malagassische ariary",
+ "de": "Ariary",
+ "it": "Ariary malgascio",
+ "hu": "Madagaszk\u00e1ri ariary",
+ "es": "ariary"
+ },
+ "PHP": {
+ "fr": "peso philippin",
+ "en": "Philippine peso",
+ "nl": "Filipijnse peso",
+ "de": "Philippinischer Peso",
+ "it": "peso filippino",
+ "hu": "F\u00fcl\u00f6p-szigeteki peso",
+ "es": "peso"
+ },
+ "LRD": {
+ "fr": "Dollar lib\u00e9rien",
+ "en": "Liberian dollar",
+ "nl": "Liberiaanse dollar",
+ "de": "Liberianischer Dollar",
+ "it": "Dollaro liberiano",
+ "hu": "Lib\u00e9riai doll\u00e1r",
+ "es": "D\u00f3lar liberiano"
+ },
+ "RWF": {
+ "fr": "franc rwandais",
+ "en": "Rwandan franc",
+ "nl": "Rwandese frank",
+ "de": "Ruanda-Franc",
+ "it": "Franco ruandese",
+ "hu": "Ruandai frank",
+ "es": "franco"
+ },
+ "NOK": {
+ "fr": "Couronne norv\u00e9gienne",
+ "en": "Norwegian krone",
+ "nl": "Noorse kroon",
+ "de": "Norwegische Krone",
+ "it": "Corona norvegese",
+ "hu": "norv\u00e9g korona",
+ "es": "Corona noruega"
+ },
+ "MOP": {
+ "fr": "Pataca",
+ "en": "Macanese pataca",
+ "nl": "Macause pataca",
+ "de": "Macao-Pataca",
+ "it": "Pataca di Macao",
+ "hu": "Maka\u00f3i pataca",
+ "es": "Pataca"
+ },
+ "SSP": {
+ "fr": "Livre sud-soudanaise",
+ "en": "South Sudanese pound",
+ "nl": "Zuid-Soedanees pond",
+ "de": "S\u00fcdsudanesisches Pfund",
+ "it": "Sterlina sudsudanese",
+ "hu": "D\u00e9l-szud\u00e1ni font",
+ "es": "Libra sursudanesa"
+ },
+ "INR": {
+ "fr": "Roupie indienne",
+ "en": "Indian rupee",
+ "nl": "Indiase roepie",
+ "de": "Indische Rupie",
+ "it": "rupia indiana",
+ "hu": "Indiai r\u00fapia",
+ "es": "Rupia india"
+ },
+ "MXN": {
+ "fr": "peso mexicain",
+ "en": "Mexican peso",
+ "nl": "Mexicaanse peso",
+ "de": "Mexikanischer Peso",
+ "it": "Peso messicano",
+ "hu": "mexik\u00f3i peso",
+ "es": "peso"
+ },
+ "CZK": {
+ "fr": "Couronne tch\u00e8que",
+ "en": "Czech koruna",
+ "nl": "Tsjechische kroon",
+ "de": "Tschechische Krone",
+ "it": "Corona ceca",
+ "hu": "cseh korona",
+ "es": "Corona checa"
+ },
+ "TJS": {
+ "fr": "Somoni",
+ "en": "Tajikistani somoni",
+ "nl": "Tadzjiekse somoni",
+ "de": "Somoni",
+ "it": "Somoni tagico",
+ "hu": "t\u00e1dzsik szomoni",
+ "es": "Somoni tayiko"
+ },
+ "TJR": {
+ "en": "Tajikistani ruble",
+ "es": "Rublo tayiko"
+ },
+ "BTN": {
+ "fr": "ngultrum",
+ "en": "Bhutanese ngultrum",
+ "nl": "Bhutaanse ngultrum",
+ "de": "Ngultrum",
+ "it": "Ngultrum del Bhutan",
+ "hu": "bhut\u00e1ni ngultrum",
+ "es": "Ngultrum butan\u00e9s"
+ },
+ "KMF": {
+ "fr": "Franc comorien",
+ "en": "Comorian franc",
+ "nl": "Comorese frank",
+ "de": "Komoren-Franc",
+ "it": "Franco delle Comore",
+ "hu": "Comore-i frank",
+ "es": "Franco comorano"
+ },
+ "TMT": {
+ "fr": "Manat turkm\u00e8ne",
+ "en": "Turkmenistan manat",
+ "nl": "Turkmeense manat",
+ "de": "Turkmenistan-Manat",
+ "it": "Manat turkmeno",
+ "hu": "T\u00fcrkm\u00e9n manat",
+ "es": "Manat turkmeno"
+ },
+ "MUR": {
+ "fr": "Roupie mauricienne",
+ "en": "Mauritian rupee",
+ "nl": "Mauritiaanse roepie",
+ "de": "Mauritius-Rupie",
+ "it": "Rupia mauriziana",
+ "hu": "Mauritiusi r\u00fapia",
+ "es": "Rupia de Mauricio"
+ },
+ "IDR": {
+ "fr": "Roupie indon\u00e9sienne",
+ "en": "Indonesian Rupiah",
+ "nl": "Indonesische roepia",
+ "de": "Indonesische Rupiah",
+ "it": "Rupia indonesiana",
+ "hu": "indon\u00e9z r\u00fapia",
+ "es": "Rupia indonesia"
+ },
+ "HNL": {
+ "fr": "Lempira",
+ "en": "Honduran lempira",
+ "nl": "Hondurese lempira",
+ "de": "Lempira",
+ "it": "Lempira honduregna",
+ "hu": "hondurasi lempira",
+ "es": "lempira"
+ },
+ "ETB": {
+ "fr": "Birr",
+ "en": "Ethiopian birr",
+ "nl": "Ethiopische birr",
+ "de": "\u00c4thiopischer Birr",
+ "it": "Birr etiope",
+ "hu": "eti\u00f3p birr",
+ "es": "Birr et\u00edope"
+ },
+ "FJD": {
+ "fr": "dollar de Fidji",
+ "en": "Fijian dollar",
+ "nl": "Fiji-dollar",
+ "de": "Fidschi-Dollar",
+ "it": "Dollaro delle Figi",
+ "hu": "Fidzsi doll\u00e1r",
+ "es": "d\u00f3lar"
+ },
+ "ISK": {
+ "fr": "Couronne islandaise",
+ "en": "Icelandic kr\u00f3na",
+ "nl": "IJslandse kroon",
+ "de": "Isl\u00e4ndische Krone",
+ "it": "Corona islandese",
+ "hu": "izlandi korona",
+ "es": "corona islandesa"
+ },
+ "PEN": {
+ "fr": "nouveau sol",
+ "en": "Peruvian nuevo sol",
+ "nl": "Peruviaanse sol",
+ "de": "Nuevo Sol",
+ "it": "nuevo sol peruviano",
+ "hu": "perui \u00faj sol",
+ "es": "nuevo sol"
+ },
+ "MKD": {
+ "fr": "Dinar mac\u00e9donien",
+ "en": "Macedonian denar",
+ "nl": "Macedonische denar",
+ "de": "Mazedonischer Denar",
+ "it": "Denaro macedone",
+ "hu": "maced\u00f3n d\u00e9n\u00e1r",
+ "es": "Denar macedonio"
+ },
+ "ILS": {
+ "fr": "Shekel",
+ "en": "Israeli new shekel",
+ "nl": "Isra\u00eblische sjekel",
+ "de": "Schekel",
+ "it": "nuovo siclo israeliano",
+ "hu": "izraeli \u00faj s\u00e9kel",
+ "es": "Nuevo sh\u00e9quel"
+ },
+ "DOP": {
+ "fr": "Peso dominicain",
+ "en": "Dominican peso",
+ "nl": "Dominicaanse peso",
+ "de": "Dominikanischer Peso",
+ "it": "Peso dominicano",
+ "hu": "Dominikai peso",
+ "es": "peso"
+ },
+ "AZN": {
+ "fr": "Manat azerba\u00efdjanais",
+ "en": "Azerbaijani manat",
+ "nl": "Azerbeidzjaanse manat",
+ "de": "Aserbaidschan-Manat",
+ "it": "Manat azero",
+ "hu": "Azeri manat",
+ "es": "Manat azerbaiyano"
+ },
+ "MDL": {
+ "fr": "Leu moldave",
+ "en": "Moldovan leu",
+ "nl": "Moldavische leu",
+ "de": "Moldauischer Leu",
+ "it": "Leu moldavo",
+ "hu": "moldov\u00e1n lej",
+ "es": "Leu moldavo"
+ },
+ "BSD": {
+ "fr": "Dollar baham\u00e9en",
+ "en": "Bahamian dollar",
+ "nl": "Bahamaanse dollar",
+ "de": "Bahama-Dollar",
+ "it": "Dollaro delle Bahamas",
+ "hu": "bahamai doll\u00e1r",
+ "es": "D\u00f3lar bahame\u00f1o"
+ },
+ "SEK": {
+ "fr": "Couronne su\u00e9doise",
+ "en": "Swedish krona",
+ "nl": "Zweedse kroon",
+ "de": "Schwedische Krone",
+ "it": "Corona svedese",
+ "hu": "Sv\u00e9d korona",
+ "es": "Corona sueca"
+ },
+ "MVR": {
+ "fr": "Rufiyaa",
+ "en": "Maldivian rufiyaa",
+ "nl": "Maldivische rufiyaa",
+ "de": "Rufiyaa",
+ "it": "Rufiyaa delle Maldive",
+ "hu": "Mald\u00edv-szigeteki r\u00fafia",
+ "es": "Rupia de Maldivas"
+ },
+ "FRF": {
+ "fr": "Franc fran\u00e7ais",
+ "en": "French franc",
+ "nl": "Franse frank",
+ "de": "Franz\u00f6sischer Franc",
+ "it": "Franco francese",
+ "hu": "Francia frank",
+ "es": "Franco franc\u00e9s"
+ },
+ "SRD": {
+ "fr": "Dollar de Surinam",
+ "en": "Surinamese dollar",
+ "nl": "Surinaamse dollar",
+ "de": "Suriname-Dollar",
+ "it": "Dollaro surinamese",
+ "hu": "suriname-i doll\u00e1r",
+ "es": "D\u00f3lar surinam\u00e9s"
+ },
+ "CUP": {
+ "fr": "peso cubain",
+ "en": "Cuban peso",
+ "nl": "Cubaanse peso",
+ "de": "Kubanischer Peso",
+ "it": "peso cubano",
+ "hu": "kubai peso",
+ "es": "peso"
+ },
+ "BBD": {
+ "fr": "dollar barbadien",
+ "en": "Barbadian dollar",
+ "nl": "Barbadiaanse dollar",
+ "de": "Barbados-Dollar",
+ "it": "Dollaro di Barbados",
+ "hu": "barbadosi doll\u00e1r",
+ "es": "D\u00f3lar de Barbados"
+ },
+ "KRW": {
+ "fr": "Won sud-cor\u00e9en",
+ "en": "South Korean won",
+ "nl": "Zuid-Koreaanse won",
+ "de": "S\u00fcdkoreanischer Won",
+ "it": "Won sudcoreano",
+ "hu": "D\u00e9l-koreai von",
+ "es": "W\u014fn surcoreano"
+ },
+ "GMD": {
+ "fr": "Dalasi",
+ "en": "Gambian dalasi",
+ "nl": "Gambiaanse dalasi",
+ "de": "Dalasi",
+ "it": "Dalasi gambese",
+ "hu": "Gambiai dalasi",
+ "es": "Dalasi"
+ },
+ "VEF": {
+ "fr": "Bol\u00edvar v\u00e9n\u00e9zu\u00e9lien",
+ "en": "Venezuelan bol\u00edvar",
+ "nl": "Venezolaanse bol\u00edvar",
+ "de": "Venezolanischer Bol\u00edvar",
+ "it": "Bol\u00edvar venezuelano",
+ "hu": "venezuelai bol\u00edvar",
+ "es": "Bol\u00edvar"
+ },
+ "GTQ": {
+ "fr": "Quetzal",
+ "en": "Guatemalan quetzal",
+ "nl": "Guatemalteekse quetzal",
+ "de": "Guatemaltekischer Quetzal",
+ "it": "Quetzal guatemalteco",
+ "hu": "Guatemalai quetzal",
+ "es": "Quetzal"
+ },
+ "ANG": {
+ "fr": "Florin des Antilles n\u00e9erlandaises",
+ "en": "Netherlands Antillean guilder",
+ "nl": "Antilliaanse gulden",
+ "de": "Antillen-Gulden",
+ "it": "Fiorino delle Antille Olandesi",
+ "hu": "Holland antill\u00e1kbeli forint",
+ "es": "Flor\u00edn antillano neerland\u00e9s"
+ },
+ "CUC": {
+ "fr": "Peso cubain convertible",
+ "en": "Cuban convertible peso",
+ "nl": "Convertibele peso",
+ "de": "Peso convertible",
+ "it": "Peso cubano convertibile",
+ "hu": "Kubai konvertibilis peso",
+ "es": "peso convertible"
+ },
+ "CLP": {
+ "fr": "Peso chilien",
+ "en": "Chilean peso",
+ "nl": "Chileense peso",
+ "de": "Chilenischer Peso",
+ "it": "Peso cileno",
+ "hu": "chilei peso",
+ "es": "peso"
+ },
+ "ZMW": {
+ "fr": "Kwacha zambien",
+ "en": "Zambian kwacha",
+ "nl": "Zambiaanse kwacha",
+ "de": "Sambischer Kwacha",
+ "it": "Kwacha zambiano",
+ "hu": "Zambiai kwacha",
+ "es": "Kwacha zambiano"
+ },
+ "LTL": {
+ "fr": "Litas",
+ "en": "Lithuanian litas",
+ "nl": "Litouwse litas",
+ "de": "Litas",
+ "it": "Litas lituano",
+ "hu": "litv\u00e1n litas",
+ "es": "Litas lituana"
+ },
+ "CDF": {
+ "fr": "Franc congolais",
+ "en": "Congolese franc",
+ "nl": "Congolese frank",
+ "de": "Kongo-Franc",
+ "it": "Franco congolese",
+ "hu": "Kong\u00f3i frank",
+ "es": "franco"
+ },
+ "XCD": {
+ "fr": "dollar des Cara\u00efbes orientales",
+ "en": "East Caribbean dollar",
+ "nl": "Oost-Caribische dollar",
+ "de": "ostkaribischer Dollar",
+ "it": "dollaro dei Caraibi Orientali",
+ "hu": "kelet-karibi doll\u00e1r",
+ "es": "d\u00f3lar del Caribe Oriental"
+ },
+ "KZT": {
+ "fr": "Tenge kazakh",
+ "en": "Kazakhstani tenge",
+ "nl": "Kazachse tenge",
+ "de": "Tenge",
+ "it": "Tenge kazako",
+ "hu": "kazah tenge",
+ "es": "Tenge kazajo"
+ },
+ "XPF": {
+ "fr": "Franc Pacifique",
+ "en": "CFP Franc",
+ "nl": "CFP-frank",
+ "de": "CFP-Franc",
+ "it": "Franco CFP",
+ "hu": "Csendes-\u00f3ce\u00e1ni valutak\u00f6z\u00f6ss\u00e9gi frank",
+ "es": "Franco CFP"
+ },
+ "RUB": {
+ "fr": "Rouble russe",
+ "en": "Russian ruble",
+ "nl": "Russische roebel",
+ "de": "Russischer Rubel",
+ "it": "Rublo russo",
+ "hu": "orosz rubel",
+ "es": "Rublo ruso"
+ },
+ "XFU": {
+ "fr": "Franc UIC",
+ "en": "UIC franc"
+ },
+ "TTD": {
+ "fr": "Dollar de Trinit\u00e9-et-Tobago",
+ "en": "Trinidad and Tobago dollar",
+ "nl": "Trinidad en Tobagodollar",
+ "de": "Trinidad-und-Tobago-Dollar",
+ "it": "Dollaro di Trinidad e Tobago",
+ "hu": "Trinidad \u00e9s Tobag\u00f3-i doll\u00e1r",
+ "es": "D\u00f3lar trinitense"
+ },
+ "RON": {
+ "fr": "Leu roumain",
+ "en": "Romanian leu",
+ "nl": "Roemeense leu",
+ "de": "Rum\u00e4nischer Leu",
+ "it": "Leu romeno",
+ "hu": "rom\u00e1n lej",
+ "es": "Leu rumano"
+ },
+ "OMR": {
+ "fr": "Rial omanais",
+ "en": "Omani rial",
+ "nl": "Omaanse rial",
+ "de": "Omanischer Rial",
+ "it": "Riyal dell'Oman",
+ "hu": "Om\u00e1ni ri\u00e1l",
+ "es": "Rial oman\u00ed"
+ },
+ "BRL": {
+ "fr": "r\u00e9al br\u00e9silien",
+ "en": "Brazilian real",
+ "nl": "Braziliaanse real",
+ "de": "Brasilianischer Real",
+ "it": "Real brasiliano",
+ "hu": "brazil real",
+ "es": "Real brasile\u00f1o"
+ },
+ "SBD": {
+ "fr": "dollar des \u00eeles Salomon",
+ "en": "Solomon Islands dollar",
+ "nl": "Salomon-dollar",
+ "de": "Salomonen-Dollar",
+ "it": "Dollaro delle Salomone",
+ "hu": "Salamon-szigeteki doll\u00e1r",
+ "es": "d\u00f3lar de las Islas Salom\u00f3n"
+ },
+ "PYG": {
+ "fr": "Guaran\u00ed",
+ "en": "Paraguayan guaran\u00ed",
+ "nl": "Paraguayaanse guaran\u00ed",
+ "de": "Paraguayischer Guaran\u00ed",
+ "it": "Guaran\u00ed paraguaiano",
+ "hu": "Paraguayi guaran\u00ed",
+ "es": "Guaran\u00ed"
+ },
+ "KES": {
+ "fr": "Shilling k\u00e9nyan",
+ "en": "Kenyan shilling",
+ "nl": "Keniaanse shilling",
+ "de": "Kenia-Schilling",
+ "it": "Scellino keniota",
+ "hu": "Kenyai shilling",
+ "es": "Chel\u00edn keniano"
+ },
+ "USD": {
+ "fr": "dollar am\u00e9ricain",
+ "en": "United States dollar",
+ "nl": "Amerikaanse dollar",
+ "de": "US-Dollar",
+ "it": "dollaro statunitense",
+ "hu": "amerikai doll\u00e1r",
+ "es": "d\u00f3lar"
+ },
+ "TWD": {
+ "fr": "Nouveau dollar de Ta\u00efwan",
+ "en": "New Taiwan dollar",
+ "nl": "Taiwanese dollar",
+ "de": "Neuer Taiwan-Dollar",
+ "it": "Dollaro taiwanese",
+ "hu": "Tajvani \u00faj doll\u00e1r",
+ "es": "Nuevo d\u00f3lar taiwan\u00e9s"
+ },
+ "TOP": {
+ "fr": "pa\u2019anga",
+ "en": "Tongan pa\u02bbanga",
+ "nl": "Tongaanse pa'anga",
+ "de": "Pa\u02bbanga",
+ "it": "Pa'anga tongano",
+ "hu": "Tongai pa\u02bbanga",
+ "es": "pa\u02bbanga"
+ },
+ "COP": {
+ "fr": "Peso colombien",
+ "en": "peso",
+ "nl": "Colombiaanse peso",
+ "de": "Kolumbianischer Peso",
+ "it": "Peso colombiano",
+ "hu": "Kolumbiai peso",
+ "es": "peso"
+ },
+ "GNF": {
+ "fr": "Franc guin\u00e9en",
+ "en": "Guinean franc",
+ "nl": "Guineese frank",
+ "de": "Franc Guin\u00e9en",
+ "it": "Franco guineano",
+ "hu": "Guineai frank",
+ "es": "Franco guineano"
+ },
+ "WST": {
+ "fr": "Tala",
+ "en": "Samoan t\u0101l\u0101",
+ "nl": "Samoaanse tala",
+ "de": "Samoanischer Tala",
+ "it": "Tala samoano",
+ "hu": "Szamoai tala",
+ "es": "tala"
+ },
+ "IQD": {
+ "fr": "Dinar irakien",
+ "en": "Iraqi dinar",
+ "nl": "Iraakse dinar",
+ "de": "Irakischer Dinar",
+ "it": "Dinaro iracheno",
+ "hu": "iraki din\u00e1r",
+ "es": "Dinar iraqu\u00ed"
+ },
+ "ERN": {
+ "fr": "Nakfa \u00e9rythr\u00e9en",
+ "en": "Eritrean nakfa",
+ "nl": "Eritrese nakfa",
+ "de": "Eritreischer Nakfa",
+ "it": "Nacfa eritreo",
+ "hu": "Eritreai nakfa",
+ "es": "Nakfa"
+ },
+ "CVE": {
+ "fr": "Escudo cap-verdien",
+ "en": "Cape Verdean escudo",
+ "nl": "Kaapverdische escudo",
+ "de": "Kap-Verde-Escudo",
+ "it": "Escudo capoverdiano",
+ "hu": "Z\u00f6ld-foki k\u00f6zt\u00e1rsas\u00e1gi escudo",
+ "es": "escudo"
+ },
+ "AUD": {
+ "fr": "dollar australien",
+ "en": "Australian dollar",
+ "nl": "Australische dollar",
+ "de": "Australischer Dollar",
+ "it": "Dollaro australiano",
+ "hu": "Ausztr\u00e1l doll\u00e1r",
+ "es": "D\u00f3lar australiano"
+ },
+ "BAM": {
+ "fr": "Mark convertible de Bosnie-Herz\u00e9govine",
+ "en": "Bosnia and Herzegovina convertible mark",
+ "nl": "Bosnische inwisselbare mark",
+ "de": "Konvertible Mark",
+ "it": "Marco bosniaco",
+ "hu": "bosny\u00e1k konvertibilis m\u00e1rka",
+ "es": "Marco bosnioherzegovino"
+ },
+ "KWD": {
+ "fr": "Dinar kowe\u00eftien",
+ "en": "Kuwaiti dinar",
+ "nl": "Koeweitse dinar",
+ "de": "Kuwait-Dinar",
+ "it": "Dinaro kuwaitiano",
+ "hu": "kuvaiti din\u00e1r",
+ "es": "Dinar kuwait\u00ed"
+ },
+ "BIF": {
+ "fr": "Franc burundais",
+ "en": "Burundian franc",
+ "nl": "Burundese frank",
+ "de": "Burundi-Franc",
+ "it": "Franco del Burundi",
+ "hu": "Burundi frank",
+ "es": "Franco de Burundi"
+ },
+ "PGK": {
+ "fr": "Kina",
+ "en": "Papua New Guinean kina",
+ "nl": "Papoea-Nieuw-Guinese kina",
+ "de": "Kina",
+ "it": "Kina papuana",
+ "hu": "P\u00e1pua \u00faj-guineai kina",
+ "es": "Kina"
+ },
+ "SOS": {
+ "fr": "shilling somalien",
+ "en": "Somali shilling",
+ "nl": "Somalische shilling",
+ "de": "Somalia-Schilling",
+ "it": "Scellino somalo",
+ "hu": "Szom\u00e1liai shilling",
+ "es": "chel\u00edn"
+ },
+ "CAD": {
+ "fr": "Dollar canadien",
+ "en": "Canadian dollar",
+ "nl": "Canadese dollar",
+ "de": "Kanadischer Dollar",
+ "it": "Dollaro canadese",
+ "hu": "kanadai doll\u00e1r",
+ "es": "D\u00f3lar canadiense"
+ },
+ "SGD": {
+ "fr": "Dollar de Singapour",
+ "en": "Singapore dollar",
+ "nl": "Singaporese dollar",
+ "de": "Singapur-Dollar",
+ "it": "Dollaro di Singapore",
+ "hu": "szingap\u00fari doll\u00e1r",
+ "es": "D\u00f3lar de Singapur"
+ },
+ "UZS": {
+ "fr": "Sum",
+ "en": "Uzbekistani som",
+ "nl": "Oezbeekse sum",
+ "de": "So\u02bbm",
+ "it": "Som uzbeco",
+ "hu": "\u00dczb\u00e9g szom",
+ "es": "som"
+ },
+ "STD": {
+ "fr": "Dobra",
+ "en": "S\u00e3o Tom\u00e9 and Pr\u00edncipe dobra",
+ "nl": "Santomese dobra",
+ "de": "S\u00e3o-tom\u00e9ischer Dobra",
+ "it": "Dobra di S\u00e3o Tom\u00e9 e Pr\u00edncipe",
+ "hu": "S\u00e3o Tom\u00e9 \u00e9s Pr\u00edncipe-i dobra",
+ "es": "Dobra santotomense"
+ },
+ "XFO": {
+ "fr": "Franc-or",
+ "en": "Gold franc",
+ "de": "Goldfranken"
+ },
+ "IRR": {
+ "fr": "Rial iranien",
+ "en": "Iranian rial",
+ "nl": "Iraanse rial",
+ "de": "Iranischer Rial",
+ "it": "Riyal iraniano",
+ "hu": "ir\u00e1ni ri\u00e1l",
+ "es": "Rial iran\u00ed"
+ },
+ "CNY": {
+ "fr": "Yuan",
+ "en": "renminbi",
+ "nl": "Chinese renminbi",
+ "de": "Renminbi",
+ "it": "Renminbi cinese",
+ "hu": "Renminbi",
+ "es": "Yuan chino"
+ },
+ "SLL": {
+ "fr": "leone",
+ "en": "Sierra Leonean leone",
+ "nl": "Sierra Leoonse leone",
+ "de": "Sierra-leonischer Leone",
+ "it": "Leone sierraleonese",
+ "hu": "Sierra Leone-i leone",
+ "es": "leone"
+ },
+ "TND": {
+ "fr": "Dinar tunisien",
+ "en": "Tunisian dinar",
+ "nl": "Tunesische dinar",
+ "de": "Tunesischer Dinar",
+ "it": "Dinaro tunisino",
+ "hu": "Tun\u00e9ziai din\u00e1r",
+ "es": "dinar"
+ },
+ "GYD": {
+ "fr": "Dollar guyanien",
+ "en": "Guyanese dollar",
+ "nl": "Guyaanse dollar",
+ "de": "Guyana-Dollar",
+ "it": "Dollaro della Guyana",
+ "hu": "Guyanai doll\u00e1r",
+ "es": "D\u00f3lar guyan\u00e9s"
+ },
+ "MTL": {
+ "fr": "Lire maltaise",
+ "en": "Maltese lira",
+ "nl": "Maltese lire",
+ "de": "Maltesische Lira",
+ "it": "Lira maltese",
+ "hu": "M\u00e1ltai l\u00edra",
+ "es": "Lira maltesa"
+ },
+ "NZD": {
+ "fr": "dollar n\u00e9o-z\u00e9landais",
+ "en": "New Zealand dollar",
+ "nl": "Nieuw-Zeelandse dollar",
+ "de": "Neuseeland-Dollar",
+ "it": "Dollaro neozelandese",
+ "hu": "\u00faj-z\u00e9landi doll\u00e1r",
+ "es": "D\u00f3lar neozeland\u00e9s"
+ },
+ "FKP": {
+ "fr": "Livre des \u00celes Malouines",
+ "en": "Falkland Islands pound",
+ "nl": "Falklandeilands pond",
+ "de": "Falkland-Pfund",
+ "it": "Sterlina delle Falkland",
+ "hu": "Falkland-szigeteki font",
+ "es": "Libra malvinense"
+ },
+ "LVL": {
+ "fr": "Lats",
+ "en": "lats",
+ "nl": "Letse lats",
+ "de": "Lats",
+ "it": "Lats lettone",
+ "hu": "lett lat",
+ "es": "Lats let\u00f3n"
+ },
+ "ARP": {
+ "fr": "peso argentino",
+ "en": "peso argentino",
+ "nl": "peso argentino",
+ "it": "peso argentino",
+ "es": "peso argentino"
+ },
+ "KGS": {
+ "fr": "Som",
+ "en": "Kyrgyzstani som",
+ "nl": "Kirgizische som",
+ "de": "Som",
+ "it": "som kirghizo",
+ "hu": "kirgiz szom",
+ "es": "Som kirgu\u00eds"
+ },
+ "ARS": {
+ "fr": "peso argentin",
+ "en": "peso",
+ "nl": "Argentijnse peso",
+ "de": "Argentinischer Peso",
+ "it": "peso argentino",
+ "hu": "argentin peso",
+ "es": "peso"
+ },
+ "BMD": {
+ "fr": "Dollar bermudien",
+ "en": "Bermudian dollar",
+ "nl": "Bermuda-dollar",
+ "de": "Bermuda-Dollar",
+ "it": "Dollaro della Bermuda",
+ "hu": "bermudai doll\u00e1r",
+ "es": "D\u00f3lar bermude\u00f1o"
+ },
+ "RSD": {
+ "fr": "Dinar serbe",
+ "en": "Serbian dinar",
+ "nl": "Servische dinar",
+ "de": "Serbischer Dinar",
+ "it": "Dinaro serbo",
+ "hu": "szerb din\u00e1r",
+ "es": "Dinar serbio"
+ },
+ "BHD": {
+ "fr": "Dinar bahre\u00efnien",
+ "en": "Bahraini dinar",
+ "nl": "Bahreinse dinar",
+ "de": "Bahrain-Dinar",
+ "it": "Dinaro del Bahrain",
+ "hu": "bahreini din\u00e1r",
+ "es": "Dinar barein\u00ed"
+ },
+ "JPY": {
+ "fr": "Yen",
+ "en": "Japanese yen",
+ "nl": "Japanse yen",
+ "de": "Yen",
+ "it": "Yen giapponese",
+ "hu": "Jap\u00e1n jen",
+ "es": "yen"
+ },
+ "ARA": {
+ "fr": "Austral (monnaie)",
+ "en": "Argentine austral",
+ "de": "Austral (W\u00e4hrung)",
+ "it": "Austral argentino",
+ "es": "Austral"
+ },
+ "SDG": {
+ "fr": "Livre soudanaise",
+ "en": "Sudanese pound",
+ "nl": "Soedanees pond",
+ "de": "Sudanesisches Pfund",
+ "it": "Sterlina sudanese",
+ "hu": "Szud\u00e1ni font",
+ "es": "Libra sudanesa"
+ }
+ }
+} \ No newline at end of file
diff --git a/searx/data/engines_languages.json b/searx/data/engines_languages.json
new file mode 100644
index 0000000..2d97166
--- /dev/null
+++ b/searx/data/engines_languages.json
@@ -0,0 +1 @@
+{"google news": {"el": {"name": "Ελληνικά"}, "eo": {"name": "Esperanto"}, "en": {"name": "English"}, "af": {"name": "Afrikaans"}, "vi": {"name": "Tiếng Việt"}, "ca": {"name": "Català"}, "it": {"name": "Italiano"}, "iw": {"name": "עברית"}, "hy": {"name": "Հայերեն"}, "cs": {"name": "Čeština"}, "et": {"name": "Eesti"}, "id": {"name": "Indonesia"}, "es": {"name": "Español"}, "ru": {"name": "Русский"}, "nl": {"name": "Nederlands"}, "pt": {"name": "Português"}, "no": {"name": "Norsk"}, "tr": {"name": "Türkçe"}, "lt": {"name": "Lietuvių"}, "lv": {"name": "Latviešu"}, "tl": {"name": "Filipino"}, "zh-TW": {"name": "中文 (繁體)"}, "th": {"name": "ไทย"}, "ro": {"name": "Română"}, "is": {"name": "Íslenska"}, "pl": {"name": "Polski"}, "be": {"name": "Беларуская"}, "fr": {"name": "Français"}, "bg": {"name": "Български"}, "hr": {"name": "Hrvatski"}, "de": {"name": "Deutsch"}, "ko": {"name": "한국어"}, "da": {"name": "Dansk"}, "fa": {"name": "فارسی"}, "hi": {"name": "हिन्दी"}, "fi": {"name": "Suomi"}, "hu": {"name": "Magyar"}, "ja": {"name": "日本語"}, "sr": {"name": "Српски"}, "sw": {"name": "Kiswahili"}, "sv": {"name": "Svenska"}, "sk": {"name": "Slovenčina"}, "zh-CN": {"name": "中文 (简体)"}, "ar": {"name": "العربية"}, "uk": {"name": "Українська"}, "sl": {"name": "Slovenščina"}}, "bing news": ["sq", "de", "ar", "bg", "ca", "cs", "zh-CHS", "zh-CHT", "ko", "hr", "da", "sk", "sl", "es", "et", "fi", "fr", "el", "he", "nl", "hu", "id", "en", "is", "it", "ja", "lv", "lt", "ms", "no", "fa", "pl", "pt-BR", "pt-PT", "ro", "ru", "sr", "sv", "th", "tr", "uk", "vi"], "google": {"el": {"name": "Ελληνικά"}, "eo": {"name": "Esperanto"}, "en": {"name": "English"}, "af": {"name": "Afrikaans"}, "vi": {"name": "Tiếng Việt"}, "ca": {"name": "Català"}, "it": {"name": "Italiano"}, "iw": {"name": "עברית"}, "hy": {"name": "Հայերեն"}, "cs": {"name": "Čeština"}, "et": {"name": "Eesti"}, "id": {"name": "Indonesia"}, "es": {"name": "Español"}, "ru": {"name": "Русский"}, "nl": {"name": "Nederlands"}, "pt": {"name": "Português"}, "no": {"name": "Norsk"}, "tr": {"name": "Türkçe"}, "lt": {"name": "Lietuvių"}, "lv": {"name": "Latviešu"}, "tl": {"name": "Filipino"}, "zh-TW": {"name": "中文 (繁體)"}, "th": {"name": "ไทย"}, "ro": {"name": "Română"}, "is": {"name": "Íslenska"}, "pl": {"name": "Polski"}, "be": {"name": "Беларуская"}, "fr": {"name": "Français"}, "bg": {"name": "Български"}, "hr": {"name": "Hrvatski"}, "de": {"name": "Deutsch"}, "ko": {"name": "한국어"}, "da": {"name": "Dansk"}, "fa": {"name": "فارسی"}, "hi": {"name": "हिन्दी"}, "fi": {"name": "Suomi"}, "hu": {"name": "Magyar"}, "ja": {"name": "日本語"}, "sr": {"name": "Српски"}, "sw": {"name": "Kiswahili"}, "sv": {"name": "Svenska"}, "sk": {"name": "Slovenčina"}, "zh-CN": {"name": "中文 (简体)"}, "ar": {"name": "العربية"}, "uk": {"name": "Українська"}, "sl": {"name": "Slovenščina"}}, "duckduckgo": ["da-DK", "vi-VN", "en-SG", "sl-SL", "en-XA", "tzh-HK", "en-UK", "ro-RO", "en-MY", "el-GR", "it-CH", "hu-HU", "fr-FR", "en-PH", "tl-PH", "fr-CA", "fi-FI", "et-EE", "sv-SE", "es-XL", "th-TH", "sk-SK", "es-ES", "en-IE", "es-US", "es-PE", "nl-NL", "en-US", "de-DE", "de-AT", "wt-WT", "no-NO", "tr-TR", "ca-ES", "it-IT", "es-CO", "ru-RU", "ca-CT", "en-ZA", "en-CA", "jp-JP", "es-MX", "id-ID", "es-AR", "he-IL", "kr-KR", "en-AU", "ms-MY", "pl-PL", "lv-LV", "bg-BG", "zh-CN", "en-NZ", "lt-LT", "tzh-TW", "hr-HR", "pt-PT", "fr-BE", "de-CH", "cs-CZ", "en-IN", "nl-BE", "fr-CH", "en-ID", "ar-XA", "pt-BR", "uk-UA", "es-CL"], "bing": ["sq", "de", "ar", "bg", "ca", "cs", "zh-CHS", "zh-CHT", "ko", "hr", "da", "sk", "sl", "es", "et", "fi", "fr", "el", "he", "nl", "hu", "id", "en", "is", "it", "ja", "lv", "lt", "ms", "no", "fa", "pl", "pt-BR", "pt-PT", "ro", "ru", "sr", "sv", "th", "tr", "uk", "vi"], "wikipedia": {"gv": {"articles": 4955, "name": "Gaelg", "english_name": "Manx"}, "sco": {"articles": 42405, "name": "Scots", "english_name": "Scots"}, "scn": {"articles": 25379, "name": "Sicilianu", "english_name": "Sicilian"}, "wuu": {"articles": 5688, "name": "吴语", "english_name": "Wu"}, "tcy": {"articles": 750, "name": "ತುಳು", "english_name": "Tulu"}, "cdo": {"articles": 4239, "name": "Mìng-dĕ̤ng-ngṳ̄", "english_name": "Min Dong"}, "gu": {"articles": 26842, "name": "ગુજરાતી", "english_name": "Gujarati"}, "kbd": {"articles": 1569, "name": "Адыгэбзэ (Adighabze)", "english_name": "Kabardian Circassian"}, "gd": {"articles": 14368, "name": "Gàidhlig", "english_name": "Scottish Gaelic"}, "jbo": {"articles": 1197, "name": "Lojban", "english_name": "Lojban"}, "ga": {"articles": 39393, "name": "Gaeilge", "english_name": "Irish"}, "gn": {"articles": 3133, "name": "Avañe'ẽ", "english_name": "Guarani"}, "gl": {"articles": 136535, "name": "Galego", "english_name": "Galician"}, "als": {"articles": 22592, "name": "Alemannisch", "english_name": "Alemannic"}, "lg": {"articles": 1135, "name": "Luganda", "english_name": "Luganda"}, "hak": {"articles": 7386, "name": "Hak-kâ-fa / 客家話", "english_name": "Hakka"}, "lb": {"articles": 48141, "name": "Lëtzebuergesch", "english_name": "Luxembourgish"}, "szl": {"articles": 5491, "name": "Ślůnski", "english_name": "Silesian"}, "vep": {"articles": 5339, "name": "Vepsän", "english_name": "Vepsian"}, "la": {"articles": 126249, "name": "Latina", "english_name": "Latin"}, "ln": {"articles": 2786, "name": "Lingala", "english_name": "Lingala"}, "frp": {"articles": 2608, "name": "Arpitan", "english_name": "Franco-Provençal"}, "tt": {"articles": 70258, "name": "Tatarça / Татарча", "english_name": "Tatar"}, "tr": {"articles": 292026, "name": "Türkçe", "english_name": "Turkish"}, "cbk-zam": {"articles": 2967, "name": "Chavacano de Zamboanga", "english_name": "Zamboanga Chavacano"}, "li": {"articles": 11620, "name": "Limburgs", "english_name": "Limburgish"}, "lv": {"articles": 75872, "name": "Latviešu", "english_name": "Latvian"}, "to": {"articles": 1688, "name": "faka Tonga", "english_name": "Tongan"}, "tl": {"articles": 66203, "name": "Tagalog", "english_name": "Tagalog"}, "jam": {"articles": 1597, "name": "Jumiekan Kryuol", "english_name": "Jamaican Patois"}, "vec": {"articles": 10877, "name": "Vèneto", "english_name": "Venetian"}, "th": {"articles": 114824, "name": "ไทย", "english_name": "Thai"}, "ti": {"articles": 175, "name": "ትግርኛ", "english_name": "Tigrinya"}, "tg": {"articles": 67713, "name": "Тоҷикӣ", "english_name": "Tajik"}, "te": {"articles": 66514, "name": "తెలుగు", "english_name": "Telugu"}, "ksh": {"articles": 2825, "name": "Ripoarisch", "english_name": "Ripuarian"}, "pcd": {"articles": 3402, "name": "Picard", "english_name": "Picard"}, "ta": {"articles": 91441, "name": "தமிழ்", "english_name": "Tamil"}, "yi": {"articles": 13744, "name": "ייִדיש", "english_name": "Yiddish"}, "lrc": {"articles": 5280, "name": "لۊری شومالی", "english_name": "Northern Luri"}, "xmf": {"articles": 9318, "name": "მარგალური (Margaluri)", "english_name": "Mingrelian"}, "ceb": {"articles": 4049908, "name": "Sinugboanong Binisaya", "english_name": "Cebuano"}, "yo": {"articles": 31528, "name": "Yorùbá", "english_name": "Yoruba"}, "de": {"articles": 2035837, "name": "Deutsch", "english_name": "German"}, "da": {"articles": 223944, "name": "Dansk", "english_name": "Danish"}, "za": {"articles": 1161, "name": "Cuengh", "english_name": "Zhuang"}, "pdc": {"articles": 1790, "name": "Deitsch", "english_name": "Pennsylvania German"}, "bxr": {"articles": 1873, "name": "Буряад", "english_name": "Buryat"}, "dz": {"articles": 217, "name": "ཇོང་ཁ", "english_name": "Dzongkha"}, "hif": {"articles": 9646, "name": "Fiji Hindi", "english_name": "Fiji Hindi"}, "rm": {"articles": 3417, "name": "Rumantsch", "english_name": "Romansh"}, "dv": {"articles": 2970, "name": "ދިވެހިބަސް", "english_name": "Divehi"}, "qu": {"articles": 20037, "name": "Runa Simi", "english_name": "Quechua"}, "vls": {"articles": 5988, "name": "West-Vlams", "english_name": "West Flemish"}, "bar": {"articles": 22085, "name": "Boarisch", "english_name": "Bavarian"}, "eml": {"articles": 8670, "name": "Emiliàn e rumagnòl", "english_name": "Emilian-Romagnol"}, "kn": {"articles": 21679, "name": "ಕನ್ನಡ", "english_name": "Kannada"}, "fiu-vro": {"articles": 5449, "name": "Võro", "english_name": "Võro"}, "mo": {"articles": 394, "name": "Молдовеняскэ", "english_name": "Moldovan"}, "bpy": {"articles": 25069, "name": "ইমার ঠার/বিষ্ণুপ্রিয়া মণিপুরী", "english_name": "Bishnupriya Manipuri"}, "crh": {"articles": 5113, "name": "Qırımtatarca", "english_name": "Crimean Tatar"}, "mhr": {"articles": 9413, "name": "Олык Марий (Olyk Marij)", "english_name": "Meadow Mari"}, "diq": {"articles": 7537, "name": "Zazaki", "english_name": "Zazaki"}, "el": {"articles": 127970, "name": "Ελληνικά", "english_name": "Greek"}, "eo": {"articles": 237478, "name": "Esperanto", "english_name": "Esperanto"}, "en": {"articles": 5343454, "name": "English", "english_name": "English"}, "zh": {"articles": 927993, "name": "中文", "english_name": "Chinese"}, "pms": {"articles": 64046, "name": "Piemontèis", "english_name": "Piedmontese"}, "ee": {"articles": 334, "name": "Eʋegbe", "english_name": "Ewe"}, "tpi": {"articles": 1351, "name": "Tok Pisin", "english_name": "Tok Pisin"}, "arz": {"articles": 16354, "name": "مصرى (Maṣri)", "english_name": "Egyptian Arabic"}, "rmy": {"articles": 583, "name": "romani - रोमानी", "english_name": "Romani"}, "mdf": {"articles": 1133, "name": "Мокшень (Mokshanj Kälj)", "english_name": "Moksha"}, "kaa": {"articles": 1955, "name": "Qaraqalpaqsha", "english_name": "Karakalpak"}, "olo": {"articles": 1934, "name": "Karjalan", "english_name": "Livvi-Karelian"}, "arc": {"articles": 1617, "name": "ܐܪܡܝܐ", "english_name": "Aramaic"}, "cr": {"articles": 125, "name": "Nehiyaw", "english_name": "Cree"}, "eu": {"articles": 277712, "name": "Euskara", "english_name": "Basque"}, "et": {"articles": 154506, "name": "Eesti", "english_name": "Estonian"}, "tet": {"articles": 1390, "name": "Tetun", "english_name": "Tetum"}, "es": {"articles": 1318337, "name": "Español", "english_name": "Spanish"}, "ba": {"articles": 37407, "name": "Башҡорт", "english_name": "Bashkir"}, "gom": {"articles": 3119, "name": "गोंयची कोंकणी / Gõychi Konknni", "english_name": "Goan Konkani"}, "ru": {"articles": 1375970, "name": "Русский", "english_name": "Russian"}, "roa-tara": {"articles": 9229, "name": "Tarandíne", "english_name": "Tarantino"}, "ha": {"articles": 1410, "name": "هَوُسَ", "english_name": "Hausa"}, "ak": {"articles": 271, "name": "Akana", "english_name": "Akan"}, "lad": {"articles": 4421, "name": "Dzhudezmo", "english_name": "Ladino"}, "bm": {"articles": 411, "name": "Bamanankan", "english_name": "Bambara"}, "new": {"articles": 72123, "name": "नेपाल भाषा", "english_name": "Newar"}, "rn": {"articles": 495, "name": "Kirundi", "english_name": "Kirundi"}, "ro": {"articles": 374753, "name": "Română", "english_name": "Romanian"}, "dsb": {"articles": 3071, "name": "Dolnoserbski", "english_name": "Lower Sorbian"}, "jv": {"articles": 49700, "name": "Basa Jawa", "english_name": "Javanese"}, "hsb": {"articles": 11071, "name": "Hornjoserbsce", "english_name": "Upper Sorbian"}, "be": {"articles": 126878, "name": "Беларуская", "english_name": "Belarusian"}, "bg": {"articles": 227507, "name": "Български", "english_name": "Bulgarian"}, "myv": {"articles": 3510, "name": "Эрзянь (Erzjanj Kelj)", "english_name": "Erzya"}, "uk": {"articles": 682728, "name": "Українська", "english_name": "Ukrainian"}, "wa": {"articles": 14408, "name": "Walon", "english_name": "Walloon"}, "ast": {"articles": 48457, "name": "Asturianu", "english_name": "Asturian"}, "wo": {"articles": 1132, "name": "Wolof", "english_name": "Wolof"}, "got": {"articles": 493, "name": "𐌲𐌿𐍄𐌹𐍃𐌺", "english_name": "Gothic"}, "bn": {"articles": 48261, "name": "বাংলা", "english_name": "Bengali"}, "bo": {"articles": 5717, "name": "བོད་སྐད", "english_name": "Tibetan"}, "bh": {"articles": 8674, "name": "भोजपुरी", "english_name": "Bihari"}, "bi": {"articles": 817, "name": "Bislama", "english_name": "Bislama"}, "rue": {"articles": 5963, "name": "Русиньскый", "english_name": "Rusyn"}, "map-bms": {"articles": 13284, "name": "Basa Banyumasan", "english_name": "Banyumasan"}, "tum": {"articles": 564, "name": "chiTumbuka", "english_name": "Tumbuka"}, "br": {"articles": 61346, "name": "Brezhoneg", "english_name": "Breton"}, "bs": {"articles": 73437, "name": "Bosanski", "english_name": "Bosnian"}, "lez": {"articles": 3606, "name": "Лезги чІал (Lezgi č’al)", "english_name": "Lezgian"}, "ja": {"articles": 1050743, "name": "日本語", "english_name": "Japanese"}, "om": {"articles": 725, "name": "Oromoo", "english_name": "Oromo"}, "glk": {"articles": 6076, "name": "گیلکی", "english_name": "Gilaki"}, "ace": {"articles": 4077, "name": "Bahsa Acèh", "english_name": "Acehnese"}, "ilo": {"articles": 10461, "name": "Ilokano", "english_name": "Ilokano"}, "roa-rup": {"articles": 1206, "name": "Armãneashce", "english_name": "Aromanian"}, "oc": {"articles": 82700, "name": "Occitan", "english_name": "Occitan"}, "ltg": {"articles": 797, "name": "Latgaļu", "english_name": "Latgalian"}, "be-tarask": {"articles": 60248, "name": "Беларуская (тарашкевіца)", "english_name": "Belarusian (Taraškievica)"}, "st": {"articles": 384, "name": "Sesotho", "english_name": "Sesotho"}, "lo": {"articles": 1623, "name": "ລາວ", "english_name": "Lao"}, "krc": {"articles": 2016, "name": "Къарачай-Малкъар (Qarachay-Malqar)", "english_name": "Karachay-Balkar"}, "nds": {"articles": 25822, "name": "Plattdüütsch", "english_name": "Low Saxon"}, "os": {"articles": 10382, "name": "Иронау", "english_name": "Ossetian"}, "or": {"articles": 12252, "name": "ଓଡ଼ିଆ", "english_name": "Oriya"}, "udm": {"articles": 3875, "name": "Удмурт кыл", "english_name": "Udmurt"}, "xh": {"articles": 604, "name": "isiXhosa", "english_name": "Xhosa"}, "ch": {"articles": 419, "name": "Chamoru", "english_name": "Chamorro"}, "co": {"articles": 5424, "name": "Corsu", "english_name": "Corsican"}, "nso": {"articles": 7642, "name": "Sepedi", "english_name": "Northern Sotho"}, "simple": {"articles": 123305, "name": "Simple English", "english_name": "Simple English"}, "bjn": {"articles": 1710, "name": "Bahasa Banjar", "english_name": "Banjar"}, "ca": {"articles": 535045, "name": "Català", "english_name": "Catalan"}, "lmo": {"articles": 34788, "name": "Lumbaart", "english_name": "Lombard"}, "ce": {"articles": 160122, "name": "Нохчийн", "english_name": "Chechen"}, "ts": {"articles": 392, "name": "Xitsonga", "english_name": "Tsonga"}, "cy": {"articles": 90328, "name": "Cymraeg", "english_name": "Welsh"}, "ang": {"articles": 2857, "name": "Englisc", "english_name": "Anglo-Saxon"}, "cs": {"articles": 374940, "name": "Čeština", "english_name": "Czech"}, "ty": {"articles": 1179, "name": "Reo Mā`ohi", "english_name": "Tahitian"}, "ady": {"articles": 399, "name": "Адыгэбзэ", "english_name": "Adyghe"}, "cv": {"articles": 38132, "name": "Чăваш", "english_name": "Chuvash"}, "cu": {"articles": 582, "name": "Словѣньскъ", "english_name": "Old Church Slavonic"}, "ve": {"articles": 238, "name": "Tshivenda", "english_name": "Venda"}, "koi": {"articles": 3429, "name": "Перем Коми (Perem Komi)", "english_name": "Komi-Permyak"}, "ps": {"articles": 7991, "name": "پښتو", "english_name": "Pashto"}, "fj": {"articles": 340, "name": "Na Vosa Vakaviti", "english_name": "Fijian"}, "srn": {"articles": 1047, "name": "Sranantongo", "english_name": "Sranan"}, "pt": {"articles": 957637, "name": "Português", "english_name": "Portuguese"}, "sm": {"articles": 745, "name": "Gagana Samoa", "english_name": "Samoan"}, "ext": {"articles": 2898, "name": "Estremeñu", "english_name": "Extremaduran"}, "lt": {"articles": 181095, "name": "Lietuvių", "english_name": "Lithuanian"}, "zh-min-nan": {"articles": 203047, "name": "Bân-lâm-gú", "english_name": "Min Nan"}, "frr": {"articles": 4712, "name": "Nordfriisk", "english_name": "North Frisian"}, "chr": {"articles": 785, "name": "ᏣᎳᎩ", "english_name": "Cherokee"}, "pa": {"articles": 24776, "name": "ਪੰਜਾਬੀ", "english_name": "Punjabi"}, "xal": {"articles": 2073, "name": "Хальмг", "english_name": "Kalmyk"}, "chy": {"articles": 607, "name": "Tsetsêhestâhese", "english_name": "Cheyenne"}, "pi": {"articles": 2517, "name": "पाऴि", "english_name": "Pali"}, "war": {"articles": 1262274, "name": "Winaray", "english_name": "Waray-Waray"}, "pl": {"articles": 1209184, "name": "Polski", "english_name": "Polish"}, "tk": {"articles": 5193, "name": "تركمن / Туркмен", "english_name": "Turkmen"}, "hy": {"articles": 216349, "name": "Հայերեն", "english_name": "Armenian"}, "an": {"articles": 31888, "name": "Aragonés", "english_name": "Aragonese"}, "nrm": {"articles": 3621, "name": "Nouormand/Normaund", "english_name": "Norman"}, "hr": {"articles": 172218, "name": "Hrvatski", "english_name": "Croatian"}, "iu": {"articles": 391, "name": "ᐃᓄᒃᑎᑐᑦ", "english_name": "Inuktitut"}, "pfl": {"articles": 2067, "name": "Pälzisch", "english_name": "Palatinate German"}, "ht": {"articles": 51150, "name": "Krèyol ayisyen", "english_name": "Haitian"}, "hu": {"articles": 404333, "name": "Magyar", "english_name": "Hungarian"}, "gan": {"articles": 6367, "name": "贛語", "english_name": "Gan"}, "bat-smg": {"articles": 16015, "name": "Žemaitėška", "english_name": "Samogitian"}, "hi": {"articles": 117254, "name": "हिन्दी", "english_name": "Hindi"}, "tw": {"articles": 584, "name": "Twi", "english_name": "Twi"}, "gag": {"articles": 2744, "name": "Gagauz", "english_name": "Gagauz"}, "kg": {"articles": 1170, "name": "KiKongo", "english_name": "Kongo"}, "pnb": {"articles": 43662, "name": "شاہ مکھی پنجابی (Shāhmukhī Pañjābī)", "english_name": "Western Punjabi"}, "bug": {"articles": 14118, "name": "Basa Ugi", "english_name": "Buginese"}, "he": {"articles": 202507, "name": "עברית", "english_name": "Hebrew"}, "mg": {"articles": 83434, "name": "Malagasy", "english_name": "Malagasy"}, "fur": {"articles": 3173, "name": "Furlan", "english_name": "Friulian"}, "uz": {"articles": 128882, "name": "O‘zbek", "english_name": "Uzbek"}, "ml": {"articles": 47909, "name": "മലയാളം", "english_name": "Malayalam"}, "azb": {"articles": 14683, "name": "تۆرکجه", "english_name": "South Azerbaijani"}, "mn": {"articles": 16812, "name": "Монгол", "english_name": "Mongolian"}, "mi": {"articles": 7112, "name": "Māori", "english_name": "Maori"}, "ik": {"articles": 246, "name": "Iñupiak", "english_name": "Inupiak"}, "mk": {"articles": 88524, "name": "Македонски", "english_name": "Macedonian"}, "ur": {"articles": 115102, "name": "اردو", "english_name": "Urdu"}, "zea": {"articles": 4369, "name": "Zeêuws", "english_name": "Zeelandic"}, "mt": {"articles": 3190, "name": "Malti", "english_name": "Maltese"}, "stq": {"articles": 3754, "name": "Seeltersk", "english_name": "Saterland Frisian"}, "ms": {"articles": 287146, "name": "Bahasa Melayu", "english_name": "Malay"}, "mr": {"articles": 46054, "name": "मराठी", "english_name": "Marathi"}, "ug": {"articles": 3273, "name": "ئۇيغۇر تىلى", "english_name": "Uyghur"}, "mwl": {"articles": 2828, "name": "Mirandés", "english_name": "Mirandese"}, "my": {"articles": 34904, "name": "မြန်မာဘာသာ", "english_name": "Burmese"}, "ki": {"articles": 1339, "name": "Gĩkũyũ", "english_name": "Kikuyu"}, "pih": {"articles": 514, "name": "Norfuk", "english_name": "Norfolk"}, "sah": {"articles": 11079, "name": "Саха тыла (Saxa Tyla)", "english_name": "Sakha"}, "ss": {"articles": 421, "name": "SiSwati", "english_name": "Swati"}, "af": {"articles": 43924, "name": "Afrikaans", "english_name": "Afrikaans"}, "tn": {"articles": 620, "name": "Setswana", "english_name": "Tswana"}, "vi": {"articles": 1153964, "name": "Tiếng Việt", "english_name": "Vietnamese"}, "is": {"articles": 41946, "name": "Íslenska", "english_name": "Icelandic"}, "am": {"articles": 13359, "name": "አማርኛ", "english_name": "Amharic"}, "it": {"articles": 1337509, "name": "Italiano", "english_name": "Italian"}, "vo": {"articles": 120479, "name": "Volapük", "english_name": "Volapük"}, "ay": {"articles": 3900, "name": "Aymar", "english_name": "Aymara"}, "as": {"articles": 4462, "name": "অসমীয়া", "english_name": "Assamese"}, "ar": {"articles": 468031, "name": "العربية", "english_name": "Arabic"}, "lbe": {"articles": 1210, "name": "Лакку", "english_name": "Lak"}, "km": {"articles": 5029, "name": "ភាសាខ្មែរ", "english_name": "Khmer"}, "io": {"articles": 26917, "name": "Ido", "english_name": "Ido"}, "av": {"articles": 2302, "name": "Авар", "english_name": "Avar"}, "ia": {"articles": 19677, "name": "Interlingua", "english_name": "Interlingua"}, "haw": {"articles": 1976, "name": "Hawai`i", "english_name": "Hawaiian"}, "az": {"articles": 114754, "name": "Azərbaycanca", "english_name": "Azerbaijani"}, "ie": {"articles": 3594, "name": "Interlingue", "english_name": "Interlingue"}, "id": {"articles": 394114, "name": "Bahasa Indonesia", "english_name": "Indonesian"}, "nds-nl": {"articles": 6724, "name": "Nedersaksisch", "english_name": "Dutch Low Saxon"}, "pap": {"articles": 1769, "name": "Papiamentu", "english_name": "Papiamentu"}, "ks": {"articles": 268, "name": "कश्मीरी / كشميري", "english_name": "Kashmiri"}, "nl": {"articles": 1894647, "name": "Nederlands", "english_name": "Dutch"}, "nn": {"articles": 132461, "name": "Nynorsk", "english_name": "Norwegian (Nynorsk)"}, "no": {"articles": 462394, "name": "Norsk (Bokmål)", "english_name": "Norwegian (Bokmål)"}, "na": {"articles": 1269, "name": "dorerin Naoero", "english_name": "Nauruan"}, "nah": {"articles": 8763, "name": "Nāhuatl", "english_name": "Nahuatl"}, "ne": {"articles": 29974, "name": "नेपाली", "english_name": "Nepali"}, "lij": {"articles": 3236, "name": "Líguru", "english_name": "Ligurian"}, "csb": {"articles": 5128, "name": "Kaszëbsczi", "english_name": "Kashubian"}, "tyv": {"articles": 1366, "name": "Тыва", "english_name": "Tuvan"}, "ny": {"articles": 364, "name": "Chichewa", "english_name": "Chichewa"}, "nap": {"articles": 14434, "name": "Nnapulitano", "english_name": "Neapolitan"}, "ig": {"articles": 1308, "name": "Igbo", "english_name": "Igbo"}, "pag": {"articles": 2526, "name": "Pangasinan", "english_name": "Pangasinan"}, "zu": {"articles": 918, "name": "isiZulu", "english_name": "Zulu"}, "kw": {"articles": 3755, "name": "Kernewek/Karnuack", "english_name": "Cornish"}, "pam": {"articles": 8525, "name": "Kapampangan", "english_name": "Kapampangan"}, "nv": {"articles": 2638, "name": "Diné bizaad", "english_name": "Navajo"}, "sn": {"articles": 2678, "name": "chiShona", "english_name": "Shona"}, "kab": {"articles": 2842, "name": "Taqbaylit", "english_name": "Kabyle"}, "fr": {"articles": 1846517, "name": "Français", "english_name": "French"}, "mrj": {"articles": 10165, "name": "Кырык Мары (Kyryk Mary)", "english_name": "Hill Mari"}, "zh-yue": {"articles": 51227, "name": "粵語", "english_name": "Cantonese"}, "fy": {"articles": 37193, "name": "Frysk", "english_name": "West Frisian"}, "pnt": {"articles": 448, "name": "Ποντιακά", "english_name": "Pontic"}, "fa": {"articles": 524259, "name": "فارسی", "english_name": "Persian"}, "rw": {"articles": 1804, "name": "Ikinyarwanda", "english_name": "Kinyarwanda"}, "ff": {"articles": 213, "name": "Fulfulde", "english_name": "Fula"}, "mai": {"articles": 10392, "name": "मैथिली", "english_name": "Maithili"}, "fi": {"articles": 409282, "name": "Suomi", "english_name": "Finnish"}, "mzn": {"articles": 12372, "name": "مَزِروني", "english_name": "Mazandarani"}, "ab": {"articles": 1217, "name": "Аҧсуа", "english_name": "Abkhazian"}, "sa": {"articles": 10745, "name": "संस्कृतम्", "english_name": "Sanskrit"}, "zh-classical": {"articles": 5142, "name": "古文 / 文言文", "english_name": "Classical Chinese"}, "fo": {"articles": 12449, "name": "Føroyskt", "english_name": "Faroese"}, "bcl": {"articles": 7020, "name": "Bikol", "english_name": "Central Bicolano"}, "ka": {"articles": 113079, "name": "ქართული", "english_name": "Georgian"}, "nov": {"articles": 1645, "name": "Novial", "english_name": "Novial"}, "ckb": {"articles": 18597, "name": "Soranî / کوردی", "english_name": "Sorani"}, "kk": {"articles": 218109, "name": "Қазақша", "english_name": "Kazakh"}, "sr": {"articles": 345474, "name": "Српски / Srpski", "english_name": "Serbian"}, "sq": {"articles": 64573, "name": "Shqip", "english_name": "Albanian"}, "min": {"articles": 221972, "name": "Minangkabau", "english_name": "Minangkabau"}, "ko": {"articles": 373631, "name": "한국어", "english_name": "Korean"}, "sv": {"articles": 3783165, "name": "Svenska", "english_name": "Swedish"}, "su": {"articles": 19256, "name": "Basa Sunda", "english_name": "Sundanese"}, "kl": {"articles": 1643, "name": "Kalaallisut", "english_name": "Greenlandic"}, "sk": {"articles": 216444, "name": "Slovenčina", "english_name": "Slovak"}, "si": {"articles": 13236, "name": "සිංහල", "english_name": "Sinhalese"}, "sh": {"articles": 437610, "name": "Srpskohrvatski / Српскохрватски", "english_name": "Serbo-Croatian"}, "so": {"articles": 4363, "name": "Soomaali", "english_name": "Somali"}, "kv": {"articles": 4925, "name": "Коми", "english_name": "Komi"}, "ku": {"articles": 22705, "name": "Kurdî / كوردی", "english_name": "Kurdish"}, "sl": {"articles": 154822, "name": "Slovenščina", "english_name": "Slovenian"}, "sc": {"articles": 5447, "name": "Sardu", "english_name": "Sardinian"}, "ky": {"articles": 62712, "name": "Кыргызча", "english_name": "Kirghiz"}, "sg": {"articles": 247, "name": "Sängö", "english_name": "Sango"}, "sw": {"articles": 35324, "name": "Kiswahili", "english_name": "Swahili"}, "se": {"articles": 7278, "name": "Sámegiella", "english_name": "Northern Sami"}, "sd": {"articles": 7341, "name": "سنڌي، سندھی ، सिन्ध", "english_name": "Sindhi"}}, "dailymotion": {"gv": {"english_name": "Manx"}, "gu": {"name": "ગુજરાતી", "english_name": "Gujarati"}, "gd": {"english_name": "Gaelic, Scottish"}, "ga": {"name": "Gaeilge", "english_name": "Irish"}, "gn": {"english_name": "Guarani"}, "gl": {"name": "Galego", "english_name": "Galician"}, "lg": {"english_name": "Ganda"}, "lb": {"english_name": "Luxembourgish"}, "la": {"english_name": "Latin"}, "ln": {"english_name": "Lingala"}, "lo": {"english_name": "Lao"}, "tt": {"name": "Татарча", "english_name": "Tatar"}, "tr": {"name": "Türkçe", "english_name": "Turkish"}, "ts": {"english_name": "Tsonga"}, "li": {"english_name": "Limburgan"}, "lv": {"name": "Latviešu", "english_name": "Latvian"}, "to": {"english_name": "Tonga (Tonga Islands)"}, "lt": {"name": "Lietuvių", "english_name": "Lithuanian"}, "lu": {"english_name": "Luba-Katanga"}, "tk": {"english_name": "Turkmen"}, "th": {"name": "ไทย", "english_name": "Thai"}, "ti": {"name": "ትግርኛ", "english_name": "Tigrinya"}, "tg": {"english_name": "Tajik"}, "te": {"english_name": "Telugu"}, "ta": {"name": "தமிழ்", "english_name": "Tamil"}, "yi": {"english_name": "Yiddish"}, "yo": {"english_name": "Yoruba"}, "de": {"name": "Deutsch", "english_name": "German"}, "da": {"name": "Dansk", "english_name": "Danish"}, "dz": {"english_name": "Dzongkha"}, "st": {"english_name": "Sotho, Southern"}, "dv": {"english_name": "Dhivehi"}, "qu": {"english_name": "Quechua"}, "el": {"name": "Ελληνικά", "english_name": "Greek, Modern (1453-)"}, "eo": {"name": "Esperanto", "english_name": "Esperanto"}, "en": {"english_name": "English"}, "zh": {"name": "中文", "english_name": "Chinese"}, "ee": {"english_name": "Ewe"}, "za": {"english_name": "Zhuang"}, "mh": {"english_name": "Marshallese"}, "uk": {"name": "українська", "english_name": "Ukrainian"}, "eu": {"name": "Euskara", "english_name": "Basque"}, "et": {"name": "Eesti", "english_name": "Estonian"}, "es": {"name": "Español", "english_name": "Spanish"}, "ru": {"name": "русский", "english_name": "Russian"}, "rw": {"name": "Ikinyarwanda", "english_name": "Kinyarwanda"}, "rm": {"english_name": "Romansh"}, "rn": {"english_name": "Rundi"}, "ro": {"name": "Română", "english_name": "Romanian"}, "bn": {"name": "বাংলা", "english_name": "Bengali"}, "be": {"english_name": "Belarusian"}, "bg": {"name": "Български", "english_name": "Bulgarian"}, "ba": {"english_name": "Bashkir"}, "wa": {"name": "Walon", "english_name": "Walloon"}, "wo": {"english_name": "Wolof"}, "bm": {"english_name": "Bambara"}, "jv": {"english_name": "Javanese"}, "bo": {"english_name": "Tibetan"}, "bi": {"english_name": "Bislama"}, "br": {"name": "Brezhoneg", "english_name": "Breton"}, "bs": {"name": "Bosnian", "english_name": "Bosnian"}, "ja": {"name": "日本語", "english_name": "Japanese"}, "om": {"english_name": "Oromo"}, "oj": {"english_name": "Ojibwa"}, "ty": {"english_name": "Tahitian"}, "oc": {"name": "Occitan", "english_name": "Occitan"}, "tw": {"english_name": "Twi"}, "os": {"english_name": "Ossetian"}, "or": {"name": "Oriya", "english_name": "Oriya"}, "xh": {"name": "Xhosa", "english_name": "Xhosa"}, "ch": {"english_name": "Chamorro"}, "co": {"english_name": "Corsican"}, "ca": {"name": "Català", "english_name": "Catalan"}, "ce": {"english_name": "Chechen"}, "cy": {"name": "Cymraeg", "english_name": "Welsh"}, "cs": {"name": "čeština", "english_name": "Czech"}, "cr": {"english_name": "Cree"}, "cv": {"english_name": "Chuvash"}, "cu": {"english_name": "Slavic, Church"}, "ve": {"name": "Venda", "english_name": "Venda"}, "ps": {"name": "Pushto", "english_name": "Pushto"}, "pt": {"name": "Português", "english_name": "Portuguese"}, "tl": {"english_name": "Tagalog"}, "pa": {"name": "ਪੰਜਾਬੀ", "english_name": "Panjabi"}, "vi": {"name": "Tiếng Việt", "english_name": "Vietnamese"}, "pi": {"english_name": "Pali"}, "is": {"name": "Íslenska", "english_name": "Icelandic"}, "pl": {"name": "polski", "english_name": "Polish"}, "hz": {"english_name": "Herero"}, "hy": {"english_name": "Armenian"}, "hr": {"name": "hrvatski", "english_name": "Croatian"}, "iu": {"english_name": "Inuktitut"}, "ht": {"english_name": "Haitian"}, "hu": {"name": "magyar", "english_name": "Hungarian"}, "hi": {"name": "हिंदी", "english_name": "Hindi"}, "ho": {"english_name": "Hiri Motu"}, "ha": {"english_name": "Hausa"}, "he": {"name": "עברית", "english_name": "Hebrew"}, "mg": {"english_name": "Malagasy"}, "uz": {"english_name": "Uzbek"}, "ml": {"english_name": "Malayalam"}, "mn": {"name": "Монгол", "english_name": "Mongolian"}, "mi": {"name": "Reo Māori", "english_name": "Maori"}, "ik": {"english_name": "Inupiaq"}, "mk": {"name": "Македонски", "english_name": "Macedonian"}, "ur": {"english_name": "Urdu"}, "mt": {"name": "Malti", "english_name": "Maltese"}, "ms": {"name": "Malay", "english_name": "Malay"}, "mr": {"name": "मराठी", "english_name": "Marathi"}, "ug": {"english_name": "Uighur"}, "my": {"english_name": "Burmese"}, "sq": {"english_name": "Albanian"}, "ae": {"english_name": "Avestan"}, "ss": {"english_name": "Swati"}, "af": {"name": "Afrikaans", "english_name": "Afrikaans"}, "tn": {"english_name": "Tswana"}, "sw": {"english_name": "Swahili (macrolanguage)"}, "ak": {"english_name": "Akan"}, "am": {"name": "አማርኛ", "english_name": "Amharic"}, "it": {"name": "Italiano", "english_name": "Italian"}, "an": {"english_name": "Aragonese"}, "ii": {"english_name": "Yi, Sichuan"}, "ia": {"english_name": "Interlingua"}, "as": {"english_name": "Assamese"}, "ar": {"name": "العربية", "english_name": "Arabic"}, "su": {"english_name": "Sundanese"}, "io": {"english_name": "Ido"}, "av": {"english_name": "Avaric"}, "ay": {"english_name": "Aymara"}, "az": {"name": "Azerbaijani", "english_name": "Azerbaijani"}, "ie": {"english_name": "Interlingue"}, "id": {"name": "Indonesian", "english_name": "Indonesian"}, "ig": {"english_name": "Igbo"}, "sk": {"name": "Slovenský", "english_name": "Slovak"}, "sr": {"name": "српски", "english_name": "Serbian"}, "nl": {"name": "Nederlands", "english_name": "Dutch"}, "nn": {"name": "Norwegian Nynorsk", "english_name": "Norwegian Nynorsk"}, "no": {"english_name": "Norwegian"}, "na": {"english_name": "Nauru"}, "nb": {"name": "Norwegian Bokmål", "english_name": "Norwegian Bokmål"}, "nd": {"english_name": "Ndebele, North"}, "ne": {"english_name": "Nepali (macrolanguage)"}, "ng": {"english_name": "Ndonga"}, "ny": {"english_name": "Nyanja"}, "vo": {"english_name": "Volapük"}, "zu": {"name": "Isi-Zulu", "english_name": "Zulu"}, "so": {"english_name": "Somali"}, "nr": {"english_name": "Ndebele, South"}, "nv": {"english_name": "Navajo"}, "sn": {"english_name": "Shona"}, "fr": {"name": "français", "english_name": "French"}, "sm": {"english_name": "Samoan"}, "fy": {"english_name": "Frisian, Western"}, "sv": {"name": "Svenska", "english_name": "Swedish"}, "fa": {"name": "فارسی", "english_name": "Persian"}, "ff": {"english_name": "Fulah"}, "fi": {"name": "suomi", "english_name": "Finnish"}, "fj": {"english_name": "Fijian"}, "sa": {"english_name": "Sanskrit"}, "fo": {"english_name": "Faroese"}, "ka": {"english_name": "Georgian"}, "kg": {"english_name": "Kongo"}, "kk": {"english_name": "Kazakh"}, "kj": {"english_name": "Kuanyama"}, "ki": {"english_name": "Kikuyu"}, "ko": {"name": "한국어", "english_name": "Korean"}, "kn": {"name": "ಕನ್ನಡ", "english_name": "Kannada"}, "km": {"english_name": "Khmer, Central"}, "kl": {"english_name": "Kalaallisut"}, "ks": {"english_name": "Kashmiri"}, "kr": {"english_name": "Kanuri"}, "si": {"english_name": "Sinhala"}, "sh": {"name": "Serbo-Croatian", "english_name": "Serbo-Croatian"}, "kw": {"english_name": "Cornish"}, "kv": {"english_name": "Komi"}, "ku": {"english_name": "Kurdish"}, "sl": {"name": "slovenščina", "english_name": "Slovenian"}, "sc": {"english_name": "Sardinian"}, "ky": {"english_name": "Kirghiz"}, "sg": {"english_name": "Sango"}, "se": {"english_name": "Sami, Northern"}, "sd": {"english_name": "Sindhi"}}, "yahoo news": ["ar", "bg", "zh-chs", "zh-cht", "hr", "cs", "da", "nl", "en", "et", "fi", "fr", "de", "el", "he", "hu", "it", "ja", "ko", "lv", "lt", "no", "pl", "pt", "ro", "ru", "sk", "sl", "es", "sv", "th", "tr"], "swisscows": ["browser", "ar-SA", "es-AR", "en-AU", "de-AT", "fr-BE", "nl-BE", "pt-BR", "en-CA", "fr-CA", "es-CL", "zh-CN", "da-DK", "fi-FI", "fr-FR", "de-DE", "zh-HK", "en-IN", "en-IE", "it-IT", "ja-JP", "ko-KR", "en-MY", "es-MX", "nl-NL", "en-NZ", "no-NO", "en-PH", "pl-PL", "pt-PT", "ru-RU", "en-ZA", "es-ES", "sv-SE", "de-CH", "fr-CH", "zh-TW", "tr-TR", "en-GB", "en-US", "es-US"], "qwant images": ["el-GR", "en-GB", "en-IE", "en-CY", "en-GD", "en-US", "en-CA", "en-SG", "en-IN", "en-MY", "en-AU", "en-PH", "en-NZ", "co-FR", "vi-VN", "it-IT", "it-CH", "cy-GB", "ar-SA", "et-EE", "cs-CZ", "zh-TW", "id-ID", "es-ES", "es-AR", "es-CL", "es-CO", "es-MX", "es-PE", "ru-RU", "nl-BE", "nl-NL", "pt-BR", "pt-PT", "no-NO", "tr-TR", "th-TH", "ro-RO", "pl-PL", "fr-FR", "fr-BE", "fr-CH", "fr-CA", "bg-BG", "de-DE", "de-CH", "de-AT", "hu-HU", "fi-FI", "da-DK", "ja-JP", "he-IL", "ko-KR", "sv-SE", "gd-GB", "ms-MY"], "wikidata": {"gv": {"articles": 4955, "name": "Gaelg", "english_name": "Manx"}, "sco": {"articles": 42405, "name": "Scots", "english_name": "Scots"}, "scn": {"articles": 25379, "name": "Sicilianu", "english_name": "Sicilian"}, "wuu": {"articles": 5688, "name": "吴语", "english_name": "Wu"}, "tcy": {"articles": 750, "name": "ತುಳು", "english_name": "Tulu"}, "cdo": {"articles": 4239, "name": "Mìng-dĕ̤ng-ngṳ̄", "english_name": "Min Dong"}, "gu": {"articles": 26842, "name": "ગુજરાતી", "english_name": "Gujarati"}, "kbd": {"articles": 1569, "name": "Адыгэбзэ (Adighabze)", "english_name": "Kabardian Circassian"}, "gd": {"articles": 14368, "name": "Gàidhlig", "english_name": "Scottish Gaelic"}, "jbo": {"articles": 1197, "name": "Lojban", "english_name": "Lojban"}, "ga": {"articles": 39393, "name": "Gaeilge", "english_name": "Irish"}, "gn": {"articles": 3133, "name": "Avañe'ẽ", "english_name": "Guarani"}, "gl": {"articles": 136535, "name": "Galego", "english_name": "Galician"}, "als": {"articles": 22592, "name": "Alemannisch", "english_name": "Alemannic"}, "lg": {"articles": 1135, "name": "Luganda", "english_name": "Luganda"}, "hak": {"articles": 7386, "name": "Hak-kâ-fa / 客家話", "english_name": "Hakka"}, "lb": {"articles": 48141, "name": "Lëtzebuergesch", "english_name": "Luxembourgish"}, "szl": {"articles": 5491, "name": "Ślůnski", "english_name": "Silesian"}, "vep": {"articles": 5339, "name": "Vepsän", "english_name": "Vepsian"}, "la": {"articles": 126249, "name": "Latina", "english_name": "Latin"}, "ln": {"articles": 2786, "name": "Lingala", "english_name": "Lingala"}, "frp": {"articles": 2608, "name": "Arpitan", "english_name": "Franco-Provençal"}, "tt": {"articles": 70258, "name": "Tatarça / Татарча", "english_name": "Tatar"}, "tr": {"articles": 292026, "name": "Türkçe", "english_name": "Turkish"}, "cbk-zam": {"articles": 2967, "name": "Chavacano de Zamboanga", "english_name": "Zamboanga Chavacano"}, "li": {"articles": 11620, "name": "Limburgs", "english_name": "Limburgish"}, "lv": {"articles": 75872, "name": "Latviešu", "english_name": "Latvian"}, "to": {"articles": 1688, "name": "faka Tonga", "english_name": "Tongan"}, "tl": {"articles": 66203, "name": "Tagalog", "english_name": "Tagalog"}, "jam": {"articles": 1597, "name": "Jumiekan Kryuol", "english_name": "Jamaican Patois"}, "vec": {"articles": 10877, "name": "Vèneto", "english_name": "Venetian"}, "th": {"articles": 114824, "name": "ไทย", "english_name": "Thai"}, "ti": {"articles": 175, "name": "ትግርኛ", "english_name": "Tigrinya"}, "tg": {"articles": 67713, "name": "Тоҷикӣ", "english_name": "Tajik"}, "te": {"articles": 66514, "name": "తెలుగు", "english_name": "Telugu"}, "ksh": {"articles": 2825, "name": "Ripoarisch", "english_name": "Ripuarian"}, "pcd": {"articles": 3402, "name": "Picard", "english_name": "Picard"}, "ta": {"articles": 91441, "name": "தமிழ்", "english_name": "Tamil"}, "yi": {"articles": 13744, "name": "ייִדיש", "english_name": "Yiddish"}, "lrc": {"articles": 5280, "name": "لۊری شومالی", "english_name": "Northern Luri"}, "xmf": {"articles": 9318, "name": "მარგალური (Margaluri)", "english_name": "Mingrelian"}, "ceb": {"articles": 4049908, "name": "Sinugboanong Binisaya", "english_name": "Cebuano"}, "yo": {"articles": 31528, "name": "Yorùbá", "english_name": "Yoruba"}, "de": {"articles": 2035837, "name": "Deutsch", "english_name": "German"}, "da": {"articles": 223944, "name": "Dansk", "english_name": "Danish"}, "za": {"articles": 1161, "name": "Cuengh", "english_name": "Zhuang"}, "pdc": {"articles": 1790, "name": "Deitsch", "english_name": "Pennsylvania German"}, "bxr": {"articles": 1873, "name": "Буряад", "english_name": "Buryat"}, "dz": {"articles": 217, "name": "ཇོང་ཁ", "english_name": "Dzongkha"}, "hif": {"articles": 9646, "name": "Fiji Hindi", "english_name": "Fiji Hindi"}, "rm": {"articles": 3417, "name": "Rumantsch", "english_name": "Romansh"}, "dv": {"articles": 2970, "name": "ދިވެހިބަސް", "english_name": "Divehi"}, "qu": {"articles": 20037, "name": "Runa Simi", "english_name": "Quechua"}, "vls": {"articles": 5988, "name": "West-Vlams", "english_name": "West Flemish"}, "bar": {"articles": 22085, "name": "Boarisch", "english_name": "Bavarian"}, "eml": {"articles": 8670, "name": "Emiliàn e rumagnòl", "english_name": "Emilian-Romagnol"}, "kn": {"articles": 21679, "name": "ಕನ್ನಡ", "english_name": "Kannada"}, "fiu-vro": {"articles": 5449, "name": "Võro", "english_name": "Võro"}, "mo": {"articles": 394, "name": "Молдовеняскэ", "english_name": "Moldovan"}, "bpy": {"articles": 25069, "name": "ইমার ঠার/বিষ্ণুপ্রিয়া মণিপুরী", "english_name": "Bishnupriya Manipuri"}, "crh": {"articles": 5113, "name": "Qırımtatarca", "english_name": "Crimean Tatar"}, "mhr": {"articles": 9413, "name": "Олык Марий (Olyk Marij)", "english_name": "Meadow Mari"}, "diq": {"articles": 7537, "name": "Zazaki", "english_name": "Zazaki"}, "el": {"articles": 127970, "name": "Ελληνικά", "english_name": "Greek"}, "eo": {"articles": 237478, "name": "Esperanto", "english_name": "Esperanto"}, "en": {"articles": 5343454, "name": "English", "english_name": "English"}, "zh": {"articles": 927993, "name": "中文", "english_name": "Chinese"}, "pms": {"articles": 64046, "name": "Piemontèis", "english_name": "Piedmontese"}, "ee": {"articles": 334, "name": "Eʋegbe", "english_name": "Ewe"}, "tpi": {"articles": 1351, "name": "Tok Pisin", "english_name": "Tok Pisin"}, "arz": {"articles": 16354, "name": "مصرى (Maṣri)", "english_name": "Egyptian Arabic"}, "rmy": {"articles": 583, "name": "romani - रोमानी", "english_name": "Romani"}, "mdf": {"articles": 1133, "name": "Мокшень (Mokshanj Kälj)", "english_name": "Moksha"}, "kaa": {"articles": 1955, "name": "Qaraqalpaqsha", "english_name": "Karakalpak"}, "olo": {"articles": 1934, "name": "Karjalan", "english_name": "Livvi-Karelian"}, "arc": {"articles": 1617, "name": "ܐܪܡܝܐ", "english_name": "Aramaic"}, "cr": {"articles": 125, "name": "Nehiyaw", "english_name": "Cree"}, "eu": {"articles": 277712, "name": "Euskara", "english_name": "Basque"}, "et": {"articles": 154506, "name": "Eesti", "english_name": "Estonian"}, "tet": {"articles": 1390, "name": "Tetun", "english_name": "Tetum"}, "es": {"articles": 1318337, "name": "Español", "english_name": "Spanish"}, "ba": {"articles": 37407, "name": "Башҡорт", "english_name": "Bashkir"}, "gom": {"articles": 3119, "name": "गोंयची कोंकणी / Gõychi Konknni", "english_name": "Goan Konkani"}, "ru": {"articles": 1375970, "name": "Русский", "english_name": "Russian"}, "roa-tara": {"articles": 9229, "name": "Tarandíne", "english_name": "Tarantino"}, "ha": {"articles": 1410, "name": "هَوُسَ", "english_name": "Hausa"}, "ak": {"articles": 271, "name": "Akana", "english_name": "Akan"}, "lad": {"articles": 4421, "name": "Dzhudezmo", "english_name": "Ladino"}, "bm": {"articles": 411, "name": "Bamanankan", "english_name": "Bambara"}, "new": {"articles": 72123, "name": "नेपाल भाषा", "english_name": "Newar"}, "rn": {"articles": 495, "name": "Kirundi", "english_name": "Kirundi"}, "ro": {"articles": 374753, "name": "Română", "english_name": "Romanian"}, "dsb": {"articles": 3071, "name": "Dolnoserbski", "english_name": "Lower Sorbian"}, "jv": {"articles": 49700, "name": "Basa Jawa", "english_name": "Javanese"}, "hsb": {"articles": 11071, "name": "Hornjoserbsce", "english_name": "Upper Sorbian"}, "be": {"articles": 126878, "name": "Беларуская", "english_name": "Belarusian"}, "bg": {"articles": 227507, "name": "Български", "english_name": "Bulgarian"}, "myv": {"articles": 3510, "name": "Эрзянь (Erzjanj Kelj)", "english_name": "Erzya"}, "uk": {"articles": 682728, "name": "Українська", "english_name": "Ukrainian"}, "wa": {"articles": 14408, "name": "Walon", "english_name": "Walloon"}, "ast": {"articles": 48457, "name": "Asturianu", "english_name": "Asturian"}, "wo": {"articles": 1132, "name": "Wolof", "english_name": "Wolof"}, "got": {"articles": 493, "name": "𐌲𐌿𐍄𐌹𐍃𐌺", "english_name": "Gothic"}, "bn": {"articles": 48261, "name": "বাংলা", "english_name": "Bengali"}, "bo": {"articles": 5717, "name": "བོད་སྐད", "english_name": "Tibetan"}, "bh": {"articles": 8674, "name": "भोजपुरी", "english_name": "Bihari"}, "bi": {"articles": 817, "name": "Bislama", "english_name": "Bislama"}, "rue": {"articles": 5963, "name": "Русиньскый", "english_name": "Rusyn"}, "map-bms": {"articles": 13284, "name": "Basa Banyumasan", "english_name": "Banyumasan"}, "tum": {"articles": 564, "name": "chiTumbuka", "english_name": "Tumbuka"}, "br": {"articles": 61346, "name": "Brezhoneg", "english_name": "Breton"}, "bs": {"articles": 73437, "name": "Bosanski", "english_name": "Bosnian"}, "lez": {"articles": 3606, "name": "Лезги чІал (Lezgi č’al)", "english_name": "Lezgian"}, "ja": {"articles": 1050743, "name": "日本語", "english_name": "Japanese"}, "om": {"articles": 725, "name": "Oromoo", "english_name": "Oromo"}, "glk": {"articles": 6076, "name": "گیلکی", "english_name": "Gilaki"}, "ace": {"articles": 4077, "name": "Bahsa Acèh", "english_name": "Acehnese"}, "ilo": {"articles": 10461, "name": "Ilokano", "english_name": "Ilokano"}, "roa-rup": {"articles": 1206, "name": "Armãneashce", "english_name": "Aromanian"}, "oc": {"articles": 82700, "name": "Occitan", "english_name": "Occitan"}, "ltg": {"articles": 797, "name": "Latgaļu", "english_name": "Latgalian"}, "be-tarask": {"articles": 60248, "name": "Беларуская (тарашкевіца)", "english_name": "Belarusian (Taraškievica)"}, "st": {"articles": 384, "name": "Sesotho", "english_name": "Sesotho"}, "lo": {"articles": 1623, "name": "ລາວ", "english_name": "Lao"}, "krc": {"articles": 2016, "name": "Къарачай-Малкъар (Qarachay-Malqar)", "english_name": "Karachay-Balkar"}, "nds": {"articles": 25822, "name": "Plattdüütsch", "english_name": "Low Saxon"}, "os": {"articles": 10382, "name": "Иронау", "english_name": "Ossetian"}, "or": {"articles": 12252, "name": "ଓଡ଼ିଆ", "english_name": "Oriya"}, "udm": {"articles": 3875, "name": "Удмурт кыл", "english_name": "Udmurt"}, "xh": {"articles": 604, "name": "isiXhosa", "english_name": "Xhosa"}, "ch": {"articles": 419, "name": "Chamoru", "english_name": "Chamorro"}, "co": {"articles": 5424, "name": "Corsu", "english_name": "Corsican"}, "nso": {"articles": 7642, "name": "Sepedi", "english_name": "Northern Sotho"}, "simple": {"articles": 123305, "name": "Simple English", "english_name": "Simple English"}, "bjn": {"articles": 1710, "name": "Bahasa Banjar", "english_name": "Banjar"}, "ca": {"articles": 535045, "name": "Català", "english_name": "Catalan"}, "lmo": {"articles": 34788, "name": "Lumbaart", "english_name": "Lombard"}, "ce": {"articles": 160122, "name": "Нохчийн", "english_name": "Chechen"}, "ts": {"articles": 392, "name": "Xitsonga", "english_name": "Tsonga"}, "cy": {"articles": 90328, "name": "Cymraeg", "english_name": "Welsh"}, "ang": {"articles": 2857, "name": "Englisc", "english_name": "Anglo-Saxon"}, "cs": {"articles": 374940, "name": "Čeština", "english_name": "Czech"}, "ty": {"articles": 1179, "name": "Reo Mā`ohi", "english_name": "Tahitian"}, "ady": {"articles": 399, "name": "Адыгэбзэ", "english_name": "Adyghe"}, "cv": {"articles": 38132, "name": "Чăваш", "english_name": "Chuvash"}, "cu": {"articles": 582, "name": "Словѣньскъ", "english_name": "Old Church Slavonic"}, "ve": {"articles": 238, "name": "Tshivenda", "english_name": "Venda"}, "koi": {"articles": 3429, "name": "Перем Коми (Perem Komi)", "english_name": "Komi-Permyak"}, "ps": {"articles": 7991, "name": "پښتو", "english_name": "Pashto"}, "fj": {"articles": 340, "name": "Na Vosa Vakaviti", "english_name": "Fijian"}, "srn": {"articles": 1047, "name": "Sranantongo", "english_name": "Sranan"}, "pt": {"articles": 957637, "name": "Português", "english_name": "Portuguese"}, "sm": {"articles": 745, "name": "Gagana Samoa", "english_name": "Samoan"}, "ext": {"articles": 2898, "name": "Estremeñu", "english_name": "Extremaduran"}, "lt": {"articles": 181095, "name": "Lietuvių", "english_name": "Lithuanian"}, "zh-min-nan": {"articles": 203047, "name": "Bân-lâm-gú", "english_name": "Min Nan"}, "frr": {"articles": 4712, "name": "Nordfriisk", "english_name": "North Frisian"}, "chr": {"articles": 785, "name": "ᏣᎳᎩ", "english_name": "Cherokee"}, "pa": {"articles": 24776, "name": "ਪੰਜਾਬੀ", "english_name": "Punjabi"}, "xal": {"articles": 2073, "name": "Хальмг", "english_name": "Kalmyk"}, "chy": {"articles": 607, "name": "Tsetsêhestâhese", "english_name": "Cheyenne"}, "pi": {"articles": 2517, "name": "पाऴि", "english_name": "Pali"}, "war": {"articles": 1262274, "name": "Winaray", "english_name": "Waray-Waray"}, "pl": {"articles": 1209184, "name": "Polski", "english_name": "Polish"}, "tk": {"articles": 5193, "name": "تركمن / Туркмен", "english_name": "Turkmen"}, "hy": {"articles": 216349, "name": "Հայերեն", "english_name": "Armenian"}, "an": {"articles": 31888, "name": "Aragonés", "english_name": "Aragonese"}, "nrm": {"articles": 3621, "name": "Nouormand/Normaund", "english_name": "Norman"}, "hr": {"articles": 172218, "name": "Hrvatski", "english_name": "Croatian"}, "iu": {"articles": 391, "name": "ᐃᓄᒃᑎᑐᑦ", "english_name": "Inuktitut"}, "pfl": {"articles": 2067, "name": "Pälzisch", "english_name": "Palatinate German"}, "ht": {"articles": 51150, "name": "Krèyol ayisyen", "english_name": "Haitian"}, "hu": {"articles": 404333, "name": "Magyar", "english_name": "Hungarian"}, "gan": {"articles": 6367, "name": "贛語", "english_name": "Gan"}, "bat-smg": {"articles": 16015, "name": "Žemaitėška", "english_name": "Samogitian"}, "hi": {"articles": 117254, "name": "हिन्दी", "english_name": "Hindi"}, "tw": {"articles": 584, "name": "Twi", "english_name": "Twi"}, "gag": {"articles": 2744, "name": "Gagauz", "english_name": "Gagauz"}, "kg": {"articles": 1170, "name": "KiKongo", "english_name": "Kongo"}, "pnb": {"articles": 43662, "name": "شاہ مکھی پنجابی (Shāhmukhī Pañjābī)", "english_name": "Western Punjabi"}, "bug": {"articles": 14118, "name": "Basa Ugi", "english_name": "Buginese"}, "he": {"articles": 202507, "name": "עברית", "english_name": "Hebrew"}, "mg": {"articles": 83434, "name": "Malagasy", "english_name": "Malagasy"}, "fur": {"articles": 3173, "name": "Furlan", "english_name": "Friulian"}, "uz": {"articles": 128882, "name": "O‘zbek", "english_name": "Uzbek"}, "ml": {"articles": 47909, "name": "മലയാളം", "english_name": "Malayalam"}, "azb": {"articles": 14683, "name": "تۆرکجه", "english_name": "South Azerbaijani"}, "mn": {"articles": 16812, "name": "Монгол", "english_name": "Mongolian"}, "mi": {"articles": 7112, "name": "Māori", "english_name": "Maori"}, "ik": {"articles": 246, "name": "Iñupiak", "english_name": "Inupiak"}, "mk": {"articles": 88524, "name": "Македонски", "english_name": "Macedonian"}, "ur": {"articles": 115102, "name": "اردو", "english_name": "Urdu"}, "zea": {"articles": 4369, "name": "Zeêuws", "english_name": "Zeelandic"}, "mt": {"articles": 3190, "name": "Malti", "english_name": "Maltese"}, "stq": {"articles": 3754, "name": "Seeltersk", "english_name": "Saterland Frisian"}, "ms": {"articles": 287146, "name": "Bahasa Melayu", "english_name": "Malay"}, "mr": {"articles": 46054, "name": "मराठी", "english_name": "Marathi"}, "ug": {"articles": 3273, "name": "ئۇيغۇر تىلى", "english_name": "Uyghur"}, "mwl": {"articles": 2828, "name": "Mirandés", "english_name": "Mirandese"}, "my": {"articles": 34904, "name": "မြန်မာဘာသာ", "english_name": "Burmese"}, "ki": {"articles": 1339, "name": "Gĩkũyũ", "english_name": "Kikuyu"}, "pih": {"articles": 514, "name": "Norfuk", "english_name": "Norfolk"}, "sah": {"articles": 11079, "name": "Саха тыла (Saxa Tyla)", "english_name": "Sakha"}, "ss": {"articles": 421, "name": "SiSwati", "english_name": "Swati"}, "af": {"articles": 43924, "name": "Afrikaans", "english_name": "Afrikaans"}, "tn": {"articles": 620, "name": "Setswana", "english_name": "Tswana"}, "vi": {"articles": 1153964, "name": "Tiếng Việt", "english_name": "Vietnamese"}, "is": {"articles": 41946, "name": "Íslenska", "english_name": "Icelandic"}, "am": {"articles": 13359, "name": "አማርኛ", "english_name": "Amharic"}, "it": {"articles": 1337509, "name": "Italiano", "english_name": "Italian"}, "vo": {"articles": 120479, "name": "Volapük", "english_name": "Volapük"}, "ay": {"articles": 3900, "name": "Aymar", "english_name": "Aymara"}, "as": {"articles": 4462, "name": "অসমীয়া", "english_name": "Assamese"}, "ar": {"articles": 468031, "name": "العربية", "english_name": "Arabic"}, "lbe": {"articles": 1210, "name": "Лакку", "english_name": "Lak"}, "km": {"articles": 5029, "name": "ភាសាខ្មែរ", "english_name": "Khmer"}, "io": {"articles": 26917, "name": "Ido", "english_name": "Ido"}, "av": {"articles": 2302, "name": "Авар", "english_name": "Avar"}, "ia": {"articles": 19677, "name": "Interlingua", "english_name": "Interlingua"}, "haw": {"articles": 1976, "name": "Hawai`i", "english_name": "Hawaiian"}, "az": {"articles": 114754, "name": "Azərbaycanca", "english_name": "Azerbaijani"}, "ie": {"articles": 3594, "name": "Interlingue", "english_name": "Interlingue"}, "id": {"articles": 394114, "name": "Bahasa Indonesia", "english_name": "Indonesian"}, "nds-nl": {"articles": 6724, "name": "Nedersaksisch", "english_name": "Dutch Low Saxon"}, "pap": {"articles": 1769, "name": "Papiamentu", "english_name": "Papiamentu"}, "ks": {"articles": 268, "name": "कश्मीरी / كشميري", "english_name": "Kashmiri"}, "nl": {"articles": 1894647, "name": "Nederlands", "english_name": "Dutch"}, "nn": {"articles": 132461, "name": "Nynorsk", "english_name": "Norwegian (Nynorsk)"}, "no": {"articles": 462394, "name": "Norsk (Bokmål)", "english_name": "Norwegian (Bokmål)"}, "na": {"articles": 1269, "name": "dorerin Naoero", "english_name": "Nauruan"}, "nah": {"articles": 8763, "name": "Nāhuatl", "english_name": "Nahuatl"}, "ne": {"articles": 29974, "name": "नेपाली", "english_name": "Nepali"}, "lij": {"articles": 3236, "name": "Líguru", "english_name": "Ligurian"}, "csb": {"articles": 5128, "name": "Kaszëbsczi", "english_name": "Kashubian"}, "tyv": {"articles": 1366, "name": "Тыва", "english_name": "Tuvan"}, "ny": {"articles": 364, "name": "Chichewa", "english_name": "Chichewa"}, "nap": {"articles": 14434, "name": "Nnapulitano", "english_name": "Neapolitan"}, "ig": {"articles": 1308, "name": "Igbo", "english_name": "Igbo"}, "pag": {"articles": 2526, "name": "Pangasinan", "english_name": "Pangasinan"}, "zu": {"articles": 918, "name": "isiZulu", "english_name": "Zulu"}, "kw": {"articles": 3755, "name": "Kernewek/Karnuack", "english_name": "Cornish"}, "pam": {"articles": 8525, "name": "Kapampangan", "english_name": "Kapampangan"}, "nv": {"articles": 2638, "name": "Diné bizaad", "english_name": "Navajo"}, "sn": {"articles": 2678, "name": "chiShona", "english_name": "Shona"}, "kab": {"articles": 2842, "name": "Taqbaylit", "english_name": "Kabyle"}, "fr": {"articles": 1846517, "name": "Français", "english_name": "French"}, "mrj": {"articles": 10165, "name": "Кырык Мары (Kyryk Mary)", "english_name": "Hill Mari"}, "zh-yue": {"articles": 51227, "name": "粵語", "english_name": "Cantonese"}, "fy": {"articles": 37193, "name": "Frysk", "english_name": "West Frisian"}, "pnt": {"articles": 448, "name": "Ποντιακά", "english_name": "Pontic"}, "fa": {"articles": 524259, "name": "فارسی", "english_name": "Persian"}, "rw": {"articles": 1804, "name": "Ikinyarwanda", "english_name": "Kinyarwanda"}, "ff": {"articles": 213, "name": "Fulfulde", "english_name": "Fula"}, "mai": {"articles": 10392, "name": "मैथिली", "english_name": "Maithili"}, "fi": {"articles": 409282, "name": "Suomi", "english_name": "Finnish"}, "mzn": {"articles": 12372, "name": "مَزِروني", "english_name": "Mazandarani"}, "ab": {"articles": 1217, "name": "Аҧсуа", "english_name": "Abkhazian"}, "sa": {"articles": 10745, "name": "संस्कृतम्", "english_name": "Sanskrit"}, "zh-classical": {"articles": 5142, "name": "古文 / 文言文", "english_name": "Classical Chinese"}, "fo": {"articles": 12449, "name": "Føroyskt", "english_name": "Faroese"}, "bcl": {"articles": 7020, "name": "Bikol", "english_name": "Central Bicolano"}, "ka": {"articles": 113079, "name": "ქართული", "english_name": "Georgian"}, "nov": {"articles": 1645, "name": "Novial", "english_name": "Novial"}, "ckb": {"articles": 18597, "name": "Soranî / کوردی", "english_name": "Sorani"}, "kk": {"articles": 218109, "name": "Қазақша", "english_name": "Kazakh"}, "sr": {"articles": 345474, "name": "Српски / Srpski", "english_name": "Serbian"}, "sq": {"articles": 64573, "name": "Shqip", "english_name": "Albanian"}, "min": {"articles": 221972, "name": "Minangkabau", "english_name": "Minangkabau"}, "ko": {"articles": 373631, "name": "한국어", "english_name": "Korean"}, "sv": {"articles": 3783165, "name": "Svenska", "english_name": "Swedish"}, "su": {"articles": 19256, "name": "Basa Sunda", "english_name": "Sundanese"}, "kl": {"articles": 1643, "name": "Kalaallisut", "english_name": "Greenlandic"}, "sk": {"articles": 216444, "name": "Slovenčina", "english_name": "Slovak"}, "si": {"articles": 13236, "name": "සිංහල", "english_name": "Sinhalese"}, "sh": {"articles": 437610, "name": "Srpskohrvatski / Српскохрватски", "english_name": "Serbo-Croatian"}, "so": {"articles": 4363, "name": "Soomaali", "english_name": "Somali"}, "kv": {"articles": 4925, "name": "Коми", "english_name": "Komi"}, "ku": {"articles": 22705, "name": "Kurdî / كوردی", "english_name": "Kurdish"}, "sl": {"articles": 154822, "name": "Slovenščina", "english_name": "Slovenian"}, "sc": {"articles": 5447, "name": "Sardu", "english_name": "Sardinian"}, "ky": {"articles": 62712, "name": "Кыргызча", "english_name": "Kirghiz"}, "sg": {"articles": 247, "name": "Sängö", "english_name": "Sango"}, "sw": {"articles": 35324, "name": "Kiswahili", "english_name": "Swahili"}, "se": {"articles": 7278, "name": "Sámegiella", "english_name": "Northern Sami"}, "sd": {"articles": 7341, "name": "سنڌي، سندھی ، सिन्ध", "english_name": "Sindhi"}}, "qwant news": ["el-GR", "en-GB", "en-IE", "en-CY", "en-GD", "en-US", "en-CA", "en-SG", "en-IN", "en-MY", "en-AU", "en-PH", "en-NZ", "co-FR", "vi-VN", "it-IT", "it-CH", "cy-GB", "ar-SA", "et-EE", "cs-CZ", "zh-TW", "id-ID", "es-ES", "es-AR", "es-CL", "es-CO", "es-MX", "es-PE", "ru-RU", "nl-BE", "nl-NL", "pt-BR", "pt-PT", "no-NO", "tr-TR", "th-TH", "ro-RO", "pl-PL", "fr-FR", "fr-BE", "fr-CH", "fr-CA", "bg-BG", "de-DE", "de-CH", "de-AT", "hu-HU", "fi-FI", "da-DK", "ja-JP", "he-IL", "ko-KR", "sv-SE", "gd-GB", "ms-MY"], "ddg definitions": ["da-DK", "vi-VN", "en-SG", "sl-SL", "en-XA", "tzh-HK", "en-UK", "ro-RO", "en-MY", "el-GR", "it-CH", "hu-HU", "fr-FR", "en-PH", "tl-PH", "fr-CA", "fi-FI", "et-EE", "sv-SE", "es-XL", "th-TH", "sk-SK", "es-ES", "en-IE", "es-US", "es-PE", "nl-NL", "en-US", "de-DE", "de-AT", "wt-WT", "no-NO", "tr-TR", "ca-ES", "it-IT", "es-CO", "ru-RU", "ca-CT", "en-ZA", "en-CA", "jp-JP", "es-MX", "id-ID", "es-AR", "he-IL", "kr-KR", "en-AU", "ms-MY", "pl-PL", "lv-LV", "bg-BG", "zh-CN", "en-NZ", "lt-LT", "tzh-TW", "hr-HR", "pt-PT", "fr-BE", "de-CH", "cs-CZ", "en-IN", "nl-BE", "fr-CH", "en-ID", "ar-XA", "pt-BR", "uk-UA", "es-CL"], "bing images": ["sq", "de", "ar", "bg", "ca", "cs", "zh-CHS", "zh-CHT", "ko", "hr", "da", "sk", "sl", "es", "et", "fi", "fr", "el", "he", "nl", "hu", "id", "en", "is", "it", "ja", "lv", "lt", "ms", "no", "fa", "pl", "pt-BR", "pt-PT", "ro", "ru", "sr", "sv", "th", "tr", "uk", "vi"], "qwant social": ["el-GR", "en-GB", "en-IE", "en-CY", "en-GD", "en-US", "en-CA", "en-SG", "en-IN", "en-MY", "en-AU", "en-PH", "en-NZ", "co-FR", "vi-VN", "it-IT", "it-CH", "cy-GB", "ar-SA", "et-EE", "cs-CZ", "zh-TW", "id-ID", "es-ES", "es-AR", "es-CL", "es-CO", "es-MX", "es-PE", "ru-RU", "nl-BE", "nl-NL", "pt-BR", "pt-PT", "no-NO", "tr-TR", "th-TH", "ro-RO", "pl-PL", "fr-FR", "fr-BE", "fr-CH", "fr-CA", "bg-BG", "de-DE", "de-CH", "de-AT", "hu-HU", "fi-FI", "da-DK", "ja-JP", "he-IL", "ko-KR", "sv-SE", "gd-GB", "ms-MY"], "qwant": ["el-GR", "en-GB", "en-IE", "en-CY", "en-GD", "en-US", "en-CA", "en-SG", "en-IN", "en-MY", "en-AU", "en-PH", "en-NZ", "co-FR", "vi-VN", "it-IT", "it-CH", "cy-GB", "ar-SA", "et-EE", "cs-CZ", "zh-TW", "id-ID", "es-ES", "es-AR", "es-CL", "es-CO", "es-MX", "es-PE", "ru-RU", "nl-BE", "nl-NL", "pt-BR", "pt-PT", "no-NO", "tr-TR", "th-TH", "ro-RO", "pl-PL", "fr-FR", "fr-BE", "fr-CH", "fr-CA", "bg-BG", "de-DE", "de-CH", "de-AT", "hu-HU", "fi-FI", "da-DK", "ja-JP", "he-IL", "ko-KR", "sv-SE", "gd-GB", "ms-MY"], "yahoo": ["ar", "bg", "zh-chs", "zh-cht", "hr", "cs", "da", "nl", "en", "et", "fi", "fr", "de", "el", "he", "hu", "it", "ja", "ko", "lv", "lt", "no", "pl", "pt", "ro", "ru", "sk", "sl", "es", "sv", "th", "tr"], "gigablast": []} \ No newline at end of file
diff --git a/searx/engines/1337x.py b/searx/engines/1337x.py
new file mode 100644
index 0000000..0de04bd
--- /dev/null
+++ b/searx/engines/1337x.py
@@ -0,0 +1,39 @@
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.utils import get_torrent_size
+from searx.url_utils import quote, urljoin
+
+url = 'https://1337x.to/'
+search_url = url + 'search/{search_term}/{pageno}/'
+categories = ['videos']
+paging = True
+
+
+def request(query, params):
+ params['url'] = search_url.format(search_term=quote(query), pageno=params['pageno'])
+
+ return params
+
+
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ for result in dom.xpath('//table[contains(@class, "table-list")]/tbody//tr'):
+ href = urljoin(url, result.xpath('./td[contains(@class, "name")]/a[2]/@href')[0])
+ title = extract_text(result.xpath('./td[contains(@class, "name")]/a[2]'))
+ seed = extract_text(result.xpath('.//td[contains(@class, "seeds")]'))
+ leech = extract_text(result.xpath('.//td[contains(@class, "leeches")]'))
+ filesize_info = extract_text(result.xpath('.//td[contains(@class, "size")]/text()'))
+ filesize, filesize_multiplier = filesize_info.split()
+ filesize = get_torrent_size(filesize, filesize_multiplier)
+
+ results.append({'url': href,
+ 'title': title,
+ 'seed': seed,
+ 'leech': leech,
+ 'filesize': filesize,
+ 'template': 'torrent.html'})
+
+ return results
diff --git a/searx/engines/__init__.py b/searx/engines/__init__.py
new file mode 100644
index 0000000..023ec40
--- /dev/null
+++ b/searx/engines/__init__.py
@@ -0,0 +1,221 @@
+
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
+'''
+
+from os.path import realpath, dirname
+import sys
+from flask_babel import gettext
+from operator import itemgetter
+from json import loads
+from requests import get
+from searx import settings
+from searx import logger
+from searx.utils import load_module
+
+
+logger = logger.getChild('engines')
+
+engine_dir = dirname(realpath(__file__))
+
+engines = {}
+
+categories = {'general': []}
+
+languages = loads(open(engine_dir + '/../data/engines_languages.json').read())
+
+engine_shortcuts = {}
+engine_default_args = {'paging': False,
+ 'categories': ['general'],
+ 'language_support': True,
+ 'supported_languages': [],
+ 'safesearch': False,
+ 'timeout': settings['outgoing']['request_timeout'],
+ 'shortcut': '-',
+ 'disabled': False,
+ 'suspend_end_time': 0,
+ 'continuous_errors': 0,
+ 'time_range_support': False}
+
+
+def load_engine(engine_data):
+
+ if '_' in engine_data['name']:
+ logger.error('Engine name conains underscore: "{}"'.format(engine_data['name']))
+ sys.exit(1)
+
+ engine_module = engine_data['engine']
+
+ try:
+ engine = load_module(engine_module + '.py', engine_dir)
+ except:
+ logger.exception('Cannot load engine "{}"'.format(engine_module))
+ return None
+
+ for param_name in engine_data:
+ if param_name == 'engine':
+ continue
+ if param_name == 'categories':
+ if engine_data['categories'] == 'none':
+ engine.categories = []
+ else:
+ engine.categories = list(map(str.strip, engine_data['categories'].split(',')))
+ continue
+ setattr(engine, param_name, engine_data[param_name])
+
+ for arg_name, arg_value in engine_default_args.items():
+ if not hasattr(engine, arg_name):
+ setattr(engine, arg_name, arg_value)
+
+ # checking required variables
+ for engine_attr in dir(engine):
+ if engine_attr.startswith('_'):
+ continue
+ if getattr(engine, engine_attr) is None:
+ logger.error('Missing engine config attribute: "{0}.{1}"'
+ .format(engine.name, engine_attr))
+ sys.exit(1)
+
+ # assign supported languages from json file
+ if engine_data['name'] in languages:
+ setattr(engine, 'supported_languages', languages[engine_data['name']])
+
+ # assign language fetching method if auxiliary method exists
+ if hasattr(engine, '_fetch_supported_languages'):
+ setattr(engine, 'fetch_supported_languages',
+ lambda: engine._fetch_supported_languages(get(engine.supported_languages_url)))
+
+ engine.stats = {
+ 'result_count': 0,
+ 'search_count': 0,
+ 'page_load_time': 0,
+ 'page_load_count': 0,
+ 'engine_time': 0,
+ 'engine_time_count': 0,
+ 'score_count': 0,
+ 'errors': 0
+ }
+
+ for category_name in engine.categories:
+ categories.setdefault(category_name, []).append(engine)
+
+ if engine.shortcut in engine_shortcuts:
+ logger.error('Engine config error: ambigious shortcut: {0}'.format(engine.shortcut))
+ sys.exit(1)
+
+ engine_shortcuts[engine.shortcut] = engine.name
+
+ return engine
+
+
+def to_percentage(stats, maxvalue):
+ for engine_stat in stats:
+ if maxvalue:
+ engine_stat['percentage'] = int(engine_stat['avg'] / maxvalue * 100)
+ else:
+ engine_stat['percentage'] = 0
+ return stats
+
+
+def get_engines_stats():
+ # TODO refactor
+ pageloads = []
+ engine_times = []
+ results = []
+ scores = []
+ errors = []
+ scores_per_result = []
+
+ max_pageload = max_engine_times = max_results = max_score = max_errors = max_score_per_result = 0 # noqa
+ for engine in engines.values():
+ if engine.stats['search_count'] == 0:
+ continue
+ results_num = \
+ engine.stats['result_count'] / float(engine.stats['search_count'])
+
+ if engine.stats['page_load_count'] != 0:
+ load_times = engine.stats['page_load_time'] / float(engine.stats['page_load_count']) # noqa
+ else:
+ load_times = 0
+
+ if engine.stats['engine_time_count'] != 0:
+ this_engine_time = engine.stats['engine_time'] / float(engine.stats['engine_time_count']) # noqa
+ else:
+ this_engine_time = 0
+
+ if results_num:
+ score = engine.stats['score_count'] / float(engine.stats['search_count']) # noqa
+ score_per_result = score / results_num
+ else:
+ score = score_per_result = 0.0
+
+ max_pageload = max(load_times, max_pageload)
+ max_engine_times = max(this_engine_time, max_engine_times)
+ max_results = max(results_num, max_results)
+ max_score = max(score, max_score)
+ max_score_per_result = max(score_per_result, max_score_per_result)
+ max_errors = max(max_errors, engine.stats['errors'])
+
+ pageloads.append({'avg': load_times, 'name': engine.name})
+ engine_times.append({'avg': this_engine_time, 'name': engine.name})
+ results.append({'avg': results_num, 'name': engine.name})
+ scores.append({'avg': score, 'name': engine.name})
+ errors.append({'avg': engine.stats['errors'], 'name': engine.name})
+ scores_per_result.append({
+ 'avg': score_per_result,
+ 'name': engine.name
+ })
+
+ pageloads = to_percentage(pageloads, max_pageload)
+ engine_times = to_percentage(engine_times, max_engine_times)
+ results = to_percentage(results, max_results)
+ scores = to_percentage(scores, max_score)
+ scores_per_result = to_percentage(scores_per_result, max_score_per_result)
+ erros = to_percentage(errors, max_errors)
+
+ return [
+ (
+ gettext('Engine time (sec)'),
+ sorted(engine_times, key=itemgetter('avg'))
+ ),
+ (
+ gettext('Page loads (sec)'),
+ sorted(pageloads, key=itemgetter('avg'))
+ ),
+ (
+ gettext('Number of results'),
+ sorted(results, key=itemgetter('avg'), reverse=True)
+ ),
+ (
+ gettext('Scores'),
+ sorted(scores, key=itemgetter('avg'), reverse=True)
+ ),
+ (
+ gettext('Scores per result'),
+ sorted(scores_per_result, key=itemgetter('avg'), reverse=True)
+ ),
+ (
+ gettext('Errors'),
+ sorted(errors, key=itemgetter('avg'), reverse=True)
+ ),
+ ]
+
+
+def initialize_engines(engine_list):
+ for engine_data in engine_list:
+ engine = load_engine(engine_data)
+ if engine is not None:
+ engines[engine.name] = engine
diff --git a/searx/engines/archlinux.py b/searx/engines/archlinux.py
new file mode 100644
index 0000000..cad06f8
--- /dev/null
+++ b/searx/engines/archlinux.py
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+
+"""
+ Arch Linux Wiki
+
+ @website https://wiki.archlinux.org
+ @provide-api no (Mediawiki provides API, but Arch Wiki blocks access to it
+ @using-api no
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title
+"""
+
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode, urljoin
+
+# engine dependent config
+categories = ['it']
+language_support = True
+paging = True
+base_url = 'https://wiki.archlinux.org'
+
+# xpath queries
+xpath_results = '//ul[@class="mw-search-results"]/li'
+xpath_link = './/div[@class="mw-search-result-heading"]/a'
+
+
+# cut 'en' from 'en_US', 'de' from 'de_CH', and so on
+def locale_to_lang_code(locale):
+ if locale.find('-') >= 0:
+ locale = locale.split('-')[0]
+ return locale
+
+
+# wikis for some languages were moved off from the main site, we need to make
+# requests to correct URLs to be able to get results in those languages
+lang_urls = {
+ 'all': {
+ 'base': 'https://wiki.archlinux.org',
+ 'search': '/index.php?title=Special:Search&offset={offset}&{query}'
+ },
+ 'de': {
+ 'base': 'https://wiki.archlinux.de',
+ 'search': '/index.php?title=Spezial:Suche&offset={offset}&{query}'
+ },
+ 'fr': {
+ 'base': 'https://wiki.archlinux.fr',
+ 'search': '/index.php?title=Spécial:Recherche&offset={offset}&{query}'
+ },
+ 'ja': {
+ 'base': 'https://wiki.archlinuxjp.org',
+ 'search': '/index.php?title=特別:検索&offset={offset}&{query}'
+ },
+ 'ro': {
+ 'base': 'http://wiki.archlinux.ro',
+ 'search': '/index.php?title=Special:Căutare&offset={offset}&{query}'
+ },
+ 'tr': {
+ 'base': 'http://archtr.org/wiki',
+ 'search': '/index.php?title=Özel:Ara&offset={offset}&{query}'
+ }
+}
+
+
+# get base & search URLs for selected language
+def get_lang_urls(language):
+ if language in lang_urls:
+ return lang_urls[language]
+ return lang_urls['all']
+
+
+# Language names to build search requests for
+# those languages which are hosted on the main site.
+main_langs = {
+ 'ar': 'العربية',
+ 'bg': 'Български',
+ 'cs': 'Česky',
+ 'da': 'Dansk',
+ 'el': 'Ελληνικά',
+ 'es': 'Español',
+ 'he': 'עברית',
+ 'hr': 'Hrvatski',
+ 'hu': 'Magyar',
+ 'it': 'Italiano',
+ 'ko': '한국어',
+ 'lt': 'Lietuviškai',
+ 'nl': 'Nederlands',
+ 'pl': 'Polski',
+ 'pt': 'Português',
+ 'ru': 'Русский',
+ 'sl': 'Slovenský',
+ 'th': 'ไทย',
+ 'uk': 'Українська',
+ 'zh': '简体中文'
+}
+supported_languages = dict(lang_urls, **main_langs)
+
+
+# do search-request
+def request(query, params):
+ # translate the locale (e.g. 'en_US') to language code ('en')
+ language = locale_to_lang_code(params['language'])
+
+ # if our language is hosted on the main site, we need to add its name
+ # to the query in order to narrow the results to that language
+ if language in main_langs:
+ query += '(' + main_langs[language] + ')'
+
+ # prepare the request parameters
+ query = urlencode({'search': query})
+ offset = (params['pageno'] - 1) * 20
+
+ # get request URLs for our language of choice
+ urls = get_lang_urls(language)
+ search_url = urls['base'] + urls['search']
+
+ params['url'] = search_url.format(query=query, offset=offset)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ # get the base URL for the language in which request was made
+ language = locale_to_lang_code(resp.search_params['language'])
+ base_url = get_lang_urls(language)['base']
+
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath(xpath_results):
+ link = result.xpath(xpath_link)[0]
+ href = urljoin(base_url, link.attrib.get('href'))
+ title = extract_text(link)
+
+ results.append({'url': href,
+ 'title': title})
+
+ return results
diff --git a/searx/engines/base.py b/searx/engines/base.py
new file mode 100755
index 0000000..ff006a3
--- /dev/null
+++ b/searx/engines/base.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+"""
+ BASE (Scholar publications)
+
+ @website https://base-search.net
+ @provide-api yes with authorization (https://api.base-search.net/)
+
+ @using-api yes
+ @results XML
+ @stable ?
+ @parse url, title, publishedDate, content
+ More info on api: http://base-search.net/about/download/base_interface.pdf
+"""
+
+from lxml import etree
+from datetime import datetime
+import re
+from searx.url_utils import urlencode
+from searx.utils import searx_useragent
+
+
+categories = ['science']
+
+base_url = 'https://api.base-search.net/cgi-bin/BaseHttpSearchInterface.fcgi'\
+ + '?func=PerformSearch&{query}&boost=oa&hits={hits}&offset={offset}'
+
+# engine dependent config
+paging = True
+number_of_results = 10
+
+# shortcuts for advanced search
+shorcut_dict = {
+ # user-friendly keywords
+ 'format:': 'dcformat:',
+ 'author:': 'dccreator:',
+ 'collection:': 'dccollection:',
+ 'hdate:': 'dchdate:',
+ 'contributor:': 'dccontributor:',
+ 'coverage:': 'dccoverage:',
+ 'date:': 'dcdate:',
+ 'abstract:': 'dcdescription:',
+ 'urls:': 'dcidentifier:',
+ 'language:': 'dclanguage:',
+ 'publisher:': 'dcpublisher:',
+ 'relation:': 'dcrelation:',
+ 'rights:': 'dcrights:',
+ 'source:': 'dcsource:',
+ 'subject:': 'dcsubject:',
+ 'title:': 'dctitle:',
+ 'type:': 'dcdctype:'
+}
+
+
+def request(query, params):
+ # replace shortcuts with API advanced search keywords
+ for key in shorcut_dict.keys():
+ query = re.sub(str(key), str(shorcut_dict[key]), query)
+
+ # basic search
+ offset = (params['pageno'] - 1) * number_of_results
+
+ string_args = dict(query=urlencode({'query': query}),
+ offset=offset,
+ hits=number_of_results)
+
+ params['url'] = base_url.format(**string_args)
+
+ params['headers']['User-Agent'] = searx_useragent()
+ return params
+
+
+def response(resp):
+ results = []
+
+ search_results = etree.XML(resp.text)
+
+ for entry in search_results.xpath('./result/doc'):
+ content = "No description available"
+
+ date = datetime.now() # needed in case no dcdate is available for an item
+ for item in entry:
+ if item.attrib["name"] == "dchdate":
+ harvestDate = item.text
+
+ elif item.attrib["name"] == "dcdate":
+ date = item.text
+
+ elif item.attrib["name"] == "dctitle":
+ title = item.text
+
+ elif item.attrib["name"] == "dclink":
+ url = item.text
+
+ elif item.attrib["name"] == "dcdescription":
+ content = item.text[:300]
+ if len(item.text) > 300:
+ content += "..."
+
+# dates returned by the BASE API are not several formats
+ publishedDate = None
+ for date_format in ['%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%d', '%Y-%m', '%Y']:
+ try:
+ publishedDate = datetime.strptime(date, date_format)
+ break
+ except:
+ pass
+
+ if publishedDate is not None:
+ res_dict = {'url': url,
+ 'title': title,
+ 'publishedDate': publishedDate,
+ 'content': content}
+ else:
+ res_dict = {'url': url,
+ 'title': title,
+ 'content': content}
+
+ results.append(res_dict)
+
+ return results
diff --git a/searx/engines/bing.py b/searx/engines/bing.py
new file mode 100644
index 0000000..052d567
--- /dev/null
+++ b/searx/engines/bing.py
@@ -0,0 +1,101 @@
+"""
+ Bing (Web)
+
+ @website https://www.bing.com
+ @provide-api yes (http://datamarket.azure.com/dataset/bing/search),
+ max. 5000 query/month
+
+ @using-api no (because of query limit)
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content
+
+ @todo publishedDate
+"""
+
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['general']
+paging = True
+language_support = True
+supported_languages_url = 'https://www.bing.com/account/general'
+
+# search-url
+base_url = 'https://www.bing.com/'
+search_string = 'search?{query}&first={offset}'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 10 + 1
+
+ if params['language'] != 'all':
+ lang = params['language'].split('-')[0].upper()
+ else:
+ lang = 'EN'
+
+ query = u'language:{} {}'.format(lang, query.decode('utf-8')).encode('utf-8')
+
+ search_path = search_string.format(
+ query=urlencode({'q': query}),
+ offset=offset)
+
+ params['url'] = base_url + search_path
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ try:
+ results.append({'number_of_results': int(dom.xpath('//span[@class="sb_count"]/text()')[0]
+ .split()[0].replace(',', ''))})
+ except:
+ pass
+
+ # parse results
+ for result in dom.xpath('//div[@class="sa_cc"]'):
+ link = result.xpath('.//h3/a')[0]
+ url = link.attrib.get('href')
+ title = extract_text(link)
+ content = extract_text(result.xpath('.//p'))
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content})
+
+ # parse results again if nothing is found yet
+ for result in dom.xpath('//li[@class="b_algo"]'):
+ link = result.xpath('.//h2/a')[0]
+ url = link.attrib.get('href')
+ title = extract_text(link)
+ content = extract_text(result.xpath('.//p'))
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content})
+
+ # return results
+ return results
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+ supported_languages = []
+ dom = html.fromstring(resp.text)
+ options = dom.xpath('//div[@id="limit-languages"]//input')
+ for option in options:
+ code = option.xpath('./@id')[0].replace('_', '-')
+ if code == 'nb':
+ code = 'no'
+ supported_languages.append(code)
+
+ return supported_languages
diff --git a/searx/engines/bing_images.py b/searx/engines/bing_images.py
new file mode 100644
index 0000000..6300c94
--- /dev/null
+++ b/searx/engines/bing_images.py
@@ -0,0 +1,108 @@
+"""
+ Bing (Images)
+
+ @website https://www.bing.com/images
+ @provide-api yes (http://datamarket.azure.com/dataset/bing/search),
+ max. 5000 query/month
+
+ @using-api no (because of query limit)
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, img_src
+
+ @todo currently there are up to 35 images receive per page,
+ because bing does not parse count=10.
+ limited response to 10 images
+"""
+
+from lxml import html
+from json import loads
+import re
+from searx.engines.bing import _fetch_supported_languages, supported_languages_url
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['images']
+paging = True
+safesearch = True
+time_range_support = True
+
+# search-url
+base_url = 'https://www.bing.com/'
+search_string = 'images/search?{query}&count=10&first={offset}'
+time_range_string = '&qft=+filterui:age-lt{interval}'
+time_range_dict = {'day': '1440',
+ 'week': '10080',
+ 'month': '43200',
+ 'year': '525600'}
+
+# safesearch definitions
+safesearch_types = {2: 'STRICT',
+ 1: 'DEMOTE',
+ 0: 'OFF'}
+
+
+_quote_keys_regex = re.compile('({|,)([a-z][a-z0-9]*):(")', re.I | re.U)
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 10 + 1
+
+ # required for cookie
+ if params['language'] == 'all':
+ language = 'en-US'
+ else:
+ language = params['language']
+
+ search_path = search_string.format(
+ query=urlencode({'q': query}),
+ offset=offset)
+
+ params['cookies']['SRCHHPGUSR'] = \
+ 'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0] +\
+ '&ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
+
+ params['url'] = base_url + search_path
+ if params['time_range'] in time_range_dict:
+ params['url'] += time_range_string.format(interval=time_range_dict[params['time_range']])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath('//div[@id="mmComponent_images_1"]/ul/li/div/div[@class="imgpt"]'):
+ link = result.xpath('./a')[0]
+
+ # TODO find actual title
+ title = link.xpath('.//img/@alt')[0]
+
+ # parse json-data (it is required to add a space, to make it parsable)
+ json_data = loads(_quote_keys_regex.sub(r'\1"\2": \3', link.attrib.get('m')))
+
+ url = json_data.get('purl')
+ img_src = json_data.get('murl')
+
+ thumb_json_data = loads(_quote_keys_regex.sub(r'\1"\2": \3', link.attrib.get('mad')))
+ thumbnail = thumb_json_data.get('turl')
+
+ # append result
+ results.append({'template': 'images.html',
+ 'url': url,
+ 'title': title,
+ 'content': '',
+ 'thumbnail_src': thumbnail,
+ 'img_src': img_src})
+
+ # TODO stop parsing if 10 images are found
+ # if len(results) >= 10:
+ # break
+
+ # return results
+ return results
diff --git a/searx/engines/bing_news.py b/searx/engines/bing_news.py
new file mode 100644
index 0000000..b999b2a
--- /dev/null
+++ b/searx/engines/bing_news.py
@@ -0,0 +1,127 @@
+"""
+ Bing (News)
+
+ @website https://www.bing.com/news
+ @provide-api yes (http://datamarket.azure.com/dataset/bing/search),
+ max. 5000 query/month
+
+ @using-api no (because of query limit)
+ @results RSS (using search portal)
+ @stable yes (except perhaps for the images)
+ @parse url, title, content, publishedDate, thumbnail
+"""
+
+from datetime import datetime
+from dateutil import parser
+from lxml import etree
+from searx.utils import list_get
+from searx.engines.bing import _fetch_supported_languages, supported_languages_url
+from searx.url_utils import urlencode, urlparse, parse_qsl
+
+# engine dependent config
+categories = ['news']
+paging = True
+language_support = True
+time_range_support = True
+
+# search-url
+base_url = 'https://www.bing.com/'
+search_string = 'news/search?{query}&first={offset}&format=RSS'
+search_string_with_time = 'news/search?{query}&first={offset}&qft=interval%3d"{interval}"&format=RSS'
+time_range_dict = {'day': '7',
+ 'week': '8',
+ 'month': '9'}
+
+
+# remove click
+def url_cleanup(url_string):
+ parsed_url = urlparse(url_string)
+ if parsed_url.netloc == 'www.bing.com' and parsed_url.path == '/news/apiclick.aspx':
+ query = dict(parse_qsl(parsed_url.query))
+ return query.get('url', None)
+ return url_string
+
+
+# replace the http://*bing4.com/th?id=... by https://www.bing.com/th?id=...
+def image_url_cleanup(url_string):
+ parsed_url = urlparse(url_string)
+ if parsed_url.netloc.endswith('bing4.com') and parsed_url.path == '/th':
+ query = dict(parse_qsl(parsed_url.query))
+ return "https://www.bing.com/th?id=" + query.get('id')
+ return url_string
+
+
+def _get_url(query, language, offset, time_range):
+ if time_range in time_range_dict:
+ search_path = search_string_with_time.format(
+ query=urlencode({'q': query, 'setmkt': language}),
+ offset=offset,
+ interval=time_range_dict[time_range])
+ else:
+ search_path = search_string.format(
+ query=urlencode({'q': query, 'setmkt': language}),
+ offset=offset)
+ return base_url + search_path
+
+
+# do search-request
+def request(query, params):
+ if params['time_range'] and params['time_range'] not in time_range_dict:
+ return params
+
+ offset = (params['pageno'] - 1) * 10 + 1
+
+ if params['language'] == 'all':
+ language = 'en-US'
+ else:
+ language = params['language']
+
+ params['url'] = _get_url(query, language, offset, params['time_range'])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ rss = etree.fromstring(resp.content)
+
+ ns = rss.nsmap
+
+ # parse results
+ for item in rss.xpath('./channel/item'):
+ # url / title / content
+ url = url_cleanup(item.xpath('./link/text()')[0])
+ title = list_get(item.xpath('./title/text()'), 0, url)
+ content = list_get(item.xpath('./description/text()'), 0, '')
+
+ # publishedDate
+ publishedDate = list_get(item.xpath('./pubDate/text()'), 0)
+ try:
+ publishedDate = parser.parse(publishedDate, dayfirst=False)
+ except TypeError:
+ publishedDate = datetime.now()
+ except ValueError:
+ publishedDate = datetime.now()
+
+ # thumbnail
+ thumbnail = list_get(item.xpath('./News:Image/text()', namespaces=ns), 0)
+ if thumbnail is not None:
+ thumbnail = image_url_cleanup(thumbnail)
+
+ # append result
+ if thumbnail is not None:
+ results.append({'url': url,
+ 'title': title,
+ 'publishedDate': publishedDate,
+ 'content': content,
+ 'img_src': thumbnail})
+ else:
+ results.append({'url': url,
+ 'title': title,
+ 'publishedDate': publishedDate,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/blekko_images.py b/searx/engines/blekko_images.py
new file mode 100644
index 0000000..f716456
--- /dev/null
+++ b/searx/engines/blekko_images.py
@@ -0,0 +1,70 @@
+"""
+ Blekko (Images)
+
+ @website https://blekko.com
+ @provide-api yes (inofficial)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, img_src
+"""
+
+from json import loads
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['images']
+paging = True
+safesearch = True
+
+# search-url
+base_url = 'https://blekko.com'
+search_url = '/api/images?{query}&c={c}'
+
+# safesearch definitions
+safesearch_types = {2: '1',
+ 1: '',
+ 0: '0'}
+
+
+# do search-request
+def request(query, params):
+ c = (params['pageno'] - 1) * 48
+
+ params['url'] = base_url +\
+ search_url.format(query=urlencode({'q': query}),
+ c=c)
+
+ if params['pageno'] != 1:
+ params['url'] += '&page={pageno}'.format(pageno=(params['pageno'] - 1))
+
+ # let Blekko know we wan't have profiling
+ params['cookies']['tag_lesslogging'] = '1'
+
+ # parse safesearch argument
+ params['cookies']['safesearch'] = safesearch_types.get(params['safesearch'], '')
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_results = loads(resp.text)
+
+ # return empty array if there are no results
+ if not search_results:
+ return []
+
+ for result in search_results:
+ # append result
+ results.append({'url': result['page_url'],
+ 'title': result['title'],
+ 'content': '',
+ 'img_src': result['url'],
+ 'template': 'images.html'})
+
+ # return results
+ return results
diff --git a/searx/engines/btdigg.py b/searx/engines/btdigg.py
new file mode 100644
index 0000000..4043867
--- /dev/null
+++ b/searx/engines/btdigg.py
@@ -0,0 +1,92 @@
+"""
+ BTDigg (Videos, Music, Files)
+
+ @website https://btdigg.org
+ @provide-api yes (on demand)
+
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content, seed, leech, magnetlink
+"""
+
+from lxml import html
+from operator import itemgetter
+from searx.engines.xpath import extract_text
+from searx.url_utils import quote, urljoin
+from searx.utils import get_torrent_size
+
+# engine dependent config
+categories = ['videos', 'music', 'files']
+paging = True
+
+# search-url
+url = 'https://btdigg.org'
+search_url = url + '/search?q={search_term}&p={pageno}'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(search_term=quote(query),
+ pageno=params['pageno'] - 1)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ search_res = dom.xpath('//div[@id="search_res"]/table/tr')
+
+ # return empty array if nothing is found
+ if not search_res:
+ return []
+
+ # parse results
+ for result in search_res:
+ link = result.xpath('.//td[@class="torrent_name"]//a')[0]
+ href = urljoin(url, link.attrib.get('href'))
+ title = extract_text(link)
+ content = extract_text(result.xpath('.//pre[@class="snippet"]')[0])
+ content = "<br />".join(content.split("\n"))
+
+ filesize = result.xpath('.//span[@class="attr_val"]/text()')[0].split()[0]
+ filesize_multiplier = result.xpath('.//span[@class="attr_val"]/text()')[0].split()[1]
+ files = result.xpath('.//span[@class="attr_val"]/text()')[1]
+ seed = result.xpath('.//span[@class="attr_val"]/text()')[2]
+
+ # convert seed to int if possible
+ if seed.isdigit():
+ seed = int(seed)
+ else:
+ seed = 0
+
+ leech = 0
+
+ # convert filesize to byte if possible
+ filesize = get_torrent_size(filesize, filesize_multiplier)
+
+ # convert files to int if possible
+ if files.isdigit():
+ files = int(files)
+ else:
+ files = None
+
+ magnetlink = result.xpath('.//td[@class="ttth"]//a')[0].attrib['href']
+
+ # append result
+ results.append({'url': href,
+ 'title': title,
+ 'content': content,
+ 'seed': seed,
+ 'leech': leech,
+ 'filesize': filesize,
+ 'files': files,
+ 'magnetlink': magnetlink,
+ 'template': 'torrent.html'})
+
+ # return results sorted by seeder
+ return sorted(results, key=itemgetter('seed'), reverse=True)
diff --git a/searx/engines/currency_convert.py b/searx/engines/currency_convert.py
new file mode 100644
index 0000000..1218d48
--- /dev/null
+++ b/searx/engines/currency_convert.py
@@ -0,0 +1,105 @@
+import json
+import re
+import os
+import sys
+import unicodedata
+
+from datetime import datetime
+
+if sys.version_info[0] == 3:
+ unicode = str
+
+categories = []
+url = 'https://download.finance.yahoo.com/d/quotes.csv?e=.csv&f=sl1d1t1&s={query}=X'
+weight = 100
+
+parser_re = re.compile(b'.*?(\\d+(?:\\.\\d+)?) ([^.0-9]+) (?:in|to) ([^.0-9]+)', re.I)
+
+db = 1
+
+
+def normalize_name(name):
+ name = name.decode('utf-8').lower().replace('-', ' ').rstrip('s')
+ name = re.sub(' +', ' ', name)
+ return unicodedata.normalize('NFKD', name).lower()
+
+
+def name_to_iso4217(name):
+ global db
+
+ name = normalize_name(name)
+ currencies = db['names'].get(name, [name])
+ return currencies[0]
+
+
+def iso4217_to_name(iso4217, language):
+ global db
+
+ return db['iso4217'].get(iso4217, {}).get(language, iso4217)
+
+
+def request(query, params):
+ m = parser_re.match(query)
+ if not m:
+ # wrong query
+ return params
+
+ ammount, from_currency, to_currency = m.groups()
+ ammount = float(ammount)
+ from_currency = name_to_iso4217(from_currency.strip())
+ to_currency = name_to_iso4217(to_currency.strip())
+
+ q = (from_currency + to_currency).upper()
+
+ params['url'] = url.format(query=q)
+ params['ammount'] = ammount
+ params['from'] = from_currency
+ params['to'] = to_currency
+ params['from_name'] = iso4217_to_name(from_currency, 'en')
+ params['to_name'] = iso4217_to_name(to_currency, 'en')
+
+ return params
+
+
+def response(resp):
+ results = []
+ try:
+ _, conversion_rate, _ = resp.text.split(',', 2)
+ conversion_rate = float(conversion_rate)
+ except:
+ return results
+
+ answer = '{0} {1} = {2} {3}, 1 {1} ({5}) = {4} {3} ({6})'.format(
+ resp.search_params['ammount'],
+ resp.search_params['from'],
+ resp.search_params['ammount'] * conversion_rate,
+ resp.search_params['to'],
+ conversion_rate,
+ resp.search_params['from_name'],
+ resp.search_params['to_name'],
+ )
+
+ now_date = datetime.now().strftime('%Y%m%d')
+ url = 'https://finance.yahoo.com/currency/converter-results/{0}/{1}-{2}-to-{3}.html' # noqa
+ url = url.format(
+ now_date,
+ resp.search_params['ammount'],
+ resp.search_params['from'].lower(),
+ resp.search_params['to'].lower()
+ )
+
+ results.append({'answer': answer, 'url': url})
+
+ return results
+
+
+def load():
+ global db
+
+ current_dir = os.path.dirname(os.path.realpath(__file__))
+ json_data = open(current_dir + "/../data/currencies.json").read()
+
+ db = json.loads(json_data)
+
+
+load()
diff --git a/searx/engines/dailymotion.py b/searx/engines/dailymotion.py
new file mode 100644
index 0000000..fad7e59
--- /dev/null
+++ b/searx/engines/dailymotion.py
@@ -0,0 +1,97 @@
+"""
+ Dailymotion (Videos)
+
+ @website https://www.dailymotion.com
+ @provide-api yes (http://www.dailymotion.com/developer)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, thumbnail, publishedDate, embedded
+
+ @todo set content-parameter with correct data
+"""
+
+from json import loads
+from datetime import datetime
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['videos']
+paging = True
+language_support = True
+
+# search-url
+# see http://www.dailymotion.com/doc/api/obj-video.html
+search_url = 'https://api.dailymotion.com/videos?fields=created_time,title,description,duration,url,thumbnail_360_url,id&sort=relevance&limit=5&page={pageno}&{query}' # noqa
+embedded_url = '<iframe frameborder="0" width="540" height="304" ' +\
+ 'data-src="//www.dailymotion.com/embed/video/{videoid}" allowfullscreen></iframe>'
+
+supported_languages_url = 'https://api.dailymotion.com/languages'
+
+
+# do search-request
+def request(query, params):
+ if params['language'] == 'all':
+ locale = 'en-US'
+ else:
+ locale = params['language']
+
+ params['url'] = search_url.format(
+ query=urlencode({'search': query, 'localization': locale}),
+ pageno=params['pageno'])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_res = loads(resp.text)
+
+ # return empty array if there are no results
+ if 'list' not in search_res:
+ return []
+
+ # parse results
+ for res in search_res['list']:
+ title = res['title']
+ url = res['url']
+ content = res['description']
+ thumbnail = res['thumbnail_360_url']
+ publishedDate = datetime.fromtimestamp(res['created_time'], None)
+ embedded = embedded_url.format(videoid=res['id'])
+
+ # http to https
+ thumbnail = thumbnail.replace("http://", "https://")
+
+ results.append({'template': 'videos.html',
+ 'url': url,
+ 'title': title,
+ 'content': content,
+ 'publishedDate': publishedDate,
+ 'embedded': embedded,
+ 'thumbnail': thumbnail})
+
+ # return results
+ return results
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+ supported_languages = {}
+
+ response_json = loads(resp.text)
+
+ for language in response_json['list']:
+ supported_languages[language['code']] = {}
+
+ name = language['native_name']
+ if name:
+ supported_languages[language['code']]['name'] = name
+ english_name = language['name']
+ if english_name:
+ supported_languages[language['code']]['english_name'] = english_name
+
+ return supported_languages
diff --git a/searx/engines/deezer.py b/searx/engines/deezer.py
new file mode 100644
index 0000000..af63478
--- /dev/null
+++ b/searx/engines/deezer.py
@@ -0,0 +1,67 @@
+"""
+ Deezer (Music)
+
+ @website https://deezer.com
+ @provide-api yes (http://developers.deezer.com/api/)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content, embedded
+"""
+
+from json import loads
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['music']
+paging = True
+
+# search-url
+url = 'https://api.deezer.com/'
+search_url = url + 'search?{query}&index={offset}'
+
+embedded_url = '<iframe scrolling="no" frameborder="0" allowTransparency="true" ' +\
+ 'data-src="https://www.deezer.com/plugins/player?type=tracks&id={audioid}" ' +\
+ 'width="540" height="80"></iframe>'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 25
+
+ params['url'] = search_url.format(query=urlencode({'q': query}), offset=offset)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_res = loads(resp.text)
+
+ # parse results
+ for result in search_res.get('data', []):
+ if result['type'] == 'track':
+ title = result['title']
+ url = result['link']
+
+ if url.startswith('http://'):
+ url = 'https' + url[4:]
+
+ content = u'{} - {} - {}'.format(
+ result['artist']['name'],
+ result['album']['title'],
+ result['title'])
+
+ embedded = embedded_url.format(audioid=result['id'])
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'embedded': embedded,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/deviantart.py b/searx/engines/deviantart.py
new file mode 100644
index 0000000..bb85c6d
--- /dev/null
+++ b/searx/engines/deviantart.py
@@ -0,0 +1,84 @@
+"""
+ Deviantart (Images)
+
+ @website https://www.deviantart.com/
+ @provide-api yes (https://www.deviantart.com/developers/) (RSS)
+
+ @using-api no (TODO, rewrite to api)
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title, thumbnail_src, img_src
+
+ @todo rewrite to api
+"""
+
+from lxml import html
+import re
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['images']
+paging = True
+time_range_support = True
+
+# search-url
+base_url = 'https://www.deviantart.com/'
+search_url = base_url + 'browse/all/?offset={offset}&{query}'
+time_range_url = '&order={range}'
+
+time_range_dict = {'day': 11,
+ 'week': 14,
+ 'month': 15}
+
+
+# do search-request
+def request(query, params):
+ if params['time_range'] and params['time_range'] not in time_range_dict:
+ return params
+
+ offset = (params['pageno'] - 1) * 24
+
+ params['url'] = search_url.format(offset=offset,
+ query=urlencode({'q': query}))
+ if params['time_range'] in time_range_dict:
+ params['url'] += time_range_url.format(range=time_range_dict[params['time_range']])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ # return empty array if a redirection code is returned
+ if resp.status_code == 302:
+ return []
+
+ dom = html.fromstring(resp.text)
+
+ regex = re.compile(r'\/200H\/')
+
+ # parse results
+ for result in dom.xpath('.//span[@class="thumb wide"]'):
+ link = result.xpath('.//a[@class="torpedo-thumb-link"]')[0]
+ url = link.attrib.get('href')
+ title = extract_text(result.xpath('.//span[@class="title"]'))
+ thumbnail_src = link.xpath('.//img')[0].attrib.get('src')
+ img_src = regex.sub('/', thumbnail_src)
+
+ # http to https, remove domain sharding
+ thumbnail_src = re.sub(r"https?://(th|fc)\d+.", "https://th01.", thumbnail_src)
+ thumbnail_src = re.sub(r"http://", "https://", thumbnail_src)
+
+ url = re.sub(r"http://(.*)\.deviantart\.com/", "https://\\1.deviantart.com/", url)
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'img_src': img_src,
+ 'thumbnail_src': thumbnail_src,
+ 'template': 'images.html'})
+
+ # return results
+ return results
diff --git a/searx/engines/dictzone.py b/searx/engines/dictzone.py
new file mode 100644
index 0000000..7c34786
--- /dev/null
+++ b/searx/engines/dictzone.py
@@ -0,0 +1,68 @@
+"""
+ Dictzone
+
+ @website https://dictzone.com/
+ @provide-api no
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content
+"""
+
+import re
+from lxml import html
+from searx.utils import is_valid_lang
+from searx.url_utils import urljoin
+
+categories = ['general']
+url = u'http://dictzone.com/{from_lang}-{to_lang}-dictionary/{query}'
+weight = 100
+
+parser_re = re.compile(b'.*?([a-z]+)-([a-z]+) ([^ ]+)$', re.I)
+results_xpath = './/table[@id="r"]/tr'
+
+
+def request(query, params):
+ m = parser_re.match(query)
+ if not m:
+ return params
+
+ from_lang, to_lang, query = m.groups()
+
+ from_lang = is_valid_lang(from_lang)
+ to_lang = is_valid_lang(to_lang)
+
+ if not from_lang or not to_lang:
+ return params
+
+ params['url'] = url.format(from_lang=from_lang[2],
+ to_lang=to_lang[2],
+ query=query)
+
+ return params
+
+
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ for k, result in enumerate(dom.xpath(results_xpath)[1:]):
+ try:
+ from_result, to_results_raw = result.xpath('./td')
+ except:
+ continue
+
+ to_results = []
+ for to_result in to_results_raw.xpath('./p/a'):
+ t = to_result.text_content()
+ if t.strip():
+ to_results.append(to_result.text_content())
+
+ results.append({
+ 'url': urljoin(resp.url, '?%d' % k),
+ 'title': from_result.text_content(),
+ 'content': '; '.join(to_results)
+ })
+
+ return results
diff --git a/searx/engines/digbt.py b/searx/engines/digbt.py
new file mode 100644
index 0000000..ff2f945
--- /dev/null
+++ b/searx/engines/digbt.py
@@ -0,0 +1,62 @@
+"""
+ DigBT (Videos, Music, Files)
+
+ @website https://digbt.org
+ @provide-api no
+
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content, magnetlink
+"""
+
+from sys import version_info
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.utils import get_torrent_size
+from searx.url_utils import urljoin
+
+if version_info[0] == 3:
+ unicode = str
+
+categories = ['videos', 'music', 'files']
+paging = True
+
+URL = 'https://digbt.org'
+SEARCH_URL = URL + '/search/{query}-time-{pageno}'
+FILESIZE = 3
+FILESIZE_MULTIPLIER = 4
+
+
+def request(query, params):
+ params['url'] = SEARCH_URL.format(query=query, pageno=params['pageno'])
+
+ return params
+
+
+def response(resp):
+ dom = html.fromstring(resp.text)
+ search_res = dom.xpath('.//td[@class="x-item"]')
+
+ if not search_res:
+ return list()
+
+ results = list()
+ for result in search_res:
+ url = urljoin(URL, result.xpath('.//a[@title]/@href')[0])
+ title = extract_text(result.xpath('.//a[@title]'))
+ content = extract_text(result.xpath('.//div[@class="files"]'))
+ files_data = extract_text(result.xpath('.//div[@class="tail"]')).split()
+ filesize = get_torrent_size(files_data[FILESIZE], files_data[FILESIZE_MULTIPLIER])
+ magnetlink = result.xpath('.//div[@class="tail"]//a[@class="title"]/@href')[0]
+
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'filesize': filesize,
+ 'magnetlink': magnetlink,
+ 'seed': 'N/A',
+ 'leech': 'N/A',
+ 'template': 'torrent.html'})
+
+ return results
diff --git a/searx/engines/digg.py b/searx/engines/digg.py
new file mode 100644
index 0000000..606747a
--- /dev/null
+++ b/searx/engines/digg.py
@@ -0,0 +1,74 @@
+"""
+ Digg (News, Social media)
+
+ @website https://digg.com/
+ @provide-api no
+
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content, publishedDate, thumbnail
+"""
+
+from dateutil import parser
+from json import loads
+from lxml import html
+from searx.url_utils import quote_plus
+
+# engine dependent config
+categories = ['news', 'social media']
+paging = True
+
+# search-url
+base_url = 'https://digg.com/'
+search_url = base_url + 'api/search/{query}.json?position={position}&format=html'
+
+# specific xpath variables
+results_xpath = '//article'
+link_xpath = './/small[@class="time"]//a'
+title_xpath = './/h2//a//text()'
+content_xpath = './/p//text()'
+pubdate_xpath = './/time'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 10
+ params['url'] = search_url.format(position=offset,
+ query=quote_plus(query))
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_result = loads(resp.text)
+
+ if 'html' not in search_result or search_result['html'] == '':
+ return results
+
+ dom = html.fromstring(search_result['html'])
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ url = result.attrib.get('data-contenturl')
+ thumbnail = result.xpath('.//img')[0].attrib.get('src')
+ title = ''.join(result.xpath(title_xpath))
+ content = ''.join(result.xpath(content_xpath))
+ pubdate = result.xpath(pubdate_xpath)[0].attrib.get('datetime')
+ publishedDate = parser.parse(pubdate)
+
+ # http to https
+ thumbnail = thumbnail.replace("http://static.digg.com", "https://static.digg.com")
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'template': 'videos.html',
+ 'publishedDate': publishedDate,
+ 'thumbnail': thumbnail})
+
+ # return results
+ return results
diff --git a/searx/engines/doku.py b/searx/engines/doku.py
new file mode 100644
index 0000000..a391be4
--- /dev/null
+++ b/searx/engines/doku.py
@@ -0,0 +1,84 @@
+# Doku Wiki
+#
+# @website https://www.dokuwiki.org/
+# @provide-api yes
+# (https://www.dokuwiki.org/devel:xmlrpc)
+#
+# @using-api no
+# @results HTML
+# @stable yes
+# @parse (general) url, title, content
+
+from lxml.html import fromstring
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['general'] # TODO , 'images', 'music', 'videos', 'files'
+paging = False
+language_support = False
+number_of_results = 5
+
+# search-url
+# Doku is OpenSearch compatible
+base_url = 'http://localhost:8090'
+search_url = '/?do=search'\
+ '&{query}'
+# TODO '&startRecord={offset}'\
+# TODO '&maximumRecords={limit}'\
+
+
+# do search-request
+def request(query, params):
+
+ params['url'] = base_url +\
+ search_url.format(query=urlencode({'id': query}))
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ doc = fromstring(resp.text)
+
+ # parse results
+ # Quickhits
+ for r in doc.xpath('//div[@class="search_quickresult"]/ul/li'):
+ try:
+ res_url = r.xpath('.//a[@class="wikilink1"]/@href')[-1]
+ except:
+ continue
+
+ if not res_url:
+ continue
+
+ title = extract_text(r.xpath('.//a[@class="wikilink1"]/@title'))
+
+ # append result
+ results.append({'title': title,
+ 'content': "",
+ 'url': base_url + res_url})
+
+ # Search results
+ for r in doc.xpath('//dl[@class="search_results"]/*'):
+ try:
+ if r.tag == "dt":
+ res_url = r.xpath('.//a[@class="wikilink1"]/@href')[-1]
+ title = extract_text(r.xpath('.//a[@class="wikilink1"]/@title'))
+ elif r.tag == "dd":
+ content = extract_text(r.xpath('.'))
+
+ # append result
+ results.append({'title': title,
+ 'content': content,
+ 'url': base_url + res_url})
+ except:
+ continue
+
+ if not res_url:
+ continue
+
+ # return results
+ return results
diff --git a/searx/engines/duckduckgo.py b/searx/engines/duckduckgo.py
new file mode 100644
index 0000000..8b6411c
--- /dev/null
+++ b/searx/engines/duckduckgo.py
@@ -0,0 +1,137 @@
+"""
+ DuckDuckGo (Web)
+
+ @website https://duckduckgo.com/
+ @provide-api yes (https://duckduckgo.com/api),
+ but not all results from search-site
+
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content
+
+ @todo rewrite to api
+"""
+
+from lxml.html import fromstring
+from requests import get
+from json import loads
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['general']
+paging = True
+language_support = True
+supported_languages_url = 'https://duckduckgo.com/d2030.js'
+time_range_support = True
+
+# search-url
+url = 'https://duckduckgo.com/html?{query}&s={offset}&api=/d.js&o=json&dc={dc_param}'
+time_range_url = '&df={range}'
+
+time_range_dict = {'day': 'd',
+ 'week': 'w',
+ 'month': 'm'}
+
+# specific xpath variables
+result_xpath = '//div[@class="result results_links results_links_deep web-result "]' # noqa
+url_xpath = './/a[@class="result__a"]/@href'
+title_xpath = './/a[@class="result__a"]'
+content_xpath = './/a[@class="result__snippet"]'
+
+
+# match query's language to a region code that duckduckgo will accept
+def get_region_code(lang):
+ # custom fixes for languages
+ if lang == 'all':
+ region_code = None
+ elif lang[:2] == 'ja':
+ region_code = 'jp-jp'
+ elif lang[:2] == 'sl':
+ region_code = 'sl-sl'
+ elif lang == 'zh-TW':
+ region_code = 'tw-tzh'
+ elif lang == 'zh-HK':
+ region_code = 'hk-tzh'
+ elif lang[-2:] == 'SA':
+ region_code = 'xa-' + lang.split('-')[0]
+ elif lang[-2:] == 'GB':
+ region_code = 'uk-' + lang.split('-')[0]
+ else:
+ region_code = lang.split('-')
+ if len(region_code) == 2:
+ # country code goes first
+ region_code = region_code[1].lower() + '-' + region_code[0].lower()
+ else:
+ # tries to get a country code from language
+ region_code = region_code[0].lower()
+ for lc in supported_languages:
+ lc = lc.split('-')
+ if region_code == lc[0]:
+ region_code = lc[1].lower() + '-' + lc[0].lower()
+ break
+ return region_code
+
+
+# do search-request
+def request(query, params):
+ if params['time_range'] and params['time_range'] not in time_range_dict:
+ return params
+
+ offset = (params['pageno'] - 1) * 30
+
+ region_code = get_region_code(params['language'])
+ if region_code:
+ params['url'] = url.format(
+ query=urlencode({'q': query, 'kl': region_code}), offset=offset, dc_param=offset)
+ else:
+ params['url'] = url.format(
+ query=urlencode({'q': query}), offset=offset, dc_param=offset)
+
+ if params['time_range'] in time_range_dict:
+ params['url'] += time_range_url.format(range=time_range_dict[params['time_range']])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ doc = fromstring(resp.text)
+
+ # parse results
+ for r in doc.xpath(result_xpath):
+ try:
+ res_url = r.xpath(url_xpath)[-1]
+ except:
+ continue
+
+ if not res_url:
+ continue
+
+ title = extract_text(r.xpath(title_xpath))
+ content = extract_text(r.xpath(content_xpath))
+
+ # append result
+ results.append({'title': title,
+ 'content': content,
+ 'url': res_url})
+
+ # return results
+ return results
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+
+ # response is a js file with regions as an embedded object
+ response_page = resp.text
+ response_page = response_page[response_page.find('regions:{') + 8:]
+ response_page = response_page[:response_page.find('}') + 1]
+
+ regions_json = loads(response_page)
+ supported_languages = map((lambda x: x[3:] + '-' + x[:2].upper()), regions_json.keys())
+
+ return supported_languages
diff --git a/searx/engines/duckduckgo_definitions.py b/searx/engines/duckduckgo_definitions.py
new file mode 100644
index 0000000..21c6a65
--- /dev/null
+++ b/searx/engines/duckduckgo_definitions.py
@@ -0,0 +1,157 @@
+import json
+from lxml import html
+from re import compile
+from searx.engines.xpath import extract_text
+from searx.engines.duckduckgo import _fetch_supported_languages, supported_languages_url
+from searx.url_utils import urlencode
+from searx.utils import html_to_text
+
+url = 'https://api.duckduckgo.com/'\
+ + '?{query}&format=json&pretty=0&no_redirect=1&d=1'
+
+http_regex = compile(r'^http:')
+
+
+def result_to_text(url, text, htmlResult):
+ # TODO : remove result ending with "Meaning" or "Category"
+ dom = html.fromstring(htmlResult)
+ a = dom.xpath('//a')
+ if len(a) >= 1:
+ return extract_text(a[0])
+ else:
+ return text
+
+
+def request(query, params):
+ params['url'] = url.format(query=urlencode({'q': query}))
+ params['headers']['Accept-Language'] = params['language'].split('-')[0]
+ return params
+
+
+def response(resp):
+ results = []
+
+ search_res = json.loads(resp.text)
+
+ content = ''
+ heading = search_res.get('Heading', '')
+ attributes = []
+ urls = []
+ infobox_id = None
+ relatedTopics = []
+
+ # add answer if there is one
+ answer = search_res.get('Answer', '')
+ if answer != '':
+ results.append({'answer': html_to_text(answer)})
+
+ # add infobox
+ if 'Definition' in search_res:
+ content = content + search_res.get('Definition', '')
+
+ if 'Abstract' in search_res:
+ content = content + search_res.get('Abstract', '')
+
+ # image
+ image = search_res.get('Image', '')
+ image = None if image == '' else image
+
+ # attributes
+ if 'Infobox' in search_res:
+ infobox = search_res.get('Infobox', None)
+ if 'content' in infobox:
+ for info in infobox.get('content'):
+ attributes.append({'label': info.get('label'),
+ 'value': info.get('value')})
+
+ # urls
+ for ddg_result in search_res.get('Results', []):
+ if 'FirstURL' in ddg_result:
+ firstURL = ddg_result.get('FirstURL', '')
+ text = ddg_result.get('Text', '')
+ urls.append({'title': text, 'url': firstURL})
+ results.append({'title': heading, 'url': firstURL})
+
+ # related topics
+ for ddg_result in search_res.get('RelatedTopics', []):
+ if 'FirstURL' in ddg_result:
+ suggestion = result_to_text(ddg_result.get('FirstURL', None),
+ ddg_result.get('Text', None),
+ ddg_result.get('Result', None))
+ if suggestion != heading:
+ results.append({'suggestion': suggestion})
+ elif 'Topics' in ddg_result:
+ suggestions = []
+ relatedTopics.append({'name': ddg_result.get('Name', ''),
+ 'suggestions': suggestions})
+ for topic_result in ddg_result.get('Topics', []):
+ suggestion = result_to_text(topic_result.get('FirstURL', None),
+ topic_result.get('Text', None),
+ topic_result.get('Result', None))
+ if suggestion != heading:
+ suggestions.append(suggestion)
+
+ # abstract
+ abstractURL = search_res.get('AbstractURL', '')
+ if abstractURL != '':
+ # add as result ? problem always in english
+ infobox_id = abstractURL
+ urls.append({'title': search_res.get('AbstractSource'),
+ 'url': abstractURL})
+
+ # definition
+ definitionURL = search_res.get('DefinitionURL', '')
+ if definitionURL != '':
+ # add as result ? as answer ? problem always in english
+ infobox_id = definitionURL
+ urls.append({'title': search_res.get('DefinitionSource'),
+ 'url': definitionURL})
+
+ # to merge with wikidata's infobox
+ if infobox_id:
+ infobox_id = http_regex.sub('https:', infobox_id)
+
+ # entity
+ entity = search_res.get('Entity', None)
+ # TODO continent / country / department / location / waterfall /
+ # mountain range :
+ # link to map search, get weather, near by locations
+ # TODO musician : link to music search
+ # TODO concert tour : ??
+ # TODO film / actor / television / media franchise :
+ # links to IMDB / rottentomatoes (or scrap result)
+ # TODO music : link tu musicbrainz / last.fm
+ # TODO book : ??
+ # TODO artist / playwright : ??
+ # TODO compagny : ??
+ # TODO software / os : ??
+ # TODO software engineer : ??
+ # TODO prepared food : ??
+ # TODO website : ??
+ # TODO performing art : ??
+ # TODO prepared food : ??
+ # TODO programming language : ??
+ # TODO file format : ??
+
+ if len(heading) > 0:
+ # TODO get infobox.meta.value where .label='article_title'
+ if image is None and len(attributes) == 0 and len(urls) == 1 and\
+ len(relatedTopics) == 0 and len(content) == 0:
+ results.append({
+ 'url': urls[0]['url'],
+ 'title': heading,
+ 'content': content
+ })
+ else:
+ results.append({
+ 'infobox': heading,
+ 'id': infobox_id,
+ 'entity': entity,
+ 'content': content,
+ 'img_src': image,
+ 'attributes': attributes,
+ 'urls': urls,
+ 'relatedTopics': relatedTopics
+ })
+
+ return results
diff --git a/searx/engines/duckduckgo_images.py b/searx/engines/duckduckgo_images.py
new file mode 100644
index 0000000..f355523
--- /dev/null
+++ b/searx/engines/duckduckgo_images.py
@@ -0,0 +1,91 @@
+"""
+ DuckDuckGo (Images)
+
+ @website https://duckduckgo.com/
+ @provide-api yes (https://duckduckgo.com/api),
+ but images are not supported
+
+ @using-api no
+ @results JSON (site requires js to get images)
+ @stable no (JSON can change)
+ @parse url, title, img_src
+
+ @todo avoid extra request
+"""
+
+from requests import get
+from json import loads
+from searx.engines.xpath import extract_text
+from searx.engines.duckduckgo import _fetch_supported_languages, supported_languages_url, get_region_code
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['images']
+paging = True
+language_support = True
+safesearch = True
+
+# search-url
+images_url = 'https://duckduckgo.com/i.js?{query}&s={offset}&p={safesearch}&o=json&vqd={vqd}'
+site_url = 'https://duckduckgo.com/?{query}&iar=images&iax=1&ia=images'
+
+
+# run query in site to get vqd number needed for requesting images
+# TODO: find a way to get this number without an extra request (is it a hash of the query?)
+def get_vqd(query):
+ res = get(site_url.format(query=urlencode({'q': query})))
+ content = res.text
+ vqd = content[content.find('vqd=\'') + 5:]
+ vqd = vqd[:vqd.find('\'')]
+ return vqd
+
+
+# do search-request
+def request(query, params):
+ # to avoid running actual external requests when testing
+ if 'is_test' not in params:
+ vqd = get_vqd(query)
+ else:
+ vqd = '12345'
+
+ offset = (params['pageno'] - 1) * 50
+
+ safesearch = params['safesearch'] - 1
+
+ region_code = get_region_code(params['language'])
+ if region_code:
+ params['url'] = images_url.format(
+ query=urlencode({'q': query, 'l': region_code}), offset=offset, safesearch=safesearch, vqd=vqd)
+ else:
+ params['url'] = images_url.format(
+ query=urlencode({'q': query}), offset=offset, safesearch=safesearch, vqd=vqd)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ content = resp.text
+ try:
+ res_json = loads(content)
+ except:
+ return []
+
+ # parse results
+ for result in res_json['results']:
+ title = result['title']
+ url = result['url']
+ thumbnail = result['thumbnail']
+ image = result['image']
+
+ # append result
+ results.append({'template': 'images.html',
+ 'title': title,
+ 'content': '',
+ 'thumbnail_src': thumbnail,
+ 'img_src': image,
+ 'url': url})
+
+ return results
diff --git a/searx/engines/dummy.py b/searx/engines/dummy.py
new file mode 100644
index 0000000..50b56ef
--- /dev/null
+++ b/searx/engines/dummy.py
@@ -0,0 +1,16 @@
+"""
+ Dummy
+
+ @results empty array
+ @stable yes
+"""
+
+
+# do search-request
+def request(query, params):
+ return params
+
+
+# get response from search-request
+def response(resp):
+ return []
diff --git a/searx/engines/faroo.py b/searx/engines/faroo.py
new file mode 100644
index 0000000..e24d1b7
--- /dev/null
+++ b/searx/engines/faroo.py
@@ -0,0 +1,116 @@
+"""
+ Faroo (Web, News)
+
+ @website http://www.faroo.com
+ @provide-api yes (http://www.faroo.com/hp/api/api.html), require API-key
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content, publishedDate, img_src
+"""
+
+from json import loads
+import datetime
+from searx.utils import searx_useragent
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['general', 'news']
+paging = True
+language_support = True
+number_of_results = 10
+api_key = None
+
+# search-url
+url = 'http://www.faroo.com/'
+search_url = url + 'api?{query}'\
+ '&start={offset}'\
+ '&length={number_of_results}'\
+ '&l={language}'\
+ '&src={categorie}'\
+ '&i=false'\
+ '&f=json'\
+ '&key={api_key}' # noqa
+
+search_category = {'general': 'web',
+ 'news': 'news'}
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * number_of_results + 1
+ categorie = search_category.get(params['category'], 'web')
+
+ if params['language'] == 'all':
+ language = 'en'
+ else:
+ language = params['language'].split('_')[0]
+
+ # if language is not supported, put it in english
+ if language != 'en' and\
+ language != 'de' and\
+ language != 'zh':
+ language = 'en'
+
+ params['url'] = search_url.format(offset=offset,
+ number_of_results=number_of_results,
+ query=urlencode({'q': query}),
+ language=language,
+ categorie=categorie,
+ api_key=api_key)
+
+ # using searx User-Agent
+ params['headers']['User-Agent'] = searx_useragent()
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ # HTTP-Code 401: api-key is not valide
+ if resp.status_code == 401:
+ raise Exception("API key is not valide")
+
+ # HTTP-Code 429: rate limit exceeded
+ if resp.status_code == 429:
+ raise Exception("rate limit has been exceeded!")
+
+ results = []
+
+ search_res = loads(resp.text)
+
+ # return empty array if there are no results
+ if not search_res.get('results', {}):
+ return []
+
+ # parse results
+ for result in search_res['results']:
+ if result['news']:
+ # timestamp (milliseconds since 1970)
+ publishedDate = datetime.datetime.fromtimestamp(result['date'] / 1000.0) # noqa
+
+ # append news result
+ results.append({'url': result['url'],
+ 'title': result['title'],
+ 'publishedDate': publishedDate,
+ 'content': result['kwic']})
+
+ else:
+ # append general result
+ # TODO, publishedDate correct?
+ results.append({'url': result['url'],
+ 'title': result['title'],
+ 'content': result['kwic']})
+
+ # append image result if image url is set
+ # TODO, show results with an image like in faroo
+ if result['iurl']:
+ results.append({'template': 'images.html',
+ 'url': result['url'],
+ 'title': result['title'],
+ 'content': result['kwic'],
+ 'img_src': result['iurl']})
+
+ # return results
+ return results
diff --git a/searx/engines/fdroid.py b/searx/engines/fdroid.py
new file mode 100644
index 0000000..a6b01a8
--- /dev/null
+++ b/searx/engines/fdroid.py
@@ -0,0 +1,51 @@
+"""
+ F-Droid (a repository of FOSS applications for Android)
+
+ @website https://f-droid.org/
+ @provide-api no
+ @using-api no
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title, content
+"""
+
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['files']
+paging = True
+
+# search-url
+base_url = 'https://f-droid.org/'
+search_url = base_url + 'repository/browse/?{query}'
+
+
+# do search-request
+def request(query, params):
+ query = urlencode({'fdfilter': query, 'fdpage': params['pageno']})
+ params['url'] = search_url.format(query=query)
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ for app in dom.xpath('//div[@id="appheader"]'):
+ url = app.xpath('./ancestor::a/@href')[0]
+ title = app.xpath('./p/span/text()')[0]
+ img_src = app.xpath('.//img/@src')[0]
+
+ content = extract_text(app.xpath('./p')[0])
+ content = content.replace(title, '', 1).strip()
+
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'img_src': img_src})
+
+ return results
diff --git a/searx/engines/filecrop.py b/searx/engines/filecrop.py
new file mode 100644
index 0000000..ed57a6b
--- /dev/null
+++ b/searx/engines/filecrop.py
@@ -0,0 +1,88 @@
+from searx.url_utils import urlencode
+
+try:
+ from HTMLParser import HTMLParser
+except:
+ from html.parser import HTMLParser
+
+url = 'http://www.filecrop.com/'
+search_url = url + '/search.php?{query}&size_i=0&size_f=100000000&engine_r=1&engine_d=1&engine_e=1&engine_4=1&engine_m=1&pos={index}' # noqa
+
+paging = True
+
+
+class FilecropResultParser(HTMLParser):
+
+ def __init__(self):
+ HTMLParser.__init__(self)
+ self.__start_processing = False
+
+ self.results = []
+ self.result = {}
+
+ self.tr_counter = 0
+ self.data_counter = 0
+
+ def handle_starttag(self, tag, attrs):
+
+ if tag == 'tr':
+ if ('bgcolor', '#edeff5') in attrs or\
+ ('bgcolor', '#ffffff') in attrs:
+ self.__start_processing = True
+
+ if not self.__start_processing:
+ return
+
+ if tag == 'label':
+ self.result['title'] = [attr[1] for attr in attrs
+ if attr[0] == 'title'][0]
+ elif tag == 'a' and ('rel', 'nofollow') in attrs\
+ and ('class', 'sourcelink') in attrs:
+ if 'content' in self.result:
+ self.result['content'] += [attr[1] for attr in attrs
+ if attr[0] == 'title'][0]
+ else:
+ self.result['content'] = [attr[1] for attr in attrs
+ if attr[0] == 'title'][0]
+ self.result['content'] += ' '
+ elif tag == 'a':
+ self.result['url'] = url + [attr[1] for attr in attrs
+ if attr[0] == 'href'][0]
+
+ def handle_endtag(self, tag):
+ if self.__start_processing is False:
+ return
+
+ if tag == 'tr':
+ self.tr_counter += 1
+
+ if self.tr_counter == 2:
+ self.__start_processing = False
+ self.tr_counter = 0
+ self.data_counter = 0
+ self.results.append(self.result)
+ self.result = {}
+
+ def handle_data(self, data):
+ if not self.__start_processing:
+ return
+
+ if 'content' in self.result:
+ self.result['content'] += data + ' '
+ else:
+ self.result['content'] = data + ' '
+
+ self.data_counter += 1
+
+
+def request(query, params):
+ index = 1 + (params['pageno'] - 1) * 30
+ params['url'] = search_url.format(query=urlencode({'w': query}), index=index)
+ return params
+
+
+def response(resp):
+ parser = FilecropResultParser()
+ parser.feed(resp.text)
+
+ return parser.results
diff --git a/searx/engines/flickr.py b/searx/engines/flickr.py
new file mode 100644
index 0000000..de17693
--- /dev/null
+++ b/searx/engines/flickr.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+
+"""
+ Flickr (Images)
+
+ @website https://www.flickr.com
+ @provide-api yes (https://secure.flickr.com/services/api/flickr.photos.search.html)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, thumbnail, img_src
+ More info on api-key : https://www.flickr.com/services/apps/create/
+"""
+
+from json import loads
+from searx.url_utils import urlencode
+
+categories = ['images']
+
+nb_per_page = 15
+paging = True
+api_key = None
+
+
+url = 'https://api.flickr.com/services/rest/?method=flickr.photos.search' +\
+ '&api_key={api_key}&{text}&sort=relevance' +\
+ '&extras=description%2C+owner_name%2C+url_o%2C+url_n%2C+url_z' +\
+ '&per_page={nb_per_page}&format=json&nojsoncallback=1&page={page}'
+photo_url = 'https://www.flickr.com/photos/{userid}/{photoid}'
+
+paging = True
+
+
+def build_flickr_url(user_id, photo_id):
+ return photo_url.format(userid=user_id, photoid=photo_id)
+
+
+def request(query, params):
+ params['url'] = url.format(text=urlencode({'text': query}),
+ api_key=api_key,
+ nb_per_page=nb_per_page,
+ page=params['pageno'])
+ return params
+
+
+def response(resp):
+ results = []
+
+ search_results = loads(resp.text)
+
+ # return empty array if there are no results
+ if 'photos' not in search_results:
+ return []
+
+ if 'photo' not in search_results['photos']:
+ return []
+
+ photos = search_results['photos']['photo']
+
+ # parse results
+ for photo in photos:
+ if 'url_o' in photo:
+ img_src = photo['url_o']
+ elif 'url_z' in photo:
+ img_src = photo['url_z']
+ else:
+ continue
+
+# For a bigger thumbnail, keep only the url_z, not the url_n
+ if 'url_n' in photo:
+ thumbnail_src = photo['url_n']
+ elif 'url_z' in photo:
+ thumbnail_src = photo['url_z']
+ else:
+ thumbnail_src = img_src
+
+ url = build_flickr_url(photo['owner'], photo['id'])
+
+ # append result
+ results.append({'url': url,
+ 'title': photo['title'],
+ 'img_src': img_src,
+ 'thumbnail_src': thumbnail_src,
+ 'content': photo['description']['_content'],
+ 'author': photo['ownername'],
+ 'template': 'images.html'})
+
+ # return results
+ return results
diff --git a/searx/engines/flickr_noapi.py b/searx/engines/flickr_noapi.py
new file mode 100644
index 0000000..08f07f7
--- /dev/null
+++ b/searx/engines/flickr_noapi.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+
+"""
+ Flickr (Images)
+
+ @website https://www.flickr.com
+ @provide-api yes (https://secure.flickr.com/services/api/flickr.photos.search.html)
+
+ @using-api no
+ @results HTML
+ @stable no
+ @parse url, title, thumbnail, img_src
+"""
+
+from json import loads
+from time import time
+import re
+from searx.engines import logger
+from searx.url_utils import urlencode
+
+
+logger = logger.getChild('flickr-noapi')
+
+categories = ['images']
+
+url = 'https://www.flickr.com/'
+search_url = url + 'search?{query}&page={page}'
+time_range_url = '&min_upload_date={start}&max_upload_date={end}'
+photo_url = 'https://www.flickr.com/photos/{userid}/{photoid}'
+regex = re.compile(r"\"search-photos-lite-models\",\"photos\":(.*}),\"totalItems\":", re.DOTALL)
+image_sizes = ('o', 'k', 'h', 'b', 'c', 'z', 'n', 'm', 't', 'q', 's')
+
+paging = True
+time_range_support = True
+time_range_dict = {'day': 60 * 60 * 24,
+ 'week': 60 * 60 * 24 * 7,
+ 'month': 60 * 60 * 24 * 7 * 4,
+ 'year': 60 * 60 * 24 * 7 * 52}
+
+
+def build_flickr_url(user_id, photo_id):
+ return photo_url.format(userid=user_id, photoid=photo_id)
+
+
+def _get_time_range_url(time_range):
+ if time_range in time_range_dict:
+ return time_range_url.format(start=time(), end=str(int(time()) - time_range_dict[time_range]))
+ return ''
+
+
+def request(query, params):
+ params['url'] = (search_url.format(query=urlencode({'text': query}), page=params['pageno'])
+ + _get_time_range_url(params['time_range']))
+ return params
+
+
+def response(resp):
+ results = []
+
+ matches = regex.search(resp.text)
+
+ if matches is None:
+ return results
+
+ match = matches.group(1)
+ search_results = loads(match)
+
+ if '_data' not in search_results:
+ return []
+
+ photos = search_results['_data']
+
+ for photo in photos:
+
+ # In paged configuration, the first pages' photos
+ # are represented by a None object
+ if photo is None:
+ continue
+
+ img_src = None
+ # From the biggest to the lowest format
+ for image_size in image_sizes:
+ if image_size in photo['sizes']:
+ img_src = photo['sizes'][image_size]['url']
+ break
+
+ if not img_src:
+ logger.debug('cannot find valid image size: {0}'.format(repr(photo)))
+ continue
+
+ if 'ownerNsid' not in photo:
+ continue
+
+ # For a bigger thumbnail, keep only the url_z, not the url_n
+ if 'n' in photo['sizes']:
+ thumbnail_src = photo['sizes']['n']['url']
+ elif 'z' in photo['sizes']:
+ thumbnail_src = photo['sizes']['z']['url']
+ else:
+ thumbnail_src = img_src
+
+ url = build_flickr_url(photo['ownerNsid'], photo['id'])
+
+ title = photo.get('title', '')
+
+ author = photo['username']
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'img_src': img_src,
+ 'thumbnail_src': thumbnail_src,
+ 'content': '',
+ 'author': author,
+ 'template': 'images.html'})
+
+ return results
diff --git a/searx/engines/framalibre.py b/searx/engines/framalibre.py
new file mode 100644
index 0000000..146cdae
--- /dev/null
+++ b/searx/engines/framalibre.py
@@ -0,0 +1,69 @@
+"""
+ FramaLibre (It)
+
+ @website https://framalibre.org/
+ @provide-api no
+
+ @using-api no
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title, content, thumbnail, img_src
+"""
+
+from cgi import escape
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.url_utils import urljoin, urlencode
+
+# engine dependent config
+categories = ['it']
+paging = True
+
+# search-url
+base_url = 'https://framalibre.org/'
+search_url = base_url + 'recherche-par-crit-res?{query}&page={offset}'
+
+# specific xpath variables
+results_xpath = '//div[@class="nodes-list-row"]/div[contains(@typeof,"sioc:Item")]'
+link_xpath = './/h3[@class="node-title"]/a[@href]'
+thumbnail_xpath = './/img[@class="media-object img-responsive"]/@src'
+content_xpath = './/div[@class="content"]//p'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1)
+ params['url'] = search_url.format(query=urlencode({'keys': query}),
+ offset=offset)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ link = result.xpath(link_xpath)[0]
+ href = urljoin(base_url, link.attrib.get('href'))
+ # there's also a span (class="rdf-meta element-hidden" property="dc:title")'s content property for this...
+ title = escape(extract_text(link))
+ thumbnail_tags = result.xpath(thumbnail_xpath)
+ thumbnail = None
+ if len(thumbnail_tags) > 0:
+ thumbnail = extract_text(thumbnail_tags[0])
+ if thumbnail[0] == '/':
+ thumbnail = base_url + thumbnail
+ content = escape(extract_text(result.xpath(content_xpath)))
+
+ # append result
+ results.append({'url': href,
+ 'title': title,
+ 'img_src': thumbnail,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/frinkiac.py b/searx/engines/frinkiac.py
new file mode 100644
index 0000000..a67b42d
--- /dev/null
+++ b/searx/engines/frinkiac.py
@@ -0,0 +1,44 @@
+"""
+Frinkiac (Images)
+
+@website https://www.frinkiac.com
+@provide-api no
+@using-api no
+@results JSON
+@stable no
+@parse url, title, img_src
+"""
+
+from json import loads
+from searx.url_utils import urlencode
+
+categories = ['images']
+
+BASE = 'https://frinkiac.com/'
+SEARCH_URL = '{base}api/search?{query}'
+RESULT_URL = '{base}?{query}'
+THUMB_URL = '{base}img/{episode}/{timestamp}/medium.jpg'
+IMAGE_URL = '{base}img/{episode}/{timestamp}.jpg'
+
+
+def request(query, params):
+ params['url'] = SEARCH_URL.format(base=BASE, query=urlencode({'q': query}))
+ return params
+
+
+def response(resp):
+ results = []
+ response_data = loads(resp.text)
+ for result in response_data:
+ episode = result['Episode']
+ timestamp = result['Timestamp']
+
+ results.append({'template': 'images.html',
+ 'url': RESULT_URL.format(base=BASE,
+ query=urlencode({'p': 'caption', 'e': episode, 't': timestamp})),
+ 'title': episode,
+ 'content': '',
+ 'thumbnail_src': THUMB_URL.format(base=BASE, episode=episode, timestamp=timestamp),
+ 'img_src': IMAGE_URL.format(base=BASE, episode=episode, timestamp=timestamp)})
+
+ return results
diff --git a/searx/engines/generalfile.py b/searx/engines/generalfile.py
new file mode 100644
index 0000000..3bb2744
--- /dev/null
+++ b/searx/engines/generalfile.py
@@ -0,0 +1,62 @@
+"""
+ General Files (Files)
+
+ @website http://www.general-files.org
+ @provide-api no (nothing found)
+
+ @using-api no (because nothing found)
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content
+
+ @todo detect torrents?
+"""
+
+from lxml import html
+
+# engine dependent config
+categories = ['files']
+paging = True
+
+# search-url
+base_url = 'http://www.general-file.com'
+search_url = base_url + '/files-{letter}/{query}/{pageno}'
+
+# specific xpath variables
+result_xpath = '//table[@class="block-file"]'
+title_xpath = './/h2/a//text()'
+url_xpath = './/h2/a/@href'
+content_xpath = './/p//text()'
+
+
+# do search-request
+def request(query, params):
+
+ params['url'] = search_url.format(query=query,
+ letter=query[0],
+ pageno=params['pageno'])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath(result_xpath):
+ url = result.xpath(url_xpath)[0]
+
+ # skip fast download links
+ if not url.startswith('/'):
+ continue
+
+ # append result
+ results.append({'url': base_url + url,
+ 'title': ''.join(result.xpath(title_xpath)),
+ 'content': ''.join(result.xpath(content_xpath))})
+
+ # return results
+ return results
diff --git a/searx/engines/gigablast.py b/searx/engines/gigablast.py
new file mode 100644
index 0000000..37933c6
--- /dev/null
+++ b/searx/engines/gigablast.py
@@ -0,0 +1,106 @@
+"""
+ Gigablast (Web)
+
+ @website https://gigablast.com
+ @provide-api yes (https://gigablast.com/api.html)
+
+ @using-api yes
+ @results XML
+ @stable yes
+ @parse url, title, content
+"""
+
+from json import loads
+from time import time
+from lxml.html import fromstring
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['general']
+paging = True
+number_of_results = 10
+language_support = True
+safesearch = True
+
+# search-url
+base_url = 'https://gigablast.com/'
+search_string = 'search?{query}'\
+ '&n={number_of_results}'\
+ '&c=main'\
+ '&s={offset}'\
+ '&format=json'\
+ '&qh=0'\
+ '&qlang={lang}'\
+ '&ff={safesearch}'\
+ '&rxikd={rxikd}' # random number - 9 digits
+
+# specific xpath variables
+results_xpath = '//response//result'
+url_xpath = './/url'
+title_xpath = './/title'
+content_xpath = './/sum'
+
+supported_languages_url = 'https://gigablast.com/search?&rxikd=1'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * number_of_results
+
+ if params['language'] == 'all':
+ language = 'xx'
+ else:
+ language = params['language'].replace('-', '_').lower()
+ if language.split('-')[0] != 'zh':
+ language = language.split('-')[0]
+
+ if params['safesearch'] >= 1:
+ safesearch = 1
+ else:
+ safesearch = 0
+
+ search_path = search_string.format(query=urlencode({'q': query}),
+ offset=offset,
+ number_of_results=number_of_results,
+ rxikd=str(time())[:9],
+ lang=language,
+ safesearch=safesearch)
+
+ params['url'] = base_url + search_path
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ # parse results
+ response_json = loads(resp.text)
+
+ for result in response_json['results']:
+ # append result
+ results.append({'url': result['url'],
+ 'title': result['title'],
+ 'content': result['sum']})
+
+ # return results
+ return results
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+ supported_languages = []
+ dom = fromstring(resp.text)
+ links = dom.xpath('//span[@id="menu2"]/a')
+ for link in links:
+ href = link.xpath('./@href')[0].split('lang%3A')
+ if len(href) == 2:
+ code = href[1].split('_')
+ if len(code) == 2:
+ code = code[0] + '-' + code[1].upper()
+ else:
+ code = code[0]
+ supported_languages.append(code)
+
+ return supported_languages
diff --git a/searx/engines/github.py b/searx/engines/github.py
new file mode 100644
index 0000000..eaa00da
--- /dev/null
+++ b/searx/engines/github.py
@@ -0,0 +1,60 @@
+"""
+ Github (It)
+
+ @website https://github.com/
+ @provide-api yes (https://developer.github.com/v3/)
+
+ @using-api yes
+ @results JSON
+ @stable yes (using api)
+ @parse url, title, content
+"""
+
+from json import loads
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['it']
+
+# search-url
+search_url = 'https://api.github.com/search/repositories?sort=stars&order=desc&{query}' # noqa
+
+accept_header = 'application/vnd.github.preview.text-match+json'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(query=urlencode({'q': query}))
+
+ params['headers']['Accept'] = accept_header
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_res = loads(resp.text)
+
+ # check if items are recieved
+ if 'items' not in search_res:
+ return []
+
+ # parse results
+ for res in search_res['items']:
+ title = res['name']
+ url = res['html_url']
+
+ if res['description']:
+ content = res['description'][:500]
+ else:
+ content = ''
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/google.py b/searx/engines/google.py
new file mode 100644
index 0000000..934f5c2
--- /dev/null
+++ b/searx/engines/google.py
@@ -0,0 +1,388 @@
+# Google (Web)
+#
+# @website https://www.google.com
+# @provide-api yes (https://developers.google.com/custom-search/)
+#
+# @using-api no
+# @results HTML
+# @stable no (HTML can change)
+# @parse url, title, content, suggestion
+
+import re
+from lxml import html, etree
+from searx.engines.xpath import extract_text, extract_url
+from searx import logger
+from searx.url_utils import urlencode, urlparse, parse_qsl
+
+logger = logger.getChild('google engine')
+
+
+# engine dependent config
+categories = ['general']
+paging = True
+language_support = True
+use_locale_domain = True
+time_range_support = True
+
+# based on https://en.wikipedia.org/wiki/List_of_Google_domains and tests
+default_hostname = 'www.google.com'
+
+country_to_hostname = {
+ 'BG': 'www.google.bg', # Bulgaria
+ 'CZ': 'www.google.cz', # Czech Republic
+ 'DE': 'www.google.de', # Germany
+ 'DK': 'www.google.dk', # Denmark
+ 'AT': 'www.google.at', # Austria
+ 'CH': 'www.google.ch', # Switzerland
+ 'GR': 'www.google.gr', # Greece
+ 'AU': 'www.google.com.au', # Australia
+ 'CA': 'www.google.ca', # Canada
+ 'GB': 'www.google.co.uk', # United Kingdom
+ 'ID': 'www.google.co.id', # Indonesia
+ 'IE': 'www.google.ie', # Ireland
+ 'IN': 'www.google.co.in', # India
+ 'MY': 'www.google.com.my', # Malaysia
+ 'NZ': 'www.google.co.nz', # New Zealand
+ 'PH': 'www.google.com.ph', # Philippines
+ 'SG': 'www.google.com.sg', # Singapore
+ # 'US': 'www.google.us', # United States, redirect to .com
+ 'ZA': 'www.google.co.za', # South Africa
+ 'AR': 'www.google.com.ar', # Argentina
+ 'CL': 'www.google.cl', # Chile
+ 'ES': 'www.google.es', # Spain
+ 'MX': 'www.google.com.mx', # Mexico
+ 'EE': 'www.google.ee', # Estonia
+ 'FI': 'www.google.fi', # Finland
+ 'BE': 'www.google.be', # Belgium
+ 'FR': 'www.google.fr', # France
+ 'IL': 'www.google.co.il', # Israel
+ 'HR': 'www.google.hr', # Croatia
+ 'HU': 'www.google.hu', # Hungary
+ 'IT': 'www.google.it', # Italy
+ 'JP': 'www.google.co.jp', # Japan
+ 'KR': 'www.google.co.kr', # South Korea
+ 'LT': 'www.google.lt', # Lithuania
+ 'LV': 'www.google.lv', # Latvia
+ 'NO': 'www.google.no', # Norway
+ 'NL': 'www.google.nl', # Netherlands
+ 'PL': 'www.google.pl', # Poland
+ 'BR': 'www.google.com.br', # Brazil
+ 'PT': 'www.google.pt', # Portugal
+ 'RO': 'www.google.ro', # Romania
+ 'RU': 'www.google.ru', # Russia
+ 'SK': 'www.google.sk', # Slovakia
+ 'SL': 'www.google.si', # Slovenia (SL -> si)
+ 'SE': 'www.google.se', # Sweden
+ 'TH': 'www.google.co.th', # Thailand
+ 'TR': 'www.google.com.tr', # Turkey
+ 'UA': 'www.google.com.ua', # Ukraine
+ # 'CN': 'www.google.cn', # China, only from China ?
+ 'HK': 'www.google.com.hk', # Hong Kong
+ 'TW': 'www.google.com.tw' # Taiwan
+}
+
+# osm
+url_map = 'https://www.openstreetmap.org/'\
+ + '?lat={latitude}&lon={longitude}&zoom={zoom}&layers=M'
+
+# search-url
+search_path = '/search'
+search_url = ('https://{hostname}' +
+ search_path +
+ '?{query}&start={offset}&gws_rd=cr&gbv=1&lr={lang}&ei=x')
+
+time_range_search = "&tbs=qdr:{range}"
+time_range_dict = {'day': 'd',
+ 'week': 'w',
+ 'month': 'm',
+ 'year': 'y'}
+
+# other URLs
+map_hostname_start = 'maps.google.'
+maps_path = '/maps'
+redirect_path = '/url'
+images_path = '/images'
+supported_languages_url = 'https://www.google.com/preferences?#languages'
+
+# specific xpath variables
+results_xpath = '//div[@class="g"]'
+url_xpath = './/h3/a/@href'
+title_xpath = './/h3'
+content_xpath = './/span[@class="st"]'
+content_misc_xpath = './/div[@class="f slp"]'
+suggestion_xpath = '//p[@class="_Bmc"]'
+spelling_suggestion_xpath = '//a[@class="spell"]'
+
+# map : detail location
+map_address_xpath = './/div[@class="s"]//table//td[2]/span/text()'
+map_phone_xpath = './/div[@class="s"]//table//td[2]/span/span'
+map_website_url_xpath = 'h3[2]/a/@href'
+map_website_title_xpath = 'h3[2]'
+
+# map : near the location
+map_near = 'table[@class="ts"]//tr'
+map_near_title = './/h4'
+map_near_url = './/h4/a/@href'
+map_near_phone = './/span[@class="nobr"]'
+
+# images
+images_xpath = './/div/a'
+image_url_xpath = './@href'
+image_img_src_xpath = './img/@src'
+
+# property names
+# FIXME : no translation
+property_address = "Address"
+property_phone = "Phone number"
+
+
+# remove google-specific tracking-url
+def parse_url(url_string, google_hostname):
+ # sanity check
+ if url_string is None:
+ return url_string
+
+ # normal case
+ parsed_url = urlparse(url_string)
+ if (parsed_url.netloc in [google_hostname, '']
+ and parsed_url.path == redirect_path):
+ query = dict(parse_qsl(parsed_url.query))
+ return query['q']
+ else:
+ return url_string
+
+
+# returns extract_text on the first result selected by the xpath or None
+def extract_text_from_dom(result, xpath):
+ r = result.xpath(xpath)
+ if len(r) > 0:
+ return extract_text(r[0])
+ return None
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 10
+
+ if params['language'] == 'all':
+ language = 'en'
+ country = 'US'
+ url_lang = ''
+ elif params['language'][:2] == 'jv':
+ language = 'jw'
+ country = 'ID'
+ url_lang = 'lang_jw'
+ else:
+ language_array = params['language'].lower().split('-')
+ if len(language_array) == 2:
+ country = language_array[1]
+ else:
+ country = 'US'
+ language = language_array[0] + ',' + language_array[0] + '-' + country
+ url_lang = 'lang_' + language_array[0]
+
+ if use_locale_domain:
+ google_hostname = country_to_hostname.get(country.upper(), default_hostname)
+ else:
+ google_hostname = default_hostname
+
+ params['url'] = search_url.format(offset=offset,
+ query=urlencode({'q': query}),
+ hostname=google_hostname,
+ lang=url_lang)
+ if params['time_range'] in time_range_dict:
+ params['url'] += time_range_search.format(range=time_range_dict[params['time_range']])
+
+ params['headers']['Accept-Language'] = language
+ params['headers']['Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
+
+ params['google_hostname'] = google_hostname
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ # detect google sorry
+ resp_url = urlparse(resp.url)
+ if resp_url.netloc == 'sorry.google.com' or resp_url.path == '/sorry/IndexRedirect':
+ raise RuntimeWarning('sorry.google.com')
+
+ # which hostname ?
+ google_hostname = resp.search_params.get('google_hostname')
+ google_url = "https://" + google_hostname
+
+ # convert the text to dom
+ dom = html.fromstring(resp.text)
+
+ instant_answer = dom.xpath('//div[@id="_vBb"]//text()')
+ if instant_answer:
+ results.append({'answer': u' '.join(instant_answer)})
+ try:
+ results_num = int(dom.xpath('//div[@id="resultStats"]//text()')[0]
+ .split()[1].replace(',', ''))
+ results.append({'number_of_results': results_num})
+ except:
+ pass
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ try:
+ title = extract_text(result.xpath(title_xpath)[0])
+ url = parse_url(extract_url(result.xpath(url_xpath), google_url), google_hostname)
+ parsed_url = urlparse(url, google_hostname)
+
+ # map result
+ if parsed_url.netloc == google_hostname:
+ # TODO fix inside links
+ continue
+ # if parsed_url.path.startswith(maps_path) or parsed_url.netloc.startswith(map_hostname_start):
+ # print "yooooo"*30
+ # x = result.xpath(map_near)
+ # if len(x) > 0:
+ # # map : near the location
+ # results = results + parse_map_near(parsed_url, x, google_hostname)
+ # else:
+ # # map : detail about a location
+ # results = results + parse_map_detail(parsed_url, result, google_hostname)
+ # # google news
+ # elif parsed_url.path == search_path:
+ # # skipping news results
+ # pass
+
+ # # images result
+ # elif parsed_url.path == images_path:
+ # # only thumbnail image provided,
+ # # so skipping image results
+ # # results = results + parse_images(result, google_hostname)
+ # pass
+
+ else:
+ # normal result
+ content = extract_text_from_dom(result, content_xpath)
+ if content is None:
+ continue
+ content_misc = extract_text_from_dom(result, content_misc_xpath)
+ if content_misc is not None:
+ content = content_misc + "<br />" + content
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content
+ })
+ except:
+ logger.debug('result parse error in:\n%s', etree.tostring(result, pretty_print=True))
+ continue
+
+ # parse suggestion
+ for suggestion in dom.xpath(suggestion_xpath):
+ # append suggestion
+ results.append({'suggestion': extract_text(suggestion)})
+
+ for correction in dom.xpath(spelling_suggestion_xpath):
+ results.append({'correction': extract_text(correction)})
+
+ # return results
+ return results
+
+
+def parse_images(result, google_hostname):
+ results = []
+ for image in result.xpath(images_xpath):
+ url = parse_url(extract_text(image.xpath(image_url_xpath)[0]), google_hostname)
+ img_src = extract_text(image.xpath(image_img_src_xpath)[0])
+
+ # append result
+ results.append({'url': url,
+ 'title': '',
+ 'content': '',
+ 'img_src': img_src,
+ 'template': 'images.html'
+ })
+
+ return results
+
+
+def parse_map_near(parsed_url, x, google_hostname):
+ results = []
+
+ for result in x:
+ title = extract_text_from_dom(result, map_near_title)
+ url = parse_url(extract_text_from_dom(result, map_near_url), google_hostname)
+ attributes = []
+ phone = extract_text_from_dom(result, map_near_phone)
+ add_attributes(attributes, property_phone, phone, 'tel:' + phone)
+ results.append({'title': title,
+ 'url': url,
+ 'content': attributes_to_html(attributes)
+ })
+
+ return results
+
+
+def parse_map_detail(parsed_url, result, google_hostname):
+ results = []
+
+ # try to parse the geoloc
+ m = re.search(r'@([0-9\.]+),([0-9\.]+),([0-9]+)', parsed_url.path)
+ if m is None:
+ m = re.search(r'll\=([0-9\.]+),([0-9\.]+)\&z\=([0-9]+)', parsed_url.query)
+
+ if m is not None:
+ # geoloc found (ignored)
+ lon = float(m.group(2)) # noqa
+ lat = float(m.group(1)) # noqa
+ zoom = int(m.group(3)) # noqa
+
+ # attributes
+ attributes = []
+ address = extract_text_from_dom(result, map_address_xpath)
+ phone = extract_text_from_dom(result, map_phone_xpath)
+ add_attributes(attributes, property_address, address, 'geo:' + str(lat) + ',' + str(lon))
+ add_attributes(attributes, property_phone, phone, 'tel:' + phone)
+
+ # title / content / url
+ website_title = extract_text_from_dom(result, map_website_title_xpath)
+ content = extract_text_from_dom(result, content_xpath)
+ website_url = parse_url(extract_text_from_dom(result, map_website_url_xpath), google_hostname)
+
+ # add a result if there is a website
+ if website_url is not None:
+ results.append({'title': website_title,
+ 'content': (content + '<br />' if content is not None else '')
+ + attributes_to_html(attributes),
+ 'url': website_url
+ })
+
+ return results
+
+
+def add_attributes(attributes, name, value, url):
+ if value is not None and len(value) > 0:
+ attributes.append({'label': name, 'value': value, 'url': url})
+
+
+def attributes_to_html(attributes):
+ retval = '<table class="table table-striped">'
+ for a in attributes:
+ value = a.get('value')
+ if 'url' in a:
+ value = '<a href="' + a.get('url') + '">' + value + '</a>'
+ retval = retval + '<tr><th>' + a.get('label') + '</th><td>' + value + '</td></tr>'
+ retval = retval + '</table>'
+ return retval
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+ supported_languages = {}
+ dom = html.fromstring(resp.text)
+ options = dom.xpath('//table//td/font/label/span')
+ for option in options:
+ code = option.xpath('./@id')[0][1:]
+ name = option.text.title()
+ supported_languages[code] = {"name": name}
+
+ return supported_languages
diff --git a/searx/engines/google_images.py b/searx/engines/google_images.py
new file mode 100644
index 0000000..9692f4b
--- /dev/null
+++ b/searx/engines/google_images.py
@@ -0,0 +1,95 @@
+"""
+ Google (Images)
+
+ @website https://www.google.com
+ @provide-api yes (https://developers.google.com/custom-search/)
+
+ @using-api no
+ @results HTML chunks with JSON inside
+ @stable no
+ @parse url, title, img_src
+"""
+
+from datetime import date, timedelta
+from json import loads
+from lxml import html
+from searx.url_utils import urlencode
+
+
+# engine dependent config
+categories = ['images']
+paging = True
+safesearch = True
+time_range_support = True
+number_of_results = 100
+
+search_url = 'https://www.google.com/search'\
+ '?{query}'\
+ '&asearch=ichunk'\
+ '&async=_id:rg_s,_pms:s'\
+ '&tbm=isch'\
+ '&yv=2'\
+ '&{search_options}'
+time_range_attr = "qdr:{range}"
+time_range_custom_attr = "cdr:1,cd_min:{start},cd_max{end}"
+time_range_dict = {'day': 'd',
+ 'week': 'w',
+ 'month': 'm'}
+
+
+# do search-request
+def request(query, params):
+ search_options = {
+ 'ijn': params['pageno'] - 1,
+ 'start': (params['pageno'] - 1) * number_of_results
+ }
+
+ if params['time_range'] in time_range_dict:
+ search_options['tbs'] = time_range_attr.format(range=time_range_dict[params['time_range']])
+ elif params['time_range'] == 'year':
+ now = date.today()
+ then = now - timedelta(days=365)
+ start = then.strftime('%m/%d/%Y')
+ end = now.strftime('%m/%d/%Y')
+ search_options['tbs'] = time_range_custom_attr.format(start=start, end=end)
+
+ if safesearch and params['safesearch']:
+ search_options['safe'] = 'on'
+
+ params['url'] = search_url.format(query=urlencode({'q': query}),
+ search_options=urlencode(search_options))
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ g_result = loads(resp.text)
+
+ dom = html.fromstring(g_result[1][1])
+
+ # parse results
+ for result in dom.xpath('//div[@data-ved]'):
+
+ try:
+ metadata = loads(''.join(result.xpath('./div[@class="rg_meta"]/text()')))
+ except:
+ continue
+
+ thumbnail_src = metadata['tu']
+
+ # http to https
+ thumbnail_src = thumbnail_src.replace("http://", "https://")
+
+ # append result
+ results.append({'url': metadata['ru'],
+ 'title': metadata['pt'],
+ 'content': metadata['s'],
+ 'thumbnail_src': thumbnail_src,
+ 'img_src': metadata['ou'],
+ 'template': 'images.html'})
+
+ # return results
+ return results
diff --git a/searx/engines/google_news.py b/searx/engines/google_news.py
new file mode 100644
index 0000000..7344b52
--- /dev/null
+++ b/searx/engines/google_news.py
@@ -0,0 +1,84 @@
+"""
+ Google (News)
+
+ @website https://news.google.com
+ @provide-api no
+
+ @using-api no
+ @results HTML
+ @stable no
+ @parse url, title, content, publishedDate
+"""
+
+from lxml import html
+from searx.engines.google import _fetch_supported_languages, supported_languages_url
+from searx.url_utils import urlencode
+
+# search-url
+categories = ['news']
+paging = True
+language_support = True
+safesearch = True
+time_range_support = True
+number_of_results = 10
+
+search_url = 'https://www.google.com/search'\
+ '?{query}'\
+ '&tbm=nws'\
+ '&gws_rd=cr'\
+ '&{search_options}'
+time_range_attr = "qdr:{range}"
+time_range_dict = {'day': 'd',
+ 'week': 'w',
+ 'month': 'm',
+ 'year': 'y'}
+
+
+# do search-request
+def request(query, params):
+
+ search_options = {
+ 'start': (params['pageno'] - 1) * number_of_results
+ }
+
+ if params['time_range'] in time_range_dict:
+ search_options['tbs'] = time_range_attr.format(range=time_range_dict[params['time_range']])
+
+ if safesearch and params['safesearch']:
+ search_options['safe'] = 'on'
+
+ params['url'] = search_url.format(query=urlencode({'q': query}),
+ search_options=urlencode(search_options))
+
+ if params['language'] != 'all':
+ language_array = params['language'].lower().split('-')
+ params['url'] += '&lr=lang_' + language_array[0]
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath('//div[@class="g"]|//div[@class="g _cy"]'):
+ try:
+ r = {
+ 'url': result.xpath('.//div[@class="_cnc"]//a/@href')[0],
+ 'title': ''.join(result.xpath('.//div[@class="_cnc"]//h3//text()')),
+ 'content': ''.join(result.xpath('.//div[@class="st"]//text()')),
+ }
+ except:
+ continue
+
+ imgs = result.xpath('.//img/@src')
+ if len(imgs) and not imgs[0].startswith('data'):
+ r['img_src'] = imgs[0]
+
+ results.append(r)
+
+ # return results
+ return results
diff --git a/searx/engines/ina.py b/searx/engines/ina.py
new file mode 100644
index 0000000..37a05f0
--- /dev/null
+++ b/searx/engines/ina.py
@@ -0,0 +1,87 @@
+# INA (Videos)
+#
+# @website https://www.ina.fr/
+# @provide-api no
+#
+# @using-api no
+# @results HTML (using search portal)
+# @stable no (HTML can change)
+# @parse url, title, content, publishedDate, thumbnail
+#
+# @todo set content-parameter with correct data
+# @todo embedded (needs some md5 from video page)
+
+from json import loads
+from lxml import html
+from dateutil import parser
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode
+
+try:
+ from HTMLParser import HTMLParser
+except:
+ from html.parser import HTMLParser
+
+# engine dependent config
+categories = ['videos']
+paging = True
+page_size = 48
+
+# search-url
+base_url = 'https://www.ina.fr'
+search_url = base_url + '/layout/set/ajax/recherche/result?autopromote=&hf={ps}&b={start}&type=Video&r=&{query}'
+
+# specific xpath variables
+results_xpath = '//div[contains(@class,"search-results--list")]/div[@class="media"]'
+url_xpath = './/a/@href'
+title_xpath = './/h3[@class="h3--title media-heading"]'
+thumbnail_xpath = './/img/@src'
+publishedDate_xpath = './/span[@class="broadcast"]'
+content_xpath = './/p[@class="media-body__summary"]'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(ps=page_size,
+ start=params['pageno'] * page_size,
+ query=urlencode({'q': query}))
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ # we get html in a JSON container...
+ response = loads(resp.text)
+ if "content" not in response:
+ return []
+ dom = html.fromstring(response["content"])
+ p = HTMLParser()
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ videoid = result.xpath(url_xpath)[0]
+ url = base_url + videoid
+ title = p.unescape(extract_text(result.xpath(title_xpath)))
+ thumbnail = extract_text(result.xpath(thumbnail_xpath)[0])
+ if thumbnail[0] == '/':
+ thumbnail = base_url + thumbnail
+ d = extract_text(result.xpath(publishedDate_xpath)[0])
+ d = d.split('/')
+ # force ISO date to avoid wrong parsing
+ d = "%s-%s-%s" % (d[2], d[1], d[0])
+ publishedDate = parser.parse(d)
+ content = extract_text(result.xpath(content_xpath))
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'template': 'videos.html',
+ 'publishedDate': publishedDate,
+ 'thumbnail': thumbnail})
+
+ # return results
+ return results
diff --git a/searx/engines/json_engine.py b/searx/engines/json_engine.py
new file mode 100644
index 0000000..67d6a5a
--- /dev/null
+++ b/searx/engines/json_engine.py
@@ -0,0 +1,118 @@
+from collections import Iterable
+from json import loads
+from sys import version_info
+from searx.url_utils import urlencode
+
+if version_info[0] == 3:
+ unicode = str
+
+search_url = None
+url_query = None
+content_query = None
+title_query = None
+paging = False
+suggestion_query = ''
+results_query = ''
+
+# parameters for engines with paging support
+#
+# number of results on each page
+# (only needed if the site requires not a page number, but an offset)
+page_size = 1
+# number of the first page (usually 0 or 1)
+first_page_num = 1
+
+
+def iterate(iterable):
+ if type(iterable) == dict:
+ it = iterable.items()
+
+ else:
+ it = enumerate(iterable)
+ for index, value in it:
+ yield str(index), value
+
+
+def is_iterable(obj):
+ if type(obj) == str:
+ return False
+ if type(obj) == unicode:
+ return False
+ return isinstance(obj, Iterable)
+
+
+def parse(query):
+ q = []
+ for part in query.split('/'):
+ if part == '':
+ continue
+ else:
+ q.append(part)
+ return q
+
+
+def do_query(data, q):
+ ret = []
+ if not q:
+ return ret
+
+ qkey = q[0]
+
+ for key, value in iterate(data):
+
+ if len(q) == 1:
+ if key == qkey:
+ ret.append(value)
+ elif is_iterable(value):
+ ret.extend(do_query(value, q))
+ else:
+ if not is_iterable(value):
+ continue
+ if key == qkey:
+ ret.extend(do_query(value, q[1:]))
+ else:
+ ret.extend(do_query(value, q))
+ return ret
+
+
+def query(data, query_string):
+ q = parse(query_string)
+
+ return do_query(data, q)
+
+
+def request(query, params):
+ query = urlencode({'q': query})[2:]
+
+ fp = {'query': query}
+ if paging and search_url.find('{pageno}') >= 0:
+ fp['pageno'] = (params['pageno'] - 1) * page_size + first_page_num
+
+ params['url'] = search_url.format(**fp)
+ params['query'] = query
+
+ return params
+
+
+def response(resp):
+ results = []
+ json = loads(resp.text)
+ if results_query:
+ for result in query(json, results_query)[0]:
+ url = query(result, url_query)[0]
+ title = query(result, title_query)[0]
+ content = query(result, content_query)[0]
+ results.append({'url': url, 'title': title, 'content': content})
+ else:
+ for url, title, content in zip(
+ query(json, url_query),
+ query(json, title_query),
+ query(json, content_query)
+ ):
+ results.append({'url': url, 'title': title, 'content': content})
+
+ if not suggestion_query:
+ return results
+ for suggestion in query(json, suggestion_query):
+ results.append({'suggestion': suggestion})
+ return results
diff --git a/searx/engines/kickass.py b/searx/engines/kickass.py
new file mode 100644
index 0000000..5e897c9
--- /dev/null
+++ b/searx/engines/kickass.py
@@ -0,0 +1,92 @@
+"""
+ Kickass Torrent (Videos, Music, Files)
+
+ @website https://kickass.so
+ @provide-api no (nothing found)
+
+ @using-api no
+ @results HTML (using search portal)
+ @stable yes (HTML can change)
+ @parse url, title, content, seed, leech, magnetlink
+"""
+
+from lxml import html
+from operator import itemgetter
+from searx.engines.xpath import extract_text
+from searx.utils import get_torrent_size, convert_str_to_int
+from searx.url_utils import quote, urljoin
+
+# engine dependent config
+categories = ['videos', 'music', 'files']
+paging = True
+
+# search-url
+url = 'https://kickass.cd/'
+search_url = url + 'search/{search_term}/{pageno}/'
+
+# specific xpath variables
+magnet_xpath = './/a[@title="Torrent magnet link"]'
+torrent_xpath = './/a[@title="Download torrent file"]'
+content_xpath = './/span[@class="font11px lightgrey block"]'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(search_term=quote(query),
+ pageno=params['pageno'])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ search_res = dom.xpath('//table[@class="data"]//tr')
+
+ # return empty array if nothing is found
+ if not search_res:
+ return []
+
+ # parse results
+ for result in search_res[1:]:
+ link = result.xpath('.//a[@class="cellMainLink"]')[0]
+ href = urljoin(url, link.attrib['href'])
+ title = extract_text(link)
+ content = extract_text(result.xpath(content_xpath))
+ seed = extract_text(result.xpath('.//td[contains(@class, "green")]'))
+ leech = extract_text(result.xpath('.//td[contains(@class, "red")]'))
+ filesize_info = extract_text(result.xpath('.//td[contains(@class, "nobr")]'))
+ files = extract_text(result.xpath('.//td[contains(@class, "center")][2]'))
+
+ seed = convert_str_to_int(seed)
+ leech = convert_str_to_int(leech)
+
+ filesize, filesize_multiplier = filesize_info.split()
+ filesize = get_torrent_size(filesize, filesize_multiplier)
+ if files.isdigit():
+ files = int(files)
+ else:
+ files = None
+
+ magnetlink = result.xpath(magnet_xpath)[0].attrib['href']
+
+ torrentfile = result.xpath(torrent_xpath)[0].attrib['href']
+ torrentfileurl = quote(torrentfile, safe="%/:=&?~#+!$,;'@()*")
+
+ # append result
+ results.append({'url': href,
+ 'title': title,
+ 'content': content,
+ 'seed': seed,
+ 'leech': leech,
+ 'filesize': filesize,
+ 'files': files,
+ 'magnetlink': magnetlink,
+ 'torrentfile': torrentfileurl,
+ 'template': 'torrent.html'})
+
+ # return results sorted by seeder
+ return sorted(results, key=itemgetter('seed'), reverse=True)
diff --git a/searx/engines/mediawiki.py b/searx/engines/mediawiki.py
new file mode 100644
index 0000000..0607ac9
--- /dev/null
+++ b/searx/engines/mediawiki.py
@@ -0,0 +1,90 @@
+"""
+ general mediawiki-engine (Web)
+
+ @website websites built on mediawiki (https://www.mediawiki.org)
+ @provide-api yes (http://www.mediawiki.org/wiki/API:Search)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title
+
+ @todo content
+"""
+
+from json import loads
+from string import Formatter
+from searx.url_utils import urlencode, quote
+
+# engine dependent config
+categories = ['general']
+language_support = True
+paging = True
+number_of_results = 1
+search_type = 'nearmatch' # possible values: title, text, nearmatch
+
+# search-url
+base_url = 'https://{language}.wikipedia.org/'
+search_postfix = 'w/api.php?action=query'\
+ '&list=search'\
+ '&{query}'\
+ '&format=json'\
+ '&sroffset={offset}'\
+ '&srlimit={limit}'\
+ '&srwhat={searchtype}'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * number_of_results
+
+ string_args = dict(query=urlencode({'srsearch': query}),
+ offset=offset,
+ limit=number_of_results,
+ searchtype=search_type)
+
+ format_strings = list(Formatter().parse(base_url))
+
+ if params['language'] == 'all':
+ language = 'en'
+ else:
+ language = params['language'].split('-')[0]
+
+ # format_string [('https://', 'language', '', None), ('.wikipedia.org/', None, None, None)]
+ if any(x[1] == 'language' for x in format_strings):
+ string_args['language'] = language
+
+ # write search-language back to params, required in response
+ params['language'] = language
+
+ search_url = base_url + search_postfix
+
+ params['url'] = search_url.format(**string_args)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_results = loads(resp.text)
+
+ # return empty array if there are no results
+ if not search_results.get('query', {}).get('search'):
+ return []
+
+ # parse results
+ for result in search_results['query']['search']:
+ if result.get('snippet', '').startswith('#REDIRECT'):
+ continue
+ url = base_url.format(language=resp.search_params['language']) +\
+ 'wiki/' + quote(result['title'].replace(' ', '_').encode('utf-8'))
+
+ # append result
+ results.append({'url': url,
+ 'title': result['title'],
+ 'content': ''})
+
+ # return results
+ return results
diff --git a/searx/engines/mixcloud.py b/searx/engines/mixcloud.py
new file mode 100644
index 0000000..470c007
--- /dev/null
+++ b/searx/engines/mixcloud.py
@@ -0,0 +1,61 @@
+"""
+ Mixcloud (Music)
+
+ @website https://http://www.mixcloud.com/
+ @provide-api yes (http://www.mixcloud.com/developers/
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content, embedded, publishedDate
+"""
+
+from json import loads
+from dateutil import parser
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['music']
+paging = True
+
+# search-url
+url = 'https://api.mixcloud.com/'
+search_url = url + 'search/?{query}&type=cloudcast&limit=10&offset={offset}'
+
+embedded_url = '<iframe scrolling="no" frameborder="0" allowTransparency="true" ' +\
+ 'data-src="https://www.mixcloud.com/widget/iframe/?feed={url}" width="300" height="300"></iframe>'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 10
+
+ params['url'] = search_url.format(query=urlencode({'q': query}),
+ offset=offset)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_res = loads(resp.text)
+
+ # parse results
+ for result in search_res.get('data', []):
+ title = result['name']
+ url = result['url']
+ content = result['user']['name']
+ embedded = embedded_url.format(url=url)
+ publishedDate = parser.parse(result['created_time'])
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'embedded': embedded,
+ 'publishedDate': publishedDate,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/nyaa.py b/searx/engines/nyaa.py
new file mode 100644
index 0000000..272c712
--- /dev/null
+++ b/searx/engines/nyaa.py
@@ -0,0 +1,117 @@
+"""
+ Nyaa.se (Anime Bittorrent tracker)
+
+ @website http://www.nyaa.se/
+ @provide-api no
+ @using-api no
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title, content, seed, leech, torrentfile
+"""
+
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['files', 'images', 'videos', 'music']
+paging = True
+
+# search-url
+base_url = 'http://www.nyaa.se/'
+search_url = base_url + '?page=search&{query}&offset={offset}'
+
+# xpath queries
+xpath_results = '//table[@class="tlist"]//tr[contains(@class, "tlistrow")]'
+xpath_category = './/td[@class="tlisticon"]/a'
+xpath_title = './/td[@class="tlistname"]/a'
+xpath_torrent_file = './/td[@class="tlistdownload"]/a'
+xpath_filesize = './/td[@class="tlistsize"]/text()'
+xpath_seeds = './/td[@class="tlistsn"]/text()'
+xpath_leeches = './/td[@class="tlistln"]/text()'
+xpath_downloads = './/td[@class="tlistdn"]/text()'
+
+
+# convert a variable to integer or return 0 if it's not a number
+def int_or_zero(num):
+ if isinstance(num, list):
+ if len(num) < 1:
+ return 0
+ num = num[0]
+ if num.isdigit():
+ return int(num)
+ return 0
+
+
+# get multiplier to convert torrent size to bytes
+def get_filesize_mul(suffix):
+ return {
+ 'KB': 1024,
+ 'MB': 1024 ** 2,
+ 'GB': 1024 ** 3,
+ 'TB': 1024 ** 4,
+
+ 'KIB': 1024,
+ 'MIB': 1024 ** 2,
+ 'GIB': 1024 ** 3,
+ 'TIB': 1024 ** 4
+ }[str(suffix).upper()]
+
+
+# do search-request
+def request(query, params):
+ query = urlencode({'term': query})
+ params['url'] = search_url.format(query=query, offset=params['pageno'])
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ for result in dom.xpath(xpath_results):
+ # category in which our torrent belongs
+ category = result.xpath(xpath_category)[0].attrib.get('title')
+
+ # torrent title
+ page_a = result.xpath(xpath_title)[0]
+ title = extract_text(page_a)
+
+ # link to the page
+ href = page_a.attrib.get('href')
+
+ # link to the torrent file
+ torrent_link = result.xpath(xpath_torrent_file)[0].attrib.get('href')
+
+ # torrent size
+ try:
+ file_size, suffix = result.xpath(xpath_filesize)[0].split(' ')
+ file_size = int(float(file_size) * get_filesize_mul(suffix))
+ except:
+ file_size = None
+
+ # seed count
+ seed = int_or_zero(result.xpath(xpath_seeds))
+
+ # leech count
+ leech = int_or_zero(result.xpath(xpath_leeches))
+
+ # torrent downloads count
+ downloads = int_or_zero(result.xpath(xpath_downloads))
+
+ # content string contains all information not included into template
+ content = 'Category: "{category}". Downloaded {downloads} times.'
+ content = content.format(category=category, downloads=downloads)
+
+ results.append({'url': href,
+ 'title': title,
+ 'content': content,
+ 'seed': seed,
+ 'leech': leech,
+ 'filesize': file_size,
+ 'torrentfile': torrent_link,
+ 'template': 'torrent.html'})
+
+ return results
diff --git a/searx/engines/openstreetmap.py b/searx/engines/openstreetmap.py
new file mode 100644
index 0000000..733ba62
--- /dev/null
+++ b/searx/engines/openstreetmap.py
@@ -0,0 +1,95 @@
+"""
+ OpenStreetMap (Map)
+
+ @website https://openstreetmap.org/
+ @provide-api yes (http://wiki.openstreetmap.org/wiki/Nominatim)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title
+"""
+
+from json import loads
+
+# engine dependent config
+categories = ['map']
+paging = False
+
+# search-url
+base_url = 'https://nominatim.openstreetmap.org/'
+search_string = 'search/{query}?format=json&polygon_geojson=1&addressdetails=1'
+result_base_url = 'https://openstreetmap.org/{osm_type}/{osm_id}'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = base_url + search_string.format(query=query)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+ json = loads(resp.text)
+
+ # parse results
+ for r in json:
+ if 'display_name' not in r:
+ continue
+
+ title = r['display_name'] or u''
+ osm_type = r.get('osm_type', r.get('type'))
+ url = result_base_url.format(osm_type=osm_type,
+ osm_id=r['osm_id'])
+
+ osm = {'type': osm_type,
+ 'id': r['osm_id']}
+
+ geojson = r.get('geojson')
+
+ # if no geojson is found and osm_type is a node, add geojson Point
+ if not geojson and osm_type == 'node':
+ geojson = {u'type': u'Point', u'coordinates': [r['lon'], r['lat']]}
+
+ address_raw = r.get('address')
+ address = {}
+
+ # get name
+ if r['class'] == 'amenity' or\
+ r['class'] == 'shop' or\
+ r['class'] == 'tourism' or\
+ r['class'] == 'leisure':
+ if address_raw.get('address29'):
+ address = {'name': address_raw.get('address29')}
+ else:
+ address = {'name': address_raw.get(r['type'])}
+
+ # add rest of adressdata, if something is already found
+ if address.get('name'):
+ address.update({'house_number': address_raw.get('house_number'),
+ 'road': address_raw.get('road'),
+ 'locality': address_raw.get('city',
+ address_raw.get('town', # noqa
+ address_raw.get('village'))), # noqa
+ 'postcode': address_raw.get('postcode'),
+ 'country': address_raw.get('country'),
+ 'country_code': address_raw.get('country_code')})
+ else:
+ address = None
+
+ # append result
+ results.append({'template': 'map.html',
+ 'title': title,
+ 'content': '',
+ 'longitude': r['lon'],
+ 'latitude': r['lat'],
+ 'boundingbox': r['boundingbox'],
+ 'geojson': geojson,
+ 'address': address,
+ 'osm': osm,
+ 'url': url})
+
+ # return results
+ return results
diff --git a/searx/engines/pdbe.py b/searx/engines/pdbe.py
new file mode 100644
index 0000000..f784e10
--- /dev/null
+++ b/searx/engines/pdbe.py
@@ -0,0 +1,109 @@
+"""
+ PDBe (Protein Data Bank in Europe)
+
+ @website https://www.ebi.ac.uk/pdbe
+ @provide-api yes (https://www.ebi.ac.uk/pdbe/api/doc/search.html),
+ unlimited
+ @using-api yes
+ @results python dictionary (from json)
+ @stable yes
+ @parse url, title, content, img_src
+"""
+
+from json import loads
+from flask_babel import gettext
+
+categories = ['science']
+
+hide_obsolete = False
+
+# status codes of unpublished entries
+pdb_unpublished_codes = ['HPUB', 'HOLD', 'PROC', 'WAIT', 'AUTH', 'AUCO', 'REPL', 'POLC', 'REFI', 'TRSF', 'WDRN']
+# url for api query
+pdbe_solr_url = 'https://www.ebi.ac.uk/pdbe/search/pdb/select?'
+# base url for results
+pdbe_entry_url = 'https://www.ebi.ac.uk/pdbe/entry/pdb/{pdb_id}'
+# link to preview image of structure
+pdbe_preview_url = 'https://www.ebi.ac.uk/pdbe/static/entry/{pdb_id}_deposited_chain_front_image-200x200.png'
+
+
+def request(query, params):
+
+ params['url'] = pdbe_solr_url
+ params['method'] = 'POST'
+ params['data'] = {
+ 'q': query,
+ 'wt': "json" # request response in parsable format
+ }
+ return params
+
+
+def construct_body(result):
+ # set title
+ title = result['title']
+
+ # construct content body
+ content = """{title}<br />{authors} {journal} <strong>{volume}</strong>&nbsp;{page} ({year})"""
+
+ # replace placeholders with actual content
+ try:
+ if result['journal']:
+ content = content.format(
+ title=result['citation_title'],
+ authors=result['entry_author_list'][0], journal=result['journal'], volume=result['journal_volume'],
+ page=result['journal_page'], year=result['citation_year'])
+ else:
+ content = content.format(
+ title=result['citation_title'],
+ authors=result['entry_author_list'][0], journal='', volume='', page='', year=result['release_year'])
+ img_src = pdbe_preview_url.format(pdb_id=result['pdb_id'])
+ except (KeyError):
+ content = None
+ img_src = None
+
+ # construct url for preview image
+ try:
+ img_src = pdbe_preview_url.format(pdb_id=result['pdb_id'])
+ except (KeyError):
+ img_src = None
+
+ return [title, content, img_src]
+
+
+def response(resp):
+
+ results = []
+ json = loads(resp.text)['response']['docs']
+
+ # parse results
+ for result in json:
+ # catch obsolete entries and mark them accordingly
+ if result['status'] in pdb_unpublished_codes:
+ continue
+ if hide_obsolete:
+ continue
+ if result['status'] == 'OBS':
+ # expand title to add some sort of warning message
+ title = gettext('{title}&nbsp;(OBSOLETE)').format(title=result['title'])
+ superseded_url = pdbe_entry_url.format(pdb_id=result['superseded_by'])
+
+ # since we can't construct a proper body from the response, we'll make up our own
+ msg_superseded = gettext("This entry has been superseded by")
+ content = '<em>{msg_superseded} \<a href="{url}">{pdb_id}</a></em>'.format(
+ msg_superseded=msg_superseded,
+ url=superseded_url,
+ pdb_id=result['superseded_by'], )
+
+ # obsoleted entries don't have preview images
+ img_src = None
+ else:
+ title, content, img_src = construct_body(result)
+
+ results.append({
+ 'url': pdbe_entry_url.format(pdb_id=result['pdb_id']),
+ 'title': title,
+ 'content': content,
+ 'img_src': img_src
+ })
+
+ return results
diff --git a/searx/engines/photon.py b/searx/engines/photon.py
new file mode 100644
index 0000000..15236f6
--- /dev/null
+++ b/searx/engines/photon.py
@@ -0,0 +1,131 @@
+"""
+ Photon (Map)
+
+ @website https://photon.komoot.de
+ @provide-api yes (https://photon.komoot.de/)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title
+"""
+
+from json import loads
+from searx.utils import searx_useragent
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['map']
+paging = False
+language_support = True
+number_of_results = 10
+
+# search-url
+base_url = 'https://photon.komoot.de/'
+search_string = 'api/?{query}&limit={limit}'
+result_base_url = 'https://openstreetmap.org/{osm_type}/{osm_id}'
+
+# list of supported languages
+supported_languages = ['de', 'en', 'fr', 'it']
+
+
+# do search-request
+def request(query, params):
+ params['url'] = base_url +\
+ search_string.format(query=urlencode({'q': query}),
+ limit=number_of_results)
+
+ if params['language'] != 'all':
+ language = params['language'].split('_')[0]
+ if language in supported_languages:
+ params['url'] = params['url'] + "&lang=" + language
+
+ # using searx User-Agent
+ params['headers']['User-Agent'] = searx_useragent()
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+ json = loads(resp.text)
+
+ # parse results
+ for r in json.get('features', {}):
+
+ properties = r.get('properties')
+
+ if not properties:
+ continue
+
+ # get title
+ title = properties.get('name')
+
+ # get osm-type
+ if properties.get('osm_type') == 'N':
+ osm_type = 'node'
+ elif properties.get('osm_type') == 'W':
+ osm_type = 'way'
+ elif properties.get('osm_type') == 'R':
+ osm_type = 'relation'
+ else:
+ # continue if invalide osm-type
+ continue
+
+ url = result_base_url.format(osm_type=osm_type,
+ osm_id=properties.get('osm_id'))
+
+ osm = {'type': osm_type,
+ 'id': properties.get('osm_id')}
+
+ geojson = r.get('geometry')
+
+ if properties.get('extent'):
+ boundingbox = [properties.get('extent')[3],
+ properties.get('extent')[1],
+ properties.get('extent')[0],
+ properties.get('extent')[2]]
+ else:
+ # TODO: better boundingbox calculation
+ boundingbox = [geojson['coordinates'][1],
+ geojson['coordinates'][1],
+ geojson['coordinates'][0],
+ geojson['coordinates'][0]]
+
+ # address calculation
+ address = {}
+
+ # get name
+ if properties.get('osm_key') == 'amenity' or\
+ properties.get('osm_key') == 'shop' or\
+ properties.get('osm_key') == 'tourism' or\
+ properties.get('osm_key') == 'leisure':
+ address = {'name': properties.get('name')}
+
+ # add rest of adressdata, if something is already found
+ if address.get('name'):
+ address.update({'house_number': properties.get('housenumber'),
+ 'road': properties.get('street'),
+ 'locality': properties.get('city',
+ properties.get('town', # noqa
+ properties.get('village'))), # noqa
+ 'postcode': properties.get('postcode'),
+ 'country': properties.get('country')})
+ else:
+ address = None
+
+ # append result
+ results.append({'template': 'map.html',
+ 'title': title,
+ 'content': '',
+ 'longitude': geojson['coordinates'][0],
+ 'latitude': geojson['coordinates'][1],
+ 'boundingbox': boundingbox,
+ 'geojson': geojson,
+ 'address': address,
+ 'osm': osm,
+ 'url': url})
+
+ # return results
+ return results
diff --git a/searx/engines/piratebay.py b/searx/engines/piratebay.py
new file mode 100644
index 0000000..a5af8d8
--- /dev/null
+++ b/searx/engines/piratebay.py
@@ -0,0 +1,96 @@
+# Piratebay (Videos, Music, Files)
+#
+# @website https://thepiratebay.se
+# @provide-api no (nothing found)
+#
+# @using-api no
+# @results HTML (using search portal)
+# @stable yes (HTML can change)
+# @parse url, title, content, seed, leech, magnetlink
+
+from lxml import html
+from operator import itemgetter
+from searx.engines.xpath import extract_text
+from searx.url_utils import quote, urljoin
+
+# engine dependent config
+categories = ['videos', 'music', 'files']
+paging = True
+
+# search-url
+url = 'https://thepiratebay.se/'
+search_url = url + 'search/{search_term}/{pageno}/99/{search_type}'
+
+# piratebay specific type-definitions
+search_types = {'files': '0',
+ 'music': '100',
+ 'videos': '200'}
+
+# specific xpath variables
+magnet_xpath = './/a[@title="Download this torrent using magnet"]'
+torrent_xpath = './/a[@title="Download this torrent"]'
+content_xpath = './/font[@class="detDesc"]'
+
+
+# do search-request
+def request(query, params):
+ search_type = search_types.get(params['category'], '0')
+
+ params['url'] = search_url.format(search_term=quote(query),
+ search_type=search_type,
+ pageno=params['pageno'] - 1)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ search_res = dom.xpath('//table[@id="searchResult"]//tr')
+
+ # return empty array if nothing is found
+ if not search_res:
+ return []
+
+ # parse results
+ for result in search_res[1:]:
+ link = result.xpath('.//div[@class="detName"]//a')[0]
+ href = urljoin(url, link.attrib.get('href'))
+ title = extract_text(link)
+ content = extract_text(result.xpath(content_xpath))
+ seed, leech = result.xpath('.//td[@align="right"]/text()')[:2]
+
+ # convert seed to int if possible
+ if seed.isdigit():
+ seed = int(seed)
+ else:
+ seed = 0
+
+ # convert leech to int if possible
+ if leech.isdigit():
+ leech = int(leech)
+ else:
+ leech = 0
+
+ magnetlink = result.xpath(magnet_xpath)[0]
+ torrentfile_links = result.xpath(torrent_xpath)
+ if torrentfile_links:
+ torrentfile_link = torrentfile_links[0].attrib.get('href')
+ else:
+ torrentfile_link = None
+
+ # append result
+ results.append({'url': href,
+ 'title': title,
+ 'content': content,
+ 'seed': seed,
+ 'leech': leech,
+ 'magnetlink': magnetlink.attrib.get('href'),
+ 'torrentfile': torrentfile_link,
+ 'template': 'torrent.html'})
+
+ # return results sorted by seeder
+ return sorted(results, key=itemgetter('seed'), reverse=True)
diff --git a/searx/engines/qwant.py b/searx/engines/qwant.py
new file mode 100644
index 0000000..3d266e2
--- /dev/null
+++ b/searx/engines/qwant.py
@@ -0,0 +1,140 @@
+"""
+ Qwant (Web, Images, News, Social)
+
+ @website https://qwant.com/
+ @provide-api not officially (https://api.qwant.com/api/search/)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content
+"""
+
+from datetime import datetime
+from json import loads
+from searx.utils import html_to_text
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = None
+paging = True
+language_support = True
+supported_languages_url = 'https://qwant.com/region'
+
+category_to_keyword = {'general': 'web',
+ 'images': 'images',
+ 'news': 'news',
+ 'social media': 'social'}
+
+# search-url
+url = 'https://api.qwant.com/api/search/{keyword}?count=10&offset={offset}&f=&{query}'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 10
+
+ if categories[0] and categories[0] in category_to_keyword:
+
+ params['url'] = url.format(keyword=category_to_keyword[categories[0]],
+ query=urlencode({'q': query}),
+ offset=offset)
+ else:
+ params['url'] = url.format(keyword='web',
+ query=urlencode({'q': query}),
+ offset=offset)
+
+ # add language tag if specified
+ if params['language'] != 'all':
+ if params['language'] == 'no' or params['language'].startswith('no-'):
+ params['language'] = params['language'].replace('no', 'nb', 1)
+ if params['language'].find('-') < 0:
+ # tries to get a country code from language
+ for lang in supported_languages:
+ lc = lang.split('-')
+ if params['language'] == lc[0]:
+ params['language'] = lang
+ break
+ params['url'] += '&locale=' + params['language'].replace('-', '_').lower()
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_results = loads(resp.text)
+
+ # return empty array if there are no results
+ if 'data' not in search_results:
+ return []
+
+ data = search_results.get('data', {})
+
+ res = data.get('result', {})
+
+ # parse results
+ for result in res.get('items', {}):
+
+ title = html_to_text(result['title'])
+ res_url = result['url']
+ content = html_to_text(result['desc'])
+
+ if category_to_keyword.get(categories[0], '') == 'web':
+ results.append({'title': title,
+ 'content': content,
+ 'url': res_url})
+
+ elif category_to_keyword.get(categories[0], '') == 'images':
+ thumbnail_src = result['thumbnail']
+ img_src = result['media']
+ results.append({'template': 'images.html',
+ 'url': res_url,
+ 'title': title,
+ 'content': '',
+ 'thumbnail_src': thumbnail_src,
+ 'img_src': img_src})
+
+ elif category_to_keyword.get(categories[0], '') == 'social':
+ published_date = datetime.fromtimestamp(result['date'], None)
+ img_src = result.get('img', None)
+ results.append({'url': res_url,
+ 'title': title,
+ 'publishedDate': published_date,
+ 'content': content,
+ 'img_src': img_src})
+
+ elif category_to_keyword.get(categories[0], '') == 'news':
+ published_date = datetime.fromtimestamp(result['date'], None)
+ media = result.get('media', [])
+ if len(media) > 0:
+ img_src = media[0].get('pict', {}).get('url', None)
+ else:
+ img_src = None
+ results.append({'url': res_url,
+ 'title': title,
+ 'publishedDate': published_date,
+ 'content': content,
+ 'img_src': img_src})
+
+ return results
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+ # list of regions is embedded in page as a js object
+ response_text = resp.text
+ response_text = response_text[response_text.find('regionalisation'):]
+ response_text = response_text[response_text.find('{'):response_text.find(');')]
+
+ regions_json = loads(response_text)
+
+ supported_languages = []
+ for lang in regions_json['languages'].values():
+ if lang['code'] == 'nb':
+ lang['code'] = 'no'
+ for country in lang['countries']:
+ supported_languages.append(lang['code'] + '-' + country)
+
+ return supported_languages
diff --git a/searx/engines/reddit.py b/searx/engines/reddit.py
new file mode 100644
index 0000000..d197249
--- /dev/null
+++ b/searx/engines/reddit.py
@@ -0,0 +1,76 @@
+"""
+ Reddit
+
+ @website https://www.reddit.com/
+ @provide-api yes (https://www.reddit.com/dev/api)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content, thumbnail, publishedDate
+"""
+
+import json
+from datetime import datetime
+from searx.url_utils import urlencode, urljoin, urlparse
+
+# engine dependent config
+categories = ['general', 'images', 'news', 'social media']
+page_size = 25
+
+# search-url
+base_url = 'https://www.reddit.com/'
+search_url = base_url + 'search.json?{query}'
+
+
+# do search-request
+def request(query, params):
+ query = urlencode({'q': query, 'limit': page_size})
+ params['url'] = search_url.format(query=query)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ img_results = []
+ text_results = []
+
+ search_results = json.loads(resp.text)
+
+ # return empty array if there are no results
+ if 'data' not in search_results:
+ return []
+
+ posts = search_results.get('data', {}).get('children', [])
+
+ # process results
+ for post in posts:
+ data = post['data']
+
+ # extract post information
+ params = {
+ 'url': urljoin(base_url, data['permalink']),
+ 'title': data['title']
+ }
+
+ # if thumbnail field contains a valid URL, we need to change template
+ thumbnail = data['thumbnail']
+ url_info = urlparse(thumbnail)
+ # netloc & path
+ if url_info[1] != '' and url_info[2] != '':
+ params['img_src'] = data['url']
+ params['thumbnail_src'] = thumbnail
+ params['template'] = 'images.html'
+ img_results.append(params)
+ else:
+ created = datetime.fromtimestamp(data['created_utc'])
+ content = data['selftext']
+ if len(content) > 500:
+ content = content[:500] + '...'
+ params['content'] = content
+ params['publishedDate'] = created
+ text_results.append(params)
+
+ # show images first and text results second
+ return img_results + text_results
diff --git a/searx/engines/scanr_structures.py b/searx/engines/scanr_structures.py
new file mode 100644
index 0000000..72fd2b3
--- /dev/null
+++ b/searx/engines/scanr_structures.py
@@ -0,0 +1,76 @@
+"""
+ ScanR Structures (Science)
+
+ @website https://scanr.enseignementsup-recherche.gouv.fr
+ @provide-api yes (https://scanr.enseignementsup-recherche.gouv.fr/api/swagger-ui.html)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content, img_src
+"""
+
+from json import loads, dumps
+from searx.utils import html_to_text
+
+# engine dependent config
+categories = ['science']
+paging = True
+page_size = 20
+
+# search-url
+url = 'https://scanr.enseignementsup-recherche.gouv.fr/'
+search_url = url + 'api/structures/search'
+
+
+# do search-request
+def request(query, params):
+
+ params['url'] = search_url
+ params['method'] = 'POST'
+ params['headers']['Content-type'] = "application/json"
+ params['data'] = dumps({"query": query,
+ "searchField": "ALL",
+ "sortDirection": "ASC",
+ "sortOrder": "RELEVANCY",
+ "page": params['pageno'],
+ "pageSize": page_size})
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_res = loads(resp.text)
+
+ # return empty array if there are no results
+ if search_res.get('total', 0) < 1:
+ return []
+
+ # parse results
+ for result in search_res['results']:
+ if 'id' not in result:
+ continue
+
+ # is it thumbnail or img_src??
+ thumbnail = None
+ if 'logo' in result:
+ thumbnail = result['logo']
+ if thumbnail[0] == '/':
+ thumbnail = url + thumbnail
+
+ content = None
+ if 'highlights' in result:
+ content = result['highlights'][0]['value']
+
+ # append result
+ results.append({'url': url + 'structure/' + result['id'],
+ 'title': result['label'],
+ # 'thumbnail': thumbnail,
+ 'img_src': thumbnail,
+ 'content': html_to_text(content)})
+
+ # return results
+ return results
diff --git a/searx/engines/searchcode_code.py b/searx/engines/searchcode_code.py
new file mode 100644
index 0000000..789e8e7
--- /dev/null
+++ b/searx/engines/searchcode_code.py
@@ -0,0 +1,69 @@
+"""
+ Searchcode (It)
+
+ @website https://searchcode.com/
+ @provide-api yes (https://searchcode.com/api/)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content
+"""
+
+from json import loads
+from searx.url_utils import urlencode
+
+
+# engine dependent config
+categories = ['it']
+paging = True
+
+# search-url
+url = 'https://searchcode.com/'
+search_url = url + 'api/codesearch_I/?{query}&p={pageno}'
+
+# special code-endings which are not recognised by the file ending
+code_endings = {'cs': 'c#',
+ 'h': 'c',
+ 'hpp': 'cpp',
+ 'cxx': 'cpp'}
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(query=urlencode({'q': query}), pageno=params['pageno'] - 1)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_results = loads(resp.text)
+
+ # parse results
+ for result in search_results.get('results', []):
+ href = result['url']
+ title = "" + result['name'] + " - " + result['filename']
+ repo = result['repo']
+
+ lines = dict()
+ for line, code in result['lines'].items():
+ lines[int(line)] = code
+
+ code_language = code_endings.get(
+ result['filename'].split('.')[-1].lower(),
+ result['filename'].split('.')[-1].lower())
+
+ # append result
+ results.append({'url': href,
+ 'title': title,
+ 'content': '',
+ 'repository': repo,
+ 'codelines': sorted(lines.items()),
+ 'code_language': code_language,
+ 'template': 'code.html'})
+
+ # return results
+ return results
diff --git a/searx/engines/searchcode_doc.py b/searx/engines/searchcode_doc.py
new file mode 100644
index 0000000..4b8e9a8
--- /dev/null
+++ b/searx/engines/searchcode_doc.py
@@ -0,0 +1,49 @@
+"""
+ Searchcode (It)
+
+ @website https://searchcode.com/
+ @provide-api yes (https://searchcode.com/api/)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content
+"""
+
+from json import loads
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['it']
+paging = True
+
+# search-url
+url = 'https://searchcode.com/'
+search_url = url + 'api/search_IV/?{query}&p={pageno}'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(query=urlencode({'q': query}), pageno=params['pageno'] - 1)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_results = loads(resp.text)
+
+ # parse results
+ for result in search_results.get('results', []):
+ href = result['url']
+ title = "[{}] {} {}".format(result['type'], result['namespace'], result['name'])
+
+ # append result
+ results.append({'url': href,
+ 'title': title,
+ 'content': result['description']})
+
+ # return results
+ return results
diff --git a/searx/engines/searx_engine.py b/searx/engines/searx_engine.py
new file mode 100644
index 0000000..91c2644
--- /dev/null
+++ b/searx/engines/searx_engine.py
@@ -0,0 +1,57 @@
+"""
+ Searx (all)
+
+ @website https://github.com/asciimoo/searx
+ @provide-api yes (https://asciimoo.ithub.io/searx/dev/search_api.html)
+
+ @using-api yes
+ @results JSON
+ @stable yes (using api)
+ @parse url, title, content
+"""
+
+from json import loads
+from searx.engines import categories as searx_categories
+
+
+categories = searx_categories.keys()
+
+# search-url
+instance_urls = []
+instance_index = 0
+
+
+# do search-request
+def request(query, params):
+ global instance_index
+ params['url'] = instance_urls[instance_index % len(instance_urls)]
+ params['method'] = 'POST'
+
+ instance_index += 1
+
+ params['data'] = {
+ 'q': query,
+ 'pageno': params['pageno'],
+ 'language': params['language'],
+ 'time_range': params['time_range'],
+ 'category': params['category'],
+ 'format': 'json'
+ }
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+
+ response_json = loads(resp.text)
+ results = response_json['results']
+
+ for i in ('answers', 'infoboxes'):
+ results.extend(response_json[i])
+
+ results.extend({'suggestion': s} for s in response_json['suggestions'])
+
+ results.append({'number_of_results': response_json['number_of_results']})
+
+ return results
diff --git a/searx/engines/seedpeer.py b/searx/engines/seedpeer.py
new file mode 100644
index 0000000..3770dac
--- /dev/null
+++ b/searx/engines/seedpeer.py
@@ -0,0 +1,75 @@
+# Seedpeer (Videos, Music, Files)
+#
+# @website http://seedpeer.eu
+# @provide-api no (nothing found)
+#
+# @using-api no
+# @results HTML (using search portal)
+# @stable yes (HTML can change)
+# @parse url, title, content, seed, leech, magnetlink
+
+from lxml import html
+from operator import itemgetter
+from searx.url_utils import quote, urljoin
+
+
+url = 'http://www.seedpeer.eu/'
+search_url = url + 'search/{search_term}/7/{page_no}.html'
+# specific xpath variables
+torrent_xpath = '//*[@id="body"]/center/center/table[2]/tr/td/a'
+alternative_torrent_xpath = '//*[@id="body"]/center/center/table[1]/tr/td/a'
+title_xpath = '//*[@id="body"]/center/center/table[2]/tr/td/a/text()'
+alternative_title_xpath = '//*[@id="body"]/center/center/table/tr/td/a'
+seeds_xpath = '//*[@id="body"]/center/center/table[2]/tr/td[4]/font/text()'
+alternative_seeds_xpath = '//*[@id="body"]/center/center/table/tr/td[4]/font/text()'
+peers_xpath = '//*[@id="body"]/center/center/table[2]/tr/td[5]/font/text()'
+alternative_peers_xpath = '//*[@id="body"]/center/center/table/tr/td[5]/font/text()'
+age_xpath = '//*[@id="body"]/center/center/table[2]/tr/td[2]/text()'
+alternative_age_xpath = '//*[@id="body"]/center/center/table/tr/td[2]/text()'
+size_xpath = '//*[@id="body"]/center/center/table[2]/tr/td[3]/text()'
+alternative_size_xpath = '//*[@id="body"]/center/center/table/tr/td[3]/text()'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(search_term=quote(query),
+ page_no=params['pageno'] - 1)
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+ dom = html.fromstring(resp.text)
+ torrent_links = dom.xpath(torrent_xpath)
+ if len(torrent_links) > 0:
+ seeds = dom.xpath(seeds_xpath)
+ peers = dom.xpath(peers_xpath)
+ titles = dom.xpath(title_xpath)
+ sizes = dom.xpath(size_xpath)
+ ages = dom.xpath(age_xpath)
+ else: # under ~5 results uses a different xpath
+ torrent_links = dom.xpath(alternative_torrent_xpath)
+ seeds = dom.xpath(alternative_seeds_xpath)
+ peers = dom.xpath(alternative_peers_xpath)
+ titles = dom.xpath(alternative_title_xpath)
+ sizes = dom.xpath(alternative_size_xpath)
+ ages = dom.xpath(alternative_age_xpath)
+ # return empty array if nothing is found
+ if not torrent_links:
+ return []
+
+ # parse results
+ for index, result in enumerate(torrent_links):
+ link = result.attrib.get('href')
+ href = urljoin(url, link)
+ results.append({'url': href,
+ 'title': titles[index].text_content(),
+ 'content': '{}, {}'.format(sizes[index], ages[index]),
+ 'seed': seeds[index],
+ 'leech': peers[index],
+
+ 'template': 'torrent.html'})
+
+ # return results sorted by seeder
+ return sorted(results, key=itemgetter('seed'), reverse=True)
diff --git a/searx/engines/soundcloud.py b/searx/engines/soundcloud.py
new file mode 100644
index 0000000..41b40da
--- /dev/null
+++ b/searx/engines/soundcloud.py
@@ -0,0 +1,104 @@
+"""
+ Soundcloud (Music)
+
+ @website https://soundcloud.com
+ @provide-api yes (https://developers.soundcloud.com/)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content, publishedDate, embedded
+"""
+
+import re
+from json import loads
+from lxml import html
+from dateutil import parser
+from searx import logger
+from searx.poolrequests import get as http_get
+from searx.url_utils import quote_plus, urlencode
+
+try:
+ from cStringIO import StringIO
+except:
+ from io import StringIO
+
+# engine dependent config
+categories = ['music']
+paging = True
+
+# search-url
+url = 'https://api.soundcloud.com/'
+search_url = url + 'search?{query}'\
+ '&facet=model'\
+ '&limit=20'\
+ '&offset={offset}'\
+ '&linked_partitioning=1'\
+ '&client_id={client_id}' # noqa
+
+embedded_url = '<iframe width="100%" height="166" ' +\
+ 'scrolling="no" frameborder="no" ' +\
+ 'data-src="https://w.soundcloud.com/player/?url={uri}"></iframe>'
+
+cid_re = re.compile(r'client_id:"([^"]*)"', re.I | re.U)
+
+
+def get_client_id():
+ response = http_get("https://soundcloud.com")
+
+ if response.ok:
+ tree = html.fromstring(response.content)
+ script_tags = tree.xpath("//script[contains(@src, '/assets/app')]")
+ app_js_urls = [script_tag.get('src') for script_tag in script_tags if script_tag is not None]
+
+ # extracts valid app_js urls from soundcloud.com content
+ for app_js_url in app_js_urls:
+ # gets app_js and searches for the clientid
+ response = http_get(app_js_url)
+ if response.ok:
+ cids = cid_re.search(response.text)
+ if cids is not None and len(cids.groups()):
+ return cids.groups()[0]
+ logger.warning("Unable to fetch guest client_id from SoundCloud, check parser!")
+ return ""
+
+
+# api-key
+guest_client_id = get_client_id()
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 20
+
+ params['url'] = search_url.format(query=urlencode({'q': query}),
+ offset=offset,
+ client_id=guest_client_id)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_res = loads(resp.text)
+
+ # parse results
+ for result in search_res.get('collection', []):
+ if result['kind'] in ('track', 'playlist'):
+ title = result['title']
+ content = result['description']
+ publishedDate = parser.parse(result['last_modified'])
+ uri = quote_plus(result['uri'])
+ embedded = embedded_url.format(uri=uri)
+
+ # append result
+ results.append({'url': result['permalink_url'],
+ 'title': title,
+ 'publishedDate': publishedDate,
+ 'embedded': embedded,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/spotify.py b/searx/engines/spotify.py
new file mode 100644
index 0000000..aed756b
--- /dev/null
+++ b/searx/engines/spotify.py
@@ -0,0 +1,62 @@
+"""
+ Spotify (Music)
+
+ @website https://spotify.com
+ @provide-api yes (https://developer.spotify.com/web-api/search-item/)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content, embedded
+"""
+
+from json import loads
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['music']
+paging = True
+
+# search-url
+url = 'https://api.spotify.com/'
+search_url = url + 'v1/search?{query}&type=track&offset={offset}'
+
+embedded_url = '<iframe data-src="https://embed.spotify.com/?uri=spotify:track:{audioid}"\
+ width="300" height="80" frameborder="0" allowtransparency="true"></iframe>'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 20
+
+ params['url'] = search_url.format(query=urlencode({'q': query}), offset=offset)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_res = loads(resp.text)
+
+ # parse results
+ for result in search_res.get('tracks', {}).get('items', {}):
+ if result['type'] == 'track':
+ title = result['name']
+ url = result['external_urls']['spotify']
+ content = u'{} - {} - {}'.format(
+ result['artists'][0]['name'],
+ result['album']['name'],
+ result['name'])
+
+ embedded = embedded_url.format(audioid=result['id'])
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'embedded': embedded,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/stackoverflow.py b/searx/engines/stackoverflow.py
new file mode 100644
index 0000000..25875aa
--- /dev/null
+++ b/searx/engines/stackoverflow.py
@@ -0,0 +1,57 @@
+"""
+ Stackoverflow (It)
+
+ @website https://stackoverflow.com/
+ @provide-api not clear (https://api.stackexchange.com/docs/advanced-search)
+
+ @using-api no
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title, content
+"""
+
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode, urljoin
+
+# engine dependent config
+categories = ['it']
+paging = True
+
+# search-url
+url = 'https://stackoverflow.com/'
+search_url = url + 'search?{query}&page={pageno}'
+
+# specific xpath variables
+results_xpath = '//div[contains(@class,"question-summary")]'
+link_xpath = './/div[@class="result-link"]//a|.//div[@class="summary"]//h3//a'
+content_xpath = './/div[@class="excerpt"]'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(query=urlencode({'q': query}), pageno=params['pageno'])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ link = result.xpath(link_xpath)[0]
+ href = urljoin(url, link.attrib.get('href'))
+ title = extract_text(link)
+ content = extract_text(result.xpath(content_xpath))
+
+ # append result
+ results.append({'url': href,
+ 'title': title,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/startpage.py b/searx/engines/startpage.py
new file mode 100644
index 0000000..314b7b9
--- /dev/null
+++ b/searx/engines/startpage.py
@@ -0,0 +1,123 @@
+# Startpage (Web)
+#
+# @website https://startpage.com
+# @provide-api no (nothing found)
+#
+# @using-api no
+# @results HTML
+# @stable no (HTML can change)
+# @parse url, title, content
+#
+# @todo paging
+
+from lxml import html
+from dateutil import parser
+from datetime import datetime, timedelta
+import re
+from searx.engines.xpath import extract_text
+
+# engine dependent config
+categories = ['general']
+# there is a mechanism to block "bot" search
+# (probably the parameter qid), require
+# storing of qid's between mulitble search-calls
+
+# paging = False
+language_support = True
+
+# search-url
+base_url = 'https://startpage.com/'
+search_url = base_url + 'do/search'
+
+# specific xpath variables
+# ads xpath //div[@id="results"]/div[@id="sponsored"]//div[@class="result"]
+# not ads: div[@class="result"] are the direct childs of div[@id="results"]
+results_xpath = '//div[@class="result"]'
+link_xpath = './/h3/a'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 10
+
+ params['url'] = search_url
+ params['method'] = 'POST'
+ params['data'] = {'query': query,
+ 'startat': offset}
+
+ # set language if specified
+ if params['language'] != 'all':
+ params['data']['with_language'] = ('lang_' + params['language'].split('-')[0])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ links = result.xpath(link_xpath)
+ if not links:
+ continue
+ link = links[0]
+ url = link.attrib.get('href')
+
+ # block google-ad url's
+ if re.match(r"^http(s|)://(www\.)?google\.[a-z]+/aclk.*$", url):
+ continue
+
+ # block startpage search url's
+ if re.match(r"^http(s|)://(www\.)?startpage\.com/do/search\?.*$", url):
+ continue
+
+ # block ixquick search url's
+ if re.match(r"^http(s|)://(www\.)?ixquick\.com/do/search\?.*$", url):
+ continue
+
+ title = extract_text(link)
+
+ if result.xpath('./p[@class="desc clk"]'):
+ content = extract_text(result.xpath('./p[@class="desc clk"]'))
+ else:
+ content = ''
+
+ published_date = None
+
+ # check if search result starts with something like: "2 Sep 2014 ... "
+ if re.match(r"^([1-9]|[1-2][0-9]|3[0-1]) [A-Z][a-z]{2} [0-9]{4} \.\.\. ", content):
+ date_pos = content.find('...') + 4
+ date_string = content[0:date_pos - 5]
+ published_date = parser.parse(date_string, dayfirst=True)
+
+ # fix content string
+ content = content[date_pos:]
+
+ # check if search result starts with something like: "5 days ago ... "
+ elif re.match(r"^[0-9]+ days? ago \.\.\. ", content):
+ date_pos = content.find('...') + 4
+ date_string = content[0:date_pos - 5]
+
+ # calculate datetime
+ published_date = datetime.now() - timedelta(days=int(re.match(r'\d+', date_string).group()))
+
+ # fix content string
+ content = content[date_pos:]
+
+ if published_date:
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'publishedDate': published_date})
+ else:
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/subtitleseeker.py b/searx/engines/subtitleseeker.py
new file mode 100644
index 0000000..2cbc991
--- /dev/null
+++ b/searx/engines/subtitleseeker.py
@@ -0,0 +1,86 @@
+"""
+ Subtitleseeker (Video)
+
+ @website http://www.subtitleseeker.com
+ @provide-api no
+
+ @using-api no
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title, content
+"""
+
+from lxml import html
+from searx.languages import language_codes
+from searx.engines.xpath import extract_text
+from searx.url_utils import quote_plus
+
+# engine dependent config
+categories = ['videos']
+paging = True
+language = ""
+
+# search-url
+url = 'http://www.subtitleseeker.com/'
+search_url = url + 'search/TITLES/{query}?p={pageno}'
+
+# specific xpath variables
+results_xpath = '//div[@class="boxRows"]'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(query=quote_plus(query),
+ pageno=params['pageno'])
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ search_lang = ""
+
+ # dirty fix for languages named differenly in their site
+ if resp.search_params['language'][:2] == 'fa':
+ search_lang = 'Farsi'
+ elif resp.search_params['language'] == 'pt-BR':
+ search_lang = 'Brazilian'
+ elif resp.search_params['language'] != 'all':
+ search_lang = [lc[3]
+ for lc in language_codes
+ if lc[0].split('-')[0] == resp.search_params['language'].split('-')[0]]
+ search_lang = search_lang[0].split(' (')[0]
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ link = result.xpath(".//a")[0]
+ href = link.attrib.get('href')
+
+ if language is not "":
+ href = href + language + '/'
+ elif search_lang:
+ href = href + search_lang + '/'
+
+ title = extract_text(link)
+
+ content = extract_text(result.xpath('.//div[contains(@class,"red")]'))
+ content = content + " - "
+ text = extract_text(result.xpath('.//div[contains(@class,"grey-web")]')[0])
+ content = content + text
+
+ if result.xpath(".//span") != []:
+ content = content +\
+ " - (" +\
+ extract_text(result.xpath(".//span")) +\
+ ")"
+
+ # append result
+ results.append({'url': href,
+ 'title': title,
+ 'content': content})
+
+ # return results
+ return results
diff --git a/searx/engines/swisscows.py b/searx/engines/swisscows.py
new file mode 100644
index 0000000..e9c13ca
--- /dev/null
+++ b/searx/engines/swisscows.py
@@ -0,0 +1,126 @@
+"""
+ Swisscows (Web, Images)
+
+ @website https://swisscows.ch
+ @provide-api no
+
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content
+"""
+
+from json import loads
+import re
+from lxml.html import fromstring
+from searx.url_utils import unquote, urlencode
+
+# engine dependent config
+categories = ['general', 'images']
+paging = True
+language_support = True
+
+# search-url
+base_url = 'https://swisscows.ch/'
+search_string = '?{query}&page={page}'
+
+supported_languages_url = base_url
+
+# regex
+regex_json = re.compile(b'initialData: {"Request":(.|\n)*},\s*environment')
+regex_json_remove_start = re.compile(b'^initialData:\s*')
+regex_json_remove_end = re.compile(b',\s*environment$')
+regex_img_url_remove_start = re.compile(b'^https?://i\.swisscows\.ch/\?link=')
+
+
+# do search-request
+def request(query, params):
+ if params['language'] == 'all':
+ ui_language = 'browser'
+ region = 'browser'
+ elif params['language'].split('-')[0] == 'no':
+ region = 'nb-NO'
+ else:
+ region = params['language']
+ ui_language = params['language'].split('-')[0]
+
+ search_path = search_string.format(
+ query=urlencode({'query': query, 'uiLanguage': ui_language, 'region': region}),
+ page=params['pageno']
+ )
+
+ # image search query is something like 'image?{query}&page={page}'
+ if params['category'] == 'images':
+ search_path = 'image' + search_path
+
+ params['url'] = base_url + search_path
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ json_regex = regex_json.search(resp.text)
+
+ # check if results are returned
+ if not json_regex:
+ return []
+
+ json_raw = regex_json_remove_end.sub(b'', regex_json_remove_start.sub(b'', json_regex.group()))
+ json = loads(json_raw.decode('utf-8'))
+
+ # parse results
+ for result in json['Results'].get('items', []):
+ result_title = result['Title'].replace(u'\uE000', '').replace(u'\uE001', '')
+
+ # parse image results
+ if result.get('ContentType', '').startswith('image'):
+ img_url = unquote(regex_img_url_remove_start.sub(b'', result['Url'].encode('utf-8')).decode('utf-8'))
+
+ # append result
+ results.append({'url': result['SourceUrl'],
+ 'title': result['Title'],
+ 'content': '',
+ 'img_src': img_url,
+ 'template': 'images.html'})
+
+ # parse general results
+ else:
+ result_url = result['Url'].replace(u'\uE000', '').replace(u'\uE001', '')
+ result_content = result['Description'].replace(u'\uE000', '').replace(u'\uE001', '')
+
+ # append result
+ results.append({'url': result_url,
+ 'title': result_title,
+ 'content': result_content})
+
+ # parse images
+ for result in json.get('Images', []):
+ # decode image url
+ img_url = unquote(regex_img_url_remove_start.sub(b'', result['Url'].encode('utf-8')).decode('utf-8'))
+
+ # append result
+ results.append({'url': result['SourceUrl'],
+ 'title': result['Title'],
+ 'content': '',
+ 'img_src': img_url,
+ 'template': 'images.html'})
+
+ # return results
+ return results
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+ supported_languages = []
+ dom = fromstring(resp.text)
+ options = dom.xpath('//div[@id="regions-popup"]//ul/li/a')
+ for option in options:
+ code = option.xpath('./@data-val')[0]
+ if code.startswith('nb-'):
+ code = code.replace('nb', 'no', 1)
+ supported_languages.append(code)
+
+ return supported_languages
diff --git a/searx/engines/tokyotoshokan.py b/searx/engines/tokyotoshokan.py
new file mode 100644
index 0000000..9a6b5e5
--- /dev/null
+++ b/searx/engines/tokyotoshokan.py
@@ -0,0 +1,100 @@
+"""
+ Tokyo Toshokan (A BitTorrent Library for Japanese Media)
+
+ @website https://www.tokyotosho.info/
+ @provide-api no
+ @using-api no
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title, publishedDate, seed, leech,
+ filesize, magnetlink, content
+"""
+
+import re
+from lxml import html
+from searx.engines.xpath import extract_text
+from datetime import datetime
+from searx.engines.nyaa import int_or_zero, get_filesize_mul
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['files', 'videos', 'music']
+paging = True
+
+# search-url
+base_url = 'https://www.tokyotosho.info/'
+search_url = base_url + 'search.php?{query}'
+
+
+# do search-request
+def request(query, params):
+ query = urlencode({'page': params['pageno'], 'terms': query})
+ params['url'] = search_url.format(query=query)
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+ rows = dom.xpath('//table[@class="listing"]//tr[contains(@class, "category_0")]')
+
+ # check if there are no results or page layout was changed so we cannot parse it
+ # currently there are two rows for each result, so total count must be even
+ if len(rows) == 0 or len(rows) % 2 != 0:
+ return []
+
+ # regular expression for parsing torrent size strings
+ size_re = re.compile(r'Size:\s*([\d.]+)(TB|GB|MB|B)', re.IGNORECASE)
+
+ # processing the results, two rows at a time
+ for i in range(0, len(rows), 2):
+ # parse the first row
+ name_row = rows[i]
+
+ links = name_row.xpath('./td[@class="desc-top"]/a')
+ params = {
+ 'template': 'torrent.html',
+ 'url': links[-1].attrib.get('href'),
+ 'title': extract_text(links[-1])
+ }
+ # I have not yet seen any torrents without magnet links, but
+ # it's better to be prepared to stumble upon one some day
+ if len(links) == 2:
+ magnet = links[0].attrib.get('href')
+ if magnet.startswith('magnet'):
+ # okay, we have a valid magnet link, let's add it to the result
+ params['magnetlink'] = magnet
+
+ # no more info in the first row, start parsing the second one
+ info_row = rows[i + 1]
+ desc = extract_text(info_row.xpath('./td[@class="desc-bot"]')[0])
+ for item in desc.split('|'):
+ item = item.strip()
+ if item.startswith('Size:'):
+ try:
+ # ('1.228', 'GB')
+ groups = size_re.match(item).groups()
+ multiplier = get_filesize_mul(groups[1])
+ params['filesize'] = int(multiplier * float(groups[0]))
+ except:
+ pass
+ elif item.startswith('Date:'):
+ try:
+ # Date: 2016-02-21 21:44 UTC
+ date = datetime.strptime(item, 'Date: %Y-%m-%d %H:%M UTC')
+ params['publishedDate'] = date
+ except:
+ pass
+ elif item.startswith('Comment:'):
+ params['content'] = item
+ stats = info_row.xpath('./td[@class="stats"]/span')
+ # has the layout not changed yet?
+ if len(stats) == 3:
+ params['seed'] = int_or_zero(extract_text(stats[0]))
+ params['leech'] = int_or_zero(extract_text(stats[1]))
+
+ results.append(params)
+
+ return results
diff --git a/searx/engines/torrentz.py b/searx/engines/torrentz.py
new file mode 100644
index 0000000..dda56fc
--- /dev/null
+++ b/searx/engines/torrentz.py
@@ -0,0 +1,92 @@
+"""
+ Torrentz.eu (BitTorrent meta-search engine)
+
+ @website https://torrentz.eu/
+ @provide-api no
+
+ @using-api no
+ @results HTML
+ @stable no (HTML can change, although unlikely,
+ see https://torrentz.eu/torrentz.btsearch)
+ @parse url, title, publishedDate, seed, leech, filesize, magnetlink
+"""
+
+import re
+from lxml import html
+from datetime import datetime
+from searx.engines.nyaa import int_or_zero, get_filesize_mul
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['files', 'videos', 'music']
+paging = True
+
+# search-url
+# https://torrentz.eu/search?f=EXAMPLE&p=6
+base_url = 'https://torrentz.eu/'
+search_url = base_url + 'search?{query}'
+
+
+# do search-request
+def request(query, params):
+ page = params['pageno'] - 1
+ query = urlencode({'q': query, 'p': page})
+ params['url'] = search_url.format(query=query)
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ for result in dom.xpath('//div[@class="results"]/dl'):
+ name_cell = result.xpath('./dt')[0]
+ title = extract_text(name_cell)
+
+ # skip rows that do not contain a link to a torrent
+ links = name_cell.xpath('./a')
+ if len(links) != 1:
+ continue
+
+ # extract url and remove a slash in the beginning
+ link = links[0].attrib.get('href').lstrip('/')
+
+ seed = result.xpath('./dd/span[@class="u"]/text()')[0].replace(',', '')
+ leech = result.xpath('./dd/span[@class="d"]/text()')[0].replace(',', '')
+
+ params = {
+ 'url': base_url + link,
+ 'title': title,
+ 'seed': int_or_zero(seed),
+ 'leech': int_or_zero(leech),
+ 'template': 'torrent.html'
+ }
+
+ # let's try to calculate the torrent size
+ try:
+ size_str = result.xpath('./dd/span[@class="s"]/text()')[0]
+ size, suffix = size_str.split()
+ params['filesize'] = int(size) * get_filesize_mul(suffix)
+ except:
+ pass
+
+ # does our link contain a valid SHA1 sum?
+ if re.compile('[0-9a-fA-F]{40}').match(link):
+ # add a magnet link to the result
+ params['magnetlink'] = 'magnet:?xt=urn:btih:' + link
+
+ # extract and convert creation date
+ try:
+ date_str = result.xpath('./dd/span[@class="a"]/span')[0].attrib.get('title')
+ # Fri, 25 Mar 2016 16:29:01
+ date = datetime.strptime(date_str, '%a, %d %b %Y %H:%M:%S')
+ params['publishedDate'] = date
+ except:
+ pass
+
+ results.append(params)
+
+ return results
diff --git a/searx/engines/translated.py b/searx/engines/translated.py
new file mode 100644
index 0000000..5c7b170
--- /dev/null
+++ b/searx/engines/translated.py
@@ -0,0 +1,68 @@
+"""
+ MyMemory Translated
+
+ @website https://mymemory.translated.net/
+ @provide-api yes (https://mymemory.translated.net/doc/spec.php)
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content
+"""
+import re
+from sys import version_info
+from searx.utils import is_valid_lang
+
+if version_info[0] == 3:
+ unicode = str
+
+categories = ['general']
+url = u'http://api.mymemory.translated.net/get?q={query}&langpair={from_lang}|{to_lang}{key}'
+web_url = u'http://mymemory.translated.net/en/{from_lang}/{to_lang}/{query}'
+weight = 100
+
+parser_re = re.compile(u'.*?([a-z]+)-([a-z]+) (.{2,})$', re.I)
+api_key = ''
+
+
+def request(query, params):
+ m = parser_re.match(unicode(query, 'utf8'))
+ if not m:
+ return params
+
+ from_lang, to_lang, query = m.groups()
+
+ from_lang = is_valid_lang(from_lang)
+ to_lang = is_valid_lang(to_lang)
+
+ if not from_lang or not to_lang:
+ return params
+
+ if api_key:
+ key_form = '&key=' + api_key
+ else:
+ key_form = ''
+ params['url'] = url.format(from_lang=from_lang[1],
+ to_lang=to_lang[1],
+ query=query,
+ key=key_form)
+ params['query'] = query
+ params['from_lang'] = from_lang
+ params['to_lang'] = to_lang
+
+ return params
+
+
+def response(resp):
+ results = []
+ results.append({
+ 'url': web_url.format(
+ from_lang=resp.search_params['from_lang'][2],
+ to_lang=resp.search_params['to_lang'][2],
+ query=resp.search_params['query']),
+ 'title': '[{0}-{1}] {2}'.format(
+ resp.search_params['from_lang'][1],
+ resp.search_params['to_lang'][1],
+ resp.search_params['query']),
+ 'content': resp.json()['responseData']['translatedText']
+ })
+ return results
diff --git a/searx/engines/twitter.py b/searx/engines/twitter.py
new file mode 100644
index 0000000..d2a8d20
--- /dev/null
+++ b/searx/engines/twitter.py
@@ -0,0 +1,87 @@
+"""
+ Twitter (Social media)
+
+ @website https://twitter.com/
+ @provide-api yes (https://dev.twitter.com/docs/using-search)
+
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content
+
+ @todo publishedDate
+"""
+
+from lxml import html
+from datetime import datetime
+from searx.engines.xpath import extract_text
+from searx.url_utils import urlencode, urljoin
+
+# engine dependent config
+categories = ['social media']
+language_support = True
+
+# search-url
+base_url = 'https://twitter.com/'
+search_url = base_url + 'search?'
+
+# specific xpath variables
+results_xpath = '//li[@data-item-type="tweet"]'
+avatar_xpath = './/img[contains(@class, "avatar")]/@src'
+link_xpath = './/small[@class="time"]//a'
+title_xpath = './/span[contains(@class, "username")]'
+content_xpath = './/p[contains(@class, "tweet-text")]'
+timestamp_xpath = './/span[contains(@class,"_timestamp")]'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url + urlencode({'q': query})
+
+ # set language if specified
+ if params['language'] != 'all':
+ params['cookies']['lang'] = params['language'].split('-')[0]
+ else:
+ params['cookies']['lang'] = 'en'
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for tweet in dom.xpath(results_xpath):
+ try:
+ link = tweet.xpath(link_xpath)[0]
+ content = extract_text(tweet.xpath(content_xpath)[0])
+ img_src = tweet.xpath(avatar_xpath)[0]
+ img_src = img_src.replace('_bigger', '_normal')
+ except Exception:
+ continue
+
+ url = urljoin(base_url, link.attrib.get('href'))
+ title = extract_text(tweet.xpath(title_xpath))
+
+ pubdate = tweet.xpath(timestamp_xpath)
+ if len(pubdate) > 0:
+ timestamp = float(pubdate[0].attrib.get('data-time'))
+ publishedDate = datetime.fromtimestamp(timestamp, None)
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'img_src': img_src,
+ 'publishedDate': publishedDate})
+ else:
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'img_src': img_src})
+
+ # return results
+ return results
diff --git a/searx/engines/vimeo.py b/searx/engines/vimeo.py
new file mode 100644
index 0000000..1408be8
--- /dev/null
+++ b/searx/engines/vimeo.py
@@ -0,0 +1,67 @@
+# Vimeo (Videos)
+#
+# @website https://vimeo.com/
+# @provide-api yes (http://developer.vimeo.com/api),
+# they have a maximum count of queries/hour
+#
+# @using-api no (TODO, rewrite to api)
+# @results HTML (using search portal)
+# @stable no (HTML can change)
+# @parse url, title, publishedDate, thumbnail, embedded
+#
+# @todo rewrite to api
+# @todo set content-parameter with correct data
+
+from json import loads
+from dateutil import parser
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['videos']
+paging = True
+
+# search-url
+base_url = 'https://vimeo.com/'
+search_url = base_url + '/search/page:{pageno}?{query}'
+
+embedded_url = '<iframe data-src="//player.vimeo.com/video/{videoid}" ' +\
+ 'width="540" height="304" frameborder="0" ' +\
+ 'webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(pageno=params['pageno'],
+ query=urlencode({'q': query}))
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+ data_start_pos = resp.text.find('{"filtered"')
+ data_end_pos = resp.text.find(';\n', data_start_pos + 1)
+ data = loads(resp.text[data_start_pos:data_end_pos])
+
+ # parse results
+ for result in data['filtered']['data']:
+ result = result[result['type']]
+ videoid = result['uri'].split('/')[-1]
+ url = base_url + videoid
+ title = result['name']
+ thumbnail = result['pictures']['sizes'][-1]['link']
+ publishedDate = parser.parse(result['created_time'])
+ embedded = embedded_url.format(videoid=videoid)
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': '',
+ 'template': 'videos.html',
+ 'publishedDate': publishedDate,
+ 'embedded': embedded,
+ 'thumbnail': thumbnail})
+
+ # return results
+ return results
diff --git a/searx/engines/wikidata.py b/searx/engines/wikidata.py
new file mode 100644
index 0000000..be21746
--- /dev/null
+++ b/searx/engines/wikidata.py
@@ -0,0 +1,488 @@
+# -*- coding: utf-8 -*-
+"""
+ Wikidata
+
+ @website https://wikidata.org
+ @provide-api yes (https://wikidata.org/w/api.php)
+
+ @using-api partially (most things require scraping)
+ @results JSON, HTML
+ @stable no (html can change)
+ @parse url, infobox
+"""
+
+from searx import logger
+from searx.poolrequests import get
+from searx.engines.xpath import extract_text
+from searx.engines.wikipedia import _fetch_supported_languages, supported_languages_url
+from searx.url_utils import urlencode
+
+from json import loads
+from lxml.html import fromstring
+
+logger = logger.getChild('wikidata')
+result_count = 1
+
+# urls
+wikidata_host = 'https://www.wikidata.org'
+url_search = wikidata_host \
+ + '/wiki/Special:ItemDisambiguation?{query}'
+
+wikidata_api = wikidata_host + '/w/api.php'
+url_detail = wikidata_api\
+ + '?action=parse&format=json&{query}'\
+ + '&redirects=1&prop=text%7Cdisplaytitle%7Clanglinks%7Crevid'\
+ + '&disableeditsection=1&disabletidy=1&preview=1&sectionpreview=1&disabletoc=1&utf8=1&formatversion=2'
+
+url_map = 'https://www.openstreetmap.org/'\
+ + '?lat={latitude}&lon={longitude}&zoom={zoom}&layers=M'
+url_image = 'https://commons.wikimedia.org/wiki/Special:FilePath/{filename}?width=500&height=400'
+
+# xpaths
+wikidata_ids_xpath = '//div/ul[@class="wikibase-disambiguation"]/li/a/@title'
+title_xpath = '//*[contains(@class,"wikibase-title-label")]'
+description_xpath = '//div[contains(@class,"wikibase-entitytermsview-heading-description")]'
+property_xpath = '//div[@id="{propertyid}"]'
+label_xpath = './/div[contains(@class,"wikibase-statementgroupview-property-label")]/a'
+url_xpath = './/a[contains(@class,"external free") or contains(@class, "wb-external-id")]'
+wikilink_xpath = './/ul[contains(@class,"wikibase-sitelinklistview-listview")]'\
+ + '/li[contains(@data-wb-siteid,"{wikiid}")]//a/@href'
+property_row_xpath = './/div[contains(@class,"wikibase-statementview")]'
+preferred_rank_xpath = './/span[contains(@class,"wikibase-rankselector-preferred")]'
+value_xpath = './/div[contains(@class,"wikibase-statementview-mainsnak")]'\
+ + '/*/div[contains(@class,"wikibase-snakview-value")]'
+language_fallback_xpath = '//sup[contains(@class,"wb-language-fallback-indicator")]'
+calendar_name_xpath = './/sup[contains(@class,"wb-calendar-name")]'
+
+
+def request(query, params):
+ language = params['language'].split('-')[0]
+ if language == 'all':
+ language = 'en'
+
+ params['url'] = url_search.format(
+ query=urlencode({'label': query, 'language': language}))
+ return params
+
+
+def response(resp):
+ results = []
+ html = fromstring(resp.text)
+ wikidata_ids = html.xpath(wikidata_ids_xpath)
+
+ language = resp.search_params['language'].split('-')[0]
+ if language == 'all':
+ language = 'en'
+
+ # TODO: make requests asynchronous to avoid timeout when result_count > 1
+ for wikidata_id in wikidata_ids[:result_count]:
+ url = url_detail.format(query=urlencode({'page': wikidata_id, 'uselang': language}))
+ htmlresponse = get(url)
+ jsonresponse = loads(htmlresponse.text)
+ results += getDetail(jsonresponse, wikidata_id, language, resp.search_params['language'])
+
+ return results
+
+
+def getDetail(jsonresponse, wikidata_id, language, locale):
+ results = []
+ urls = []
+ attributes = []
+
+ title = jsonresponse.get('parse', {}).get('displaytitle', {})
+ result = jsonresponse.get('parse', {}).get('text', {})
+
+ if not title or not result:
+ return results
+
+ title = fromstring(title)
+ for elem in title.xpath(language_fallback_xpath):
+ elem.getparent().remove(elem)
+ title = extract_text(title.xpath(title_xpath))
+
+ result = fromstring(result)
+ for elem in result.xpath(language_fallback_xpath):
+ elem.getparent().remove(elem)
+
+ description = extract_text(result.xpath(description_xpath))
+
+ # URLS
+
+ # official website
+ add_url(urls, result, 'P856', results=results)
+
+ # wikipedia
+ wikipedia_link_count = 0
+ wikipedia_link = get_wikilink(result, language + 'wiki')
+ if wikipedia_link:
+ wikipedia_link_count += 1
+ urls.append({'title': 'Wikipedia (' + language + ')',
+ 'url': wikipedia_link})
+
+ if language != 'en':
+ wikipedia_en_link = get_wikilink(result, 'enwiki')
+ if wikipedia_en_link:
+ wikipedia_link_count += 1
+ urls.append({'title': 'Wikipedia (en)',
+ 'url': wikipedia_en_link})
+
+ # TODO: get_wiki_firstlanguage
+ # if wikipedia_link_count == 0:
+
+ # more wikis
+ add_url(urls, result, default_label='Wikivoyage (' + language + ')', link_type=language + 'wikivoyage')
+ add_url(urls, result, default_label='Wikiquote (' + language + ')', link_type=language + 'wikiquote')
+ add_url(urls, result, default_label='Wikimedia Commons', link_type='commonswiki')
+
+ add_url(urls, result, 'P625', 'OpenStreetMap', link_type='geo')
+
+ # musicbrainz
+ add_url(urls, result, 'P434', 'MusicBrainz', 'http://musicbrainz.org/artist/')
+ add_url(urls, result, 'P435', 'MusicBrainz', 'http://musicbrainz.org/work/')
+ add_url(urls, result, 'P436', 'MusicBrainz', 'http://musicbrainz.org/release-group/')
+ add_url(urls, result, 'P966', 'MusicBrainz', 'http://musicbrainz.org/label/')
+
+ # IMDb
+ add_url(urls, result, 'P345', 'IMDb', 'https://www.imdb.com/', link_type='imdb')
+ # source code repository
+ add_url(urls, result, 'P1324')
+ # blog
+ add_url(urls, result, 'P1581')
+ # social media links
+ add_url(urls, result, 'P2397', 'YouTube', 'https://www.youtube.com/channel/')
+ add_url(urls, result, 'P1651', 'YouTube', 'https://www.youtube.com/watch?v=')
+ add_url(urls, result, 'P2002', 'Twitter', 'https://twitter.com/')
+ add_url(urls, result, 'P2013', 'Facebook', 'https://facebook.com/')
+ add_url(urls, result, 'P2003', 'Instagram', 'https://instagram.com/')
+
+ urls.append({'title': 'Wikidata',
+ 'url': 'https://www.wikidata.org/wiki/'
+ + wikidata_id + '?uselang=' + language})
+
+ # INFOBOX ATTRIBUTES (ROWS)
+
+ # DATES
+ # inception date
+ add_attribute(attributes, result, 'P571', date=True)
+ # dissolution date
+ add_attribute(attributes, result, 'P576', date=True)
+ # start date
+ add_attribute(attributes, result, 'P580', date=True)
+ # end date
+ add_attribute(attributes, result, 'P582', date=True)
+ # date of birth
+ add_attribute(attributes, result, 'P569', date=True)
+ # date of death
+ add_attribute(attributes, result, 'P570', date=True)
+ # date of spacecraft launch
+ add_attribute(attributes, result, 'P619', date=True)
+ # date of spacecraft landing
+ add_attribute(attributes, result, 'P620', date=True)
+
+ # nationality
+ add_attribute(attributes, result, 'P27')
+ # country of origin
+ add_attribute(attributes, result, 'P495')
+ # country
+ add_attribute(attributes, result, 'P17')
+ # headquarters
+ add_attribute(attributes, result, 'Q180')
+
+ # PLACES
+ # capital
+ add_attribute(attributes, result, 'P36', trim=True)
+ # head of state
+ add_attribute(attributes, result, 'P35', trim=True)
+ # head of government
+ add_attribute(attributes, result, 'P6', trim=True)
+ # type of government
+ add_attribute(attributes, result, 'P122')
+ # official language
+ add_attribute(attributes, result, 'P37')
+ # population
+ add_attribute(attributes, result, 'P1082', trim=True)
+ # area
+ add_attribute(attributes, result, 'P2046')
+ # currency
+ add_attribute(attributes, result, 'P38', trim=True)
+ # heigth (building)
+ add_attribute(attributes, result, 'P2048')
+
+ # MEDIA
+ # platform (videogames)
+ add_attribute(attributes, result, 'P400')
+ # author
+ add_attribute(attributes, result, 'P50')
+ # creator
+ add_attribute(attributes, result, 'P170')
+ # director
+ add_attribute(attributes, result, 'P57')
+ # performer
+ add_attribute(attributes, result, 'P175')
+ # developer
+ add_attribute(attributes, result, 'P178')
+ # producer
+ add_attribute(attributes, result, 'P162')
+ # manufacturer
+ add_attribute(attributes, result, 'P176')
+ # screenwriter
+ add_attribute(attributes, result, 'P58')
+ # production company
+ add_attribute(attributes, result, 'P272')
+ # record label
+ add_attribute(attributes, result, 'P264')
+ # publisher
+ add_attribute(attributes, result, 'P123')
+ # original network
+ add_attribute(attributes, result, 'P449')
+ # distributor
+ add_attribute(attributes, result, 'P750')
+ # composer
+ add_attribute(attributes, result, 'P86')
+ # publication date
+ add_attribute(attributes, result, 'P577', date=True)
+ # genre
+ add_attribute(attributes, result, 'P136')
+ # original language
+ add_attribute(attributes, result, 'P364')
+ # isbn
+ add_attribute(attributes, result, 'Q33057')
+ # software license
+ add_attribute(attributes, result, 'P275')
+ # programming language
+ add_attribute(attributes, result, 'P277')
+ # version
+ add_attribute(attributes, result, 'P348', trim=True)
+ # narrative location
+ add_attribute(attributes, result, 'P840')
+
+ # LANGUAGES
+ # number of speakers
+ add_attribute(attributes, result, 'P1098')
+ # writing system
+ add_attribute(attributes, result, 'P282')
+ # regulatory body
+ add_attribute(attributes, result, 'P1018')
+ # language code
+ add_attribute(attributes, result, 'P218')
+
+ # OTHER
+ # ceo
+ add_attribute(attributes, result, 'P169', trim=True)
+ # founder
+ add_attribute(attributes, result, 'P112')
+ # legal form (company/organization)
+ add_attribute(attributes, result, 'P1454')
+ # operator
+ add_attribute(attributes, result, 'P137')
+ # crew members (tripulation)
+ add_attribute(attributes, result, 'P1029')
+ # taxon
+ add_attribute(attributes, result, 'P225')
+ # chemical formula
+ add_attribute(attributes, result, 'P274')
+ # winner (sports/contests)
+ add_attribute(attributes, result, 'P1346')
+ # number of deaths
+ add_attribute(attributes, result, 'P1120')
+ # currency code
+ add_attribute(attributes, result, 'P498')
+
+ image = add_image(result)
+
+ if len(attributes) == 0 and len(urls) == 2 and len(description) == 0:
+ results.append({
+ 'url': urls[0]['url'],
+ 'title': title,
+ 'content': description
+ })
+ else:
+ results.append({
+ 'infobox': title,
+ 'id': wikipedia_link,
+ 'content': description,
+ 'img_src': image,
+ 'attributes': attributes,
+ 'urls': urls
+ })
+
+ return results
+
+
+# only returns first match
+def add_image(result):
+ # P15: route map, P242: locator map, P154: logo, P18: image, P242: map, P41: flag, P2716: collage, P2910: icon
+ property_ids = ['P15', 'P242', 'P154', 'P18', 'P242', 'P41', 'P2716', 'P2910']
+
+ for property_id in property_ids:
+ image = result.xpath(property_xpath.replace('{propertyid}', property_id))
+ if image:
+ image_name = image[0].xpath(value_xpath)
+ image_src = url_image.replace('{filename}', extract_text(image_name[0]))
+ return image_src
+
+
+# setting trim will only returned high ranked rows OR the first row
+def add_attribute(attributes, result, property_id, default_label=None, date=False, trim=False):
+ attribute = result.xpath(property_xpath.replace('{propertyid}', property_id))
+ if attribute:
+
+ if default_label:
+ label = default_label
+ else:
+ label = extract_text(attribute[0].xpath(label_xpath))
+ label = label[0].upper() + label[1:]
+
+ if date:
+ trim = True
+ # remove calendar name
+ calendar_name = attribute[0].xpath(calendar_name_xpath)
+ for calendar in calendar_name:
+ calendar.getparent().remove(calendar)
+
+ concat_values = ""
+ values = []
+ first_value = None
+ for row in attribute[0].xpath(property_row_xpath):
+ if not first_value or not trim or row.xpath(preferred_rank_xpath):
+
+ value = row.xpath(value_xpath)
+ if not value:
+ continue
+ value = extract_text(value)
+
+ # save first value in case no ranked row is found
+ if trim and not first_value:
+ first_value = value
+ else:
+ # to avoid duplicate values
+ if value not in values:
+ concat_values += value + ", "
+ values.append(value)
+
+ if trim and not values:
+ attributes.append({'label': label,
+ 'value': first_value})
+ else:
+ attributes.append({'label': label,
+ 'value': concat_values[:-2]})
+
+
+# requires property_id unless it's a wiki link (defined in link_type)
+def add_url(urls, result, property_id=None, default_label=None, url_prefix=None, results=None, link_type=None):
+ links = []
+
+ # wiki links don't have property in wikidata page
+ if link_type and 'wiki' in link_type:
+ links.append(get_wikilink(result, link_type))
+ else:
+ dom_element = result.xpath(property_xpath.replace('{propertyid}', property_id))
+ if dom_element:
+ dom_element = dom_element[0]
+ if not default_label:
+ label = extract_text(dom_element.xpath(label_xpath))
+ label = label[0].upper() + label[1:]
+
+ if link_type == 'geo':
+ links.append(get_geolink(dom_element))
+
+ elif link_type == 'imdb':
+ links.append(get_imdblink(dom_element, url_prefix))
+
+ else:
+ url_results = dom_element.xpath(url_xpath)
+ for link in url_results:
+ if link is not None:
+ if url_prefix:
+ link = url_prefix + extract_text(link)
+ else:
+ link = extract_text(link)
+ links.append(link)
+
+ # append urls
+ for url in links:
+ if url is not None:
+ urls.append({'title': default_label or label,
+ 'url': url})
+ if results is not None:
+ results.append({'title': default_label or label,
+ 'url': url})
+
+
+def get_imdblink(result, url_prefix):
+ imdb_id = result.xpath(value_xpath)
+ if imdb_id:
+ imdb_id = extract_text(imdb_id)
+ id_prefix = imdb_id[:2]
+ if id_prefix == 'tt':
+ url = url_prefix + 'title/' + imdb_id
+ elif id_prefix == 'nm':
+ url = url_prefix + 'name/' + imdb_id
+ elif id_prefix == 'ch':
+ url = url_prefix + 'character/' + imdb_id
+ elif id_prefix == 'co':
+ url = url_prefix + 'company/' + imdb_id
+ elif id_prefix == 'ev':
+ url = url_prefix + 'event/' + imdb_id
+ else:
+ url = None
+ return url
+
+
+def get_geolink(result):
+ coordinates = result.xpath(value_xpath)
+ if not coordinates:
+ return None
+ coordinates = extract_text(coordinates[0])
+ latitude, longitude = coordinates.split(',')
+
+ # convert to decimal
+ lat = int(latitude[:latitude.find(u'°')])
+ if latitude.find('\'') >= 0:
+ lat += int(latitude[latitude.find(u'°') + 1:latitude.find('\'')] or 0) / 60.0
+ if latitude.find('"') >= 0:
+ lat += float(latitude[latitude.find('\'') + 1:latitude.find('"')] or 0) / 3600.0
+ if latitude.find('S') >= 0:
+ lat *= -1
+ lon = int(longitude[:longitude.find(u'°')])
+ if longitude.find('\'') >= 0:
+ lon += int(longitude[longitude.find(u'°') + 1:longitude.find('\'')] or 0) / 60.0
+ if longitude.find('"') >= 0:
+ lon += float(longitude[longitude.find('\'') + 1:longitude.find('"')] or 0) / 3600.0
+ if longitude.find('W') >= 0:
+ lon *= -1
+
+ # TODO: get precision
+ precision = 0.0002
+ # there is no zoom information, deduce from precision (error prone)
+ # samples :
+ # 13 --> 5
+ # 1 --> 6
+ # 0.016666666666667 --> 9
+ # 0.00027777777777778 --> 19
+ # wolframalpha :
+ # quadratic fit { {13, 5}, {1, 6}, {0.0166666, 9}, {0.0002777777,19}}
+ # 14.1186-8.8322 x+0.625447 x^2
+ if precision < 0.0003:
+ zoom = 19
+ else:
+ zoom = int(15 - precision * 8.8322 + precision * precision * 0.625447)
+
+ url = url_map\
+ .replace('{latitude}', str(lat))\
+ .replace('{longitude}', str(lon))\
+ .replace('{zoom}', str(zoom))
+
+ return url
+
+
+def get_wikilink(result, wikiid):
+ url = result.xpath(wikilink_xpath.replace('{wikiid}', wikiid))
+ if not url:
+ return None
+ url = url[0]
+ if url.startswith('http://'):
+ url = url.replace('http://', 'https://')
+ elif url.startswith('//'):
+ url = 'https:' + url
+ return url
diff --git a/searx/engines/wikipedia.py b/searx/engines/wikipedia.py
new file mode 100644
index 0000000..db2fdc0
--- /dev/null
+++ b/searx/engines/wikipedia.py
@@ -0,0 +1,135 @@
+"""
+ Wikipedia (Web)
+
+ @website https://{language}.wikipedia.org
+ @provide-api yes
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, infobox
+"""
+
+from json import loads
+from lxml.html import fromstring
+from searx.url_utils import quote, urlencode
+
+# search-url
+base_url = u'https://{language}.wikipedia.org/'
+search_url = base_url + u'w/api.php?'\
+ 'action=query'\
+ '&format=json'\
+ '&{query}'\
+ '&prop=extracts|pageimages'\
+ '&exintro'\
+ '&explaintext'\
+ '&pithumbsize=300'\
+ '&redirects'
+supported_languages_url = 'https://meta.wikimedia.org/wiki/List_of_Wikipedias'
+
+
+# set language in base_url
+def url_lang(lang):
+ lang = lang.split('-')[0]
+ if lang == 'all' or lang not in supported_languages:
+ language = 'en'
+ else:
+ language = lang
+
+ return language
+
+
+# do search-request
+def request(query, params):
+ if query.islower():
+ query = u'{0}|{1}'.format(query.decode('utf-8'), query.decode('utf-8').title()).encode('utf-8')
+
+ params['url'] = search_url.format(query=urlencode({'titles': query}),
+ language=url_lang(params['language']))
+
+ return params
+
+
+# get first meaningful paragraph
+# this should filter out disambiguation pages and notes above first paragraph
+# "magic numbers" were obtained by fine tuning
+def extract_first_paragraph(content, title, image):
+ first_paragraph = None
+
+ failed_attempts = 0
+ for paragraph in content.split('\n'):
+
+ starts_with_title = paragraph.lower().find(title.lower(), 0, len(title) + 35)
+ length = len(paragraph)
+
+ if length >= 200 or (starts_with_title >= 0 and (image or length >= 150)):
+ first_paragraph = paragraph
+ break
+
+ failed_attempts += 1
+ if failed_attempts > 3:
+ return None
+
+ return first_paragraph
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_result = loads(resp.text)
+
+ # wikipedia article's unique id
+ # first valid id is assumed to be the requested article
+ for article_id in search_result['query']['pages']:
+ page = search_result['query']['pages'][article_id]
+ if int(article_id) > 0:
+ break
+
+ if int(article_id) < 0:
+ return []
+
+ title = page.get('title')
+
+ image = page.get('thumbnail')
+ if image:
+ image = image.get('source')
+
+ extract = page.get('extract')
+
+ summary = extract_first_paragraph(extract, title, image)
+
+ # link to wikipedia article
+ wikipedia_link = base_url.format(language=url_lang(resp.search_params['language'])) \
+ + 'wiki/' + quote(title.replace(' ', '_').encode('utf8'))
+
+ results.append({'url': wikipedia_link, 'title': title})
+
+ results.append({'infobox': title,
+ 'id': wikipedia_link,
+ 'content': summary,
+ 'img_src': image,
+ 'urls': [{'title': 'Wikipedia', 'url': wikipedia_link}]})
+
+ return results
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+ supported_languages = {}
+ dom = fromstring(resp.text)
+ tables = dom.xpath('//table[contains(@class,"sortable")]')
+ for table in tables:
+ # exclude header row
+ trs = table.xpath('.//tr')[1:]
+ for tr in trs:
+ td = tr.xpath('./td')
+ code = td[3].xpath('./a')[0].text
+ name = td[2].xpath('./a')[0].text
+ english_name = td[1].xpath('./a')[0].text
+ articles = int(td[4].xpath('./a/b')[0].text.replace(',', ''))
+ # exclude languages with too few articles
+ if articles >= 100:
+ supported_languages[code] = {"name": name, "english_name": english_name, "articles": articles}
+
+ return supported_languages
diff --git a/searx/engines/wolframalpha_api.py b/searx/engines/wolframalpha_api.py
new file mode 100644
index 0000000..595c6b7
--- /dev/null
+++ b/searx/engines/wolframalpha_api.py
@@ -0,0 +1,129 @@
+# Wolfram Alpha (Science)
+#
+# @website https://www.wolframalpha.com
+# @provide-api yes (https://api.wolframalpha.com/v2/)
+#
+# @using-api yes
+# @results XML
+# @stable yes
+# @parse url, infobox
+
+from lxml import etree
+from searx.url_utils import urlencode
+
+# search-url
+search_url = 'https://api.wolframalpha.com/v2/query?appid={api_key}&{query}'
+site_url = 'https://www.wolframalpha.com/input/?{query}'
+api_key = '' # defined in settings.yml
+
+# xpath variables
+failure_xpath = '/queryresult[attribute::success="false"]'
+input_xpath = '//pod[starts-with(attribute::id, "Input")]/subpod/plaintext'
+pods_xpath = '//pod'
+subpods_xpath = './subpod'
+pod_primary_xpath = './@primary'
+pod_id_xpath = './@id'
+pod_title_xpath = './@title'
+plaintext_xpath = './plaintext'
+image_xpath = './img'
+img_src_xpath = './@src'
+img_alt_xpath = './@alt'
+
+# pods to display as image in infobox
+# this pods do return a plaintext, but they look better and are more useful as images
+image_pods = {'VisualRepresentation',
+ 'Illustration'}
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(query=urlencode({'input': query}), api_key=api_key)
+ params['headers']['Referer'] = site_url.format(query=urlencode({'i': query}))
+
+ return params
+
+
+# replace private user area characters to make text legible
+def replace_pua_chars(text):
+ pua_chars = {u'\uf522': u'\u2192', # rigth arrow
+ u'\uf7b1': u'\u2115', # set of natural numbers
+ u'\uf7b4': u'\u211a', # set of rational numbers
+ u'\uf7b5': u'\u211d', # set of real numbers
+ u'\uf7bd': u'\u2124', # set of integer numbers
+ u'\uf74c': 'd', # differential
+ u'\uf74d': u'\u212f', # euler's number
+ u'\uf74e': 'i', # imaginary number
+ u'\uf7d9': '='} # equals sign
+
+ for k, v in pua_chars.items():
+ text = text.replace(k, v)
+
+ return text
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_results = etree.XML(resp.text)
+
+ # return empty array if there are no results
+ if search_results.xpath(failure_xpath):
+ return []
+
+ try:
+ infobox_title = search_results.xpath(input_xpath)[0].text
+ except:
+ infobox_title = ""
+
+ pods = search_results.xpath(pods_xpath)
+ result_chunks = []
+ result_content = ""
+ for pod in pods:
+ pod_id = pod.xpath(pod_id_xpath)[0]
+ pod_title = pod.xpath(pod_title_xpath)[0]
+ pod_is_result = pod.xpath(pod_primary_xpath)
+
+ subpods = pod.xpath(subpods_xpath)
+ if not subpods:
+ continue
+
+ # Appends either a text or an image, depending on which one is more suitable
+ for subpod in subpods:
+ content = subpod.xpath(plaintext_xpath)[0].text
+ image = subpod.xpath(image_xpath)
+
+ if content and pod_id not in image_pods:
+
+ if pod_is_result or not result_content:
+ if pod_id != "Input":
+ result_content = "%s: %s" % (pod_title, content)
+
+ # if no input pod was found, title is first plaintext pod
+ if not infobox_title:
+ infobox_title = content
+
+ content = replace_pua_chars(content)
+ result_chunks.append({'label': pod_title, 'value': content})
+
+ elif image:
+ result_chunks.append({'label': pod_title,
+ 'image': {'src': image[0].xpath(img_src_xpath)[0],
+ 'alt': image[0].xpath(img_alt_xpath)[0]}})
+
+ if not result_chunks:
+ return []
+
+ title = "Wolfram|Alpha (%s)" % infobox_title
+
+ # append infobox
+ results.append({'infobox': infobox_title,
+ 'attributes': result_chunks,
+ 'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer']}]})
+
+ # append link to site
+ results.append({'url': resp.request.headers['Referer'],
+ 'title': title,
+ 'content': result_content})
+
+ return results
diff --git a/searx/engines/wolframalpha_noapi.py b/searx/engines/wolframalpha_noapi.py
new file mode 100644
index 0000000..2a8642f
--- /dev/null
+++ b/searx/engines/wolframalpha_noapi.py
@@ -0,0 +1,120 @@
+# Wolfram|Alpha (Science)
+#
+# @website https://www.wolframalpha.com/
+# @provide-api yes (https://api.wolframalpha.com/v2/)
+#
+# @using-api no
+# @results JSON
+# @stable no
+# @parse url, infobox
+
+from json import loads
+from time import time
+
+from searx.poolrequests import get as http_get
+from searx.url_utils import urlencode
+
+# search-url
+url = 'https://www.wolframalpha.com/'
+
+search_url = url + 'input/json.jsp'\
+ '?async=false'\
+ '&banners=raw'\
+ '&debuggingdata=false'\
+ '&format=image,plaintext,imagemap,minput,moutput'\
+ '&formattimeout=2'\
+ '&{query}'\
+ '&output=JSON'\
+ '&parsetimeout=2'\
+ '&proxycode={token}'\
+ '&scantimeout=0.5'\
+ '&sponsorcategories=true'\
+ '&statemethod=deploybutton'
+
+referer_url = url + 'input/?{query}'
+
+token = {'value': '',
+ 'last_updated': None}
+
+# pods to display as image in infobox
+# this pods do return a plaintext, but they look better and are more useful as images
+image_pods = {'VisualRepresentation',
+ 'Illustration',
+ 'Symbol'}
+
+
+# seems, wolframalpha resets its token in every hour
+def obtain_token():
+ update_time = time() - (time() % 3600)
+ try:
+ token_response = http_get('https://www.wolframalpha.com/input/api/v1/code?ts=9999999999999999999', timeout=2.0)
+ token['value'] = loads(token_response.text)['code']
+ token['last_updated'] = update_time
+ except:
+ pass
+ return token
+
+
+obtain_token()
+
+
+# do search-request
+def request(query, params):
+ # obtain token if last update was more than an hour
+ if time() - (token['last_updated'] or 0) > 3600:
+ obtain_token()
+ params['url'] = search_url.format(query=urlencode({'input': query}), token=token['value'])
+ params['headers']['Referer'] = referer_url.format(query=urlencode({'i': query}))
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ resp_json = loads(resp.text)
+
+ if not resp_json['queryresult']['success']:
+ return []
+
+ # TODO handle resp_json['queryresult']['assumptions']
+ result_chunks = []
+ infobox_title = ""
+ result_content = ""
+ for pod in resp_json['queryresult']['pods']:
+ pod_id = pod.get('id', '')
+ pod_title = pod.get('title', '')
+ pod_is_result = pod.get('primary', None)
+
+ if 'subpods' not in pod:
+ continue
+
+ if pod_id == 'Input' or not infobox_title:
+ infobox_title = pod['subpods'][0]['plaintext']
+
+ for subpod in pod['subpods']:
+ if subpod['plaintext'] != '' and pod_id not in image_pods:
+ # append unless it's not an actual answer
+ if subpod['plaintext'] != '(requires interactivity)':
+ result_chunks.append({'label': pod_title, 'value': subpod['plaintext']})
+
+ if pod_is_result or not result_content:
+ if pod_id != "Input":
+ result_content = pod_title + ': ' + subpod['plaintext']
+
+ elif 'img' in subpod:
+ result_chunks.append({'label': pod_title, 'image': subpod['img']})
+
+ if not result_chunks:
+ return []
+
+ results.append({'infobox': infobox_title,
+ 'attributes': result_chunks,
+ 'urls': [{'title': 'Wolfram|Alpha', 'url': resp.request.headers['Referer']}]})
+
+ results.append({'url': resp.request.headers['Referer'],
+ 'title': 'Wolfram|Alpha (' + infobox_title + ')',
+ 'content': result_content})
+
+ return results
diff --git a/searx/engines/www1x.py b/searx/engines/www1x.py
new file mode 100644
index 0000000..5088032
--- /dev/null
+++ b/searx/engines/www1x.py
@@ -0,0 +1,81 @@
+"""
+ 1x (Images)
+
+ @website http://1x.com/
+ @provide-api no
+
+ @using-api no
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title, thumbnail, img_src, content
+"""
+
+from lxml import html
+import re
+from searx.url_utils import urlencode, urljoin
+
+# engine dependent config
+categories = ['images']
+paging = False
+
+# search-url
+base_url = 'https://1x.com'
+search_url = base_url + '/backend/search.php?{query}'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(query=urlencode({'q': query}))
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ # get links from result-text
+ regex = re.compile('(</a>|<a)')
+ results_parts = re.split(regex, resp.text)
+
+ cur_element = ''
+
+ # iterate over link parts
+ for result_part in results_parts:
+ # processed start and end of link
+ if result_part == '<a':
+ cur_element = result_part
+ continue
+ elif result_part != '</a>':
+ cur_element += result_part
+ continue
+
+ cur_element += result_part
+
+ # fix xml-error
+ cur_element = cur_element.replace('"></a>', '"/></a>')
+
+ dom = html.fromstring(cur_element)
+ link = dom.xpath('//a')[0]
+
+ url = urljoin(base_url, link.attrib.get('href'))
+ title = link.attrib.get('title', '')
+
+ thumbnail_src = urljoin(base_url, link.xpath('.//img')[0].attrib['src'])
+ # TODO: get image with higher resolution
+ img_src = thumbnail_src
+
+ # check if url is showing to a photo
+ if '/photo/' not in url:
+ continue
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'img_src': img_src,
+ 'content': '',
+ 'thumbnail_src': thumbnail_src,
+ 'template': 'images.html'})
+
+ # return results
+ return results
diff --git a/searx/engines/www500px.py b/searx/engines/www500px.py
new file mode 100644
index 0000000..7a2015a
--- /dev/null
+++ b/searx/engines/www500px.py
@@ -0,0 +1,73 @@
+"""
+ 500px (Images)
+
+ @website https://500px.com
+ @provide-api yes (https://developers.500px.com/)
+
+ @using-api no
+ @results HTML
+ @stable no (HTML can change)
+ @parse url, title, thumbnail, img_src, content
+
+ @todo rewrite to api
+"""
+
+from json import loads
+from searx.url_utils import urlencode, urljoin
+
+# engine dependent config
+categories = ['images']
+paging = True
+
+# search-url
+base_url = 'https://500px.com'
+search_url = 'https://api.500px.com/v1/photos/search?type=photos'\
+ '&{query}'\
+ '&image_size%5B%5D=4'\
+ '&image_size%5B%5D=20'\
+ '&image_size%5B%5D=21'\
+ '&image_size%5B%5D=1080'\
+ '&image_size%5B%5D=1600'\
+ '&image_size%5B%5D=2048'\
+ '&include_states=true'\
+ '&formats=jpeg%2Clytro'\
+ '&include_tags=true'\
+ '&exclude_nude=true'\
+ '&page={pageno}'\
+ '&rpp=50'\
+ '&sdk_key=b68e60cff4c929bedea36ca978830c5caca790c3'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(pageno=params['pageno'],
+ query=urlencode({'term': query}))
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ response_json = loads(resp.text)
+
+ # parse results
+ for result in response_json['photos']:
+ url = urljoin(base_url, result['url'])
+ title = result['name']
+ # last index is the biggest resolution
+ img_src = result['image_url'][-1]
+ thumbnail_src = result['image_url'][0]
+ content = result['description'] or ''
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'img_src': img_src,
+ 'content': content,
+ 'thumbnail_src': thumbnail_src,
+ 'template': 'images.html'})
+
+ # return results
+ return results
diff --git a/searx/engines/xpath.py b/searx/engines/xpath.py
new file mode 100644
index 0000000..c8c56da
--- /dev/null
+++ b/searx/engines/xpath.py
@@ -0,0 +1,122 @@
+from lxml import html
+from lxml.etree import _ElementStringResult, _ElementUnicodeResult
+from searx.utils import html_to_text
+from searx.url_utils import unquote, urlencode, urljoin, urlparse
+
+search_url = None
+url_xpath = None
+content_xpath = None
+title_xpath = None
+paging = False
+suggestion_xpath = ''
+results_xpath = ''
+
+# parameters for engines with paging support
+#
+# number of results on each page
+# (only needed if the site requires not a page number, but an offset)
+page_size = 1
+# number of the first page (usually 0 or 1)
+first_page_num = 1
+
+
+'''
+if xpath_results is list, extract the text from each result and concat the list
+if xpath_results is a xml element, extract all the text node from it
+ ( text_content() method from lxml )
+if xpath_results is a string element, then it's already done
+'''
+
+
+def extract_text(xpath_results):
+ if type(xpath_results) == list:
+ # it's list of result : concat everything using recursive call
+ result = ''
+ for e in xpath_results:
+ result = result + extract_text(e)
+ return result.strip()
+ elif type(xpath_results) in [_ElementStringResult, _ElementUnicodeResult]:
+ # it's a string
+ return ''.join(xpath_results)
+ else:
+ # it's a element
+ text = html.tostring(xpath_results, encoding='unicode', method='text', with_tail=False)
+ text = text.strip().replace('\n', ' ')
+ return ' '.join(text.split())
+
+
+def extract_url(xpath_results, search_url):
+ if xpath_results == []:
+ raise Exception('Empty url resultset')
+ url = extract_text(xpath_results)
+
+ if url.startswith('//'):
+ # add http or https to this kind of url //example.com/
+ parsed_search_url = urlparse(search_url)
+ url = u'{0}:{1}'.format(parsed_search_url.scheme, url)
+ elif url.startswith('/'):
+ # fix relative url to the search engine
+ url = urljoin(search_url, url)
+
+ # normalize url
+ url = normalize_url(url)
+
+ return url
+
+
+def normalize_url(url):
+ parsed_url = urlparse(url)
+
+ # add a / at this end of the url if there is no path
+ if not parsed_url.netloc:
+ raise Exception('Cannot parse url')
+ if not parsed_url.path:
+ url += '/'
+
+ # FIXME : hack for yahoo
+ if parsed_url.hostname == 'search.yahoo.com'\
+ and parsed_url.path.startswith('/r'):
+ p = parsed_url.path
+ mark = p.find('/**')
+ if mark != -1:
+ return unquote(p[mark + 3:]).decode('utf-8')
+
+ return url
+
+
+def request(query, params):
+ query = urlencode({'q': query})[2:]
+
+ fp = {'query': query}
+ if paging and search_url.find('{pageno}') >= 0:
+ fp['pageno'] = (params['pageno'] - 1) * page_size + first_page_num
+
+ params['url'] = search_url.format(**fp)
+ params['query'] = query
+
+ return params
+
+
+def response(resp):
+ results = []
+ dom = html.fromstring(resp.text)
+ if results_xpath:
+ for result in dom.xpath(results_xpath):
+ url = extract_url(result.xpath(url_xpath), search_url)
+ title = extract_text(result.xpath(title_xpath))
+ content = extract_text(result.xpath(content_xpath))
+ results.append({'url': url, 'title': title, 'content': content})
+ else:
+ for url, title, content in zip(
+ (extract_url(x, search_url) for
+ x in dom.xpath(url_xpath)),
+ map(extract_text, dom.xpath(title_xpath)),
+ map(extract_text, dom.xpath(content_xpath))
+ ):
+ results.append({'url': url, 'title': title, 'content': content})
+
+ if not suggestion_xpath:
+ return results
+ for suggestion in dom.xpath(suggestion_xpath):
+ results.append({'suggestion': extract_text(suggestion)})
+ return results
diff --git a/searx/engines/yacy.py b/searx/engines/yacy.py
new file mode 100644
index 0000000..a62a129
--- /dev/null
+++ b/searx/engines/yacy.py
@@ -0,0 +1,99 @@
+# Yacy (Web, Images, Videos, Music, Files)
+#
+# @website http://yacy.net
+# @provide-api yes
+# (http://www.yacy-websuche.de/wiki/index.php/Dev:APIyacysearch)
+#
+# @using-api yes
+# @results JSON
+# @stable yes
+# @parse (general) url, title, content, publishedDate
+# @parse (images) url, title, img_src
+#
+# @todo parse video, audio and file results
+
+from json import loads
+from dateutil import parser
+from searx.url_utils import urlencode
+
+from searx.utils import html_to_text
+
+# engine dependent config
+categories = ['general', 'images'] # TODO , 'music', 'videos', 'files'
+paging = True
+language_support = True
+number_of_results = 5
+
+# search-url
+base_url = 'http://localhost:8090'
+search_url = '/yacysearch.json?{query}'\
+ '&startRecord={offset}'\
+ '&maximumRecords={limit}'\
+ '&contentdom={search_type}'\
+ '&resource=global'
+
+# yacy specific type-definitions
+search_types = {'general': 'text',
+ 'images': 'image',
+ 'files': 'app',
+ 'music': 'audio',
+ 'videos': 'video'}
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * number_of_results
+ search_type = search_types.get(params.get('category'), '0')
+
+ params['url'] = base_url +\
+ search_url.format(query=urlencode({'query': query}),
+ offset=offset,
+ limit=number_of_results,
+ search_type=search_type)
+
+ # add language tag if specified
+ if params['language'] != 'all':
+ params['url'] += '&lr=lang_' + params['language'].split('-')[0]
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ raw_search_results = loads(resp.text)
+
+ # return empty array if there are no results
+ if not raw_search_results:
+ return []
+
+ search_results = raw_search_results.get('channels', [])
+
+ if len(search_results) == 0:
+ return []
+
+ for result in search_results[0].get('items', []):
+ # parse image results
+ if result.get('image'):
+ # append result
+ results.append({'url': result['url'],
+ 'title': result['title'],
+ 'content': '',
+ 'img_src': result['image'],
+ 'template': 'images.html'})
+
+ # parse general results
+ else:
+ publishedDate = parser.parse(result['pubDate'])
+
+ # append result
+ results.append({'url': result['link'],
+ 'title': result['title'],
+ 'content': html_to_text(result['description']),
+ 'publishedDate': publishedDate})
+
+ # TODO parse video, audio and file results
+
+ # return results
+ return results
diff --git a/searx/engines/yahoo.py b/searx/engines/yahoo.py
new file mode 100644
index 0000000..5387aaf
--- /dev/null
+++ b/searx/engines/yahoo.py
@@ -0,0 +1,153 @@
+"""
+ Yahoo (Web)
+
+ @website https://search.yahoo.com/web
+ @provide-api yes (https://developer.yahoo.com/boss/search/),
+ $0.80/1000 queries
+
+ @using-api no (because pricing)
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content, suggestion
+"""
+
+from lxml import html
+from searx.engines.xpath import extract_text, extract_url
+from searx.url_utils import unquote, urlencode
+
+# engine dependent config
+categories = ['general']
+paging = True
+language_support = True
+time_range_support = True
+
+# search-url
+base_url = 'https://search.yahoo.com/'
+search_url = 'search?{query}&b={offset}&fl=1&vl=lang_{lang}'
+search_url_with_time = 'search?{query}&b={offset}&fl=1&vl=lang_{lang}&age={age}&btf={btf}&fr2=time'
+
+supported_languages_url = 'https://search.yahoo.com/web/advanced'
+
+# specific xpath variables
+results_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' Sr ')]"
+url_xpath = './/h3/a/@href'
+title_xpath = './/h3/a'
+content_xpath = './/div[@class="compText aAbs"]'
+suggestion_xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' AlsoTry ')]//a"
+
+time_range_dict = {'day': ['1d', 'd'],
+ 'week': ['1w', 'w'],
+ 'month': ['1m', 'm']}
+
+
+# remove yahoo-specific tracking-url
+def parse_url(url_string):
+ endings = ['/RS', '/RK']
+ endpositions = []
+ start = url_string.find('http', url_string.find('/RU=') + 1)
+
+ for ending in endings:
+ endpos = url_string.rfind(ending)
+ if endpos > -1:
+ endpositions.append(endpos)
+
+ if start == 0 or len(endpositions) == 0:
+ return url_string
+ else:
+ end = min(endpositions)
+ return unquote(url_string[start:end])
+
+
+def _get_url(query, offset, language, time_range):
+ if time_range in time_range_dict:
+ return base_url + search_url_with_time.format(offset=offset,
+ query=urlencode({'p': query}),
+ lang=language,
+ age=time_range_dict[time_range][0],
+ btf=time_range_dict[time_range][1])
+ return base_url + search_url.format(offset=offset,
+ query=urlencode({'p': query}),
+ lang=language)
+
+
+def _get_language(params):
+ if params['language'] == 'all':
+ return 'en'
+ elif params['language'][:2] == 'zh':
+ if params['language'] == 'zh' or params['language'] == 'zh-CH':
+ return 'szh'
+ else:
+ return 'tzh'
+ else:
+ return params['language'].split('-')[0]
+
+
+# do search-request
+def request(query, params):
+ if params['time_range'] and params['time_range'] not in time_range_dict:
+ return params
+
+ offset = (params['pageno'] - 1) * 10 + 1
+ language = _get_language(params)
+
+ params['url'] = _get_url(query, offset, language, params['time_range'])
+
+ # TODO required?
+ params['cookies']['sB'] = 'fl=1&vl=lang_{lang}&sh=1&rw=new&v=1'\
+ .format(lang=language)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ try:
+ results_num = int(dom.xpath('//div[@class="compPagination"]/span[last()]/text()')[0]
+ .split()[0].replace(',', ''))
+ results.append({'number_of_results': results_num})
+ except:
+ pass
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ try:
+ url = parse_url(extract_url(result.xpath(url_xpath), search_url))
+ title = extract_text(result.xpath(title_xpath)[0])
+ except:
+ continue
+
+ content = extract_text(result.xpath(content_xpath)[0])
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content})
+
+ # if no suggestion found, return results
+ suggestions = dom.xpath(suggestion_xpath)
+ if not suggestions:
+ return results
+
+ # parse suggestion
+ for suggestion in suggestions:
+ # append suggestion
+ results.append({'suggestion': extract_text(suggestion)})
+
+ # return results
+ return results
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+ supported_languages = []
+ dom = html.fromstring(resp.text)
+ options = dom.xpath('//div[@id="yschlang"]/span/label/input')
+ for option in options:
+ code = option.xpath('./@value')[0][5:].replace('_', '-')
+ supported_languages.append(code)
+
+ return supported_languages
diff --git a/searx/engines/yahoo_news.py b/searx/engines/yahoo_news.py
new file mode 100644
index 0000000..ae54a4a
--- /dev/null
+++ b/searx/engines/yahoo_news.py
@@ -0,0 +1,107 @@
+# Yahoo (News)
+#
+# @website https://news.yahoo.com
+# @provide-api yes (https://developer.yahoo.com/boss/search/)
+# $0.80/1000 queries
+#
+# @using-api no (because pricing)
+# @results HTML (using search portal)
+# @stable no (HTML can change)
+# @parse url, title, content, publishedDate
+
+import re
+from datetime import datetime, timedelta
+from lxml import html
+from searx.engines.xpath import extract_text, extract_url
+from searx.engines.yahoo import parse_url, _fetch_supported_languages, supported_languages_url
+from dateutil import parser
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['news']
+paging = True
+language_support = True
+
+# search-url
+search_url = 'https://news.search.yahoo.com/search?{query}&b={offset}&{lang}=uh3_news_web_gs_1&pz=10&xargs=0&vl=lang_{lang}' # noqa
+
+# specific xpath variables
+results_xpath = '//ol[contains(@class,"searchCenterMiddle")]//li'
+url_xpath = './/h3/a/@href'
+title_xpath = './/h3/a'
+content_xpath = './/div[@class="compText"]'
+publishedDate_xpath = './/span[contains(@class,"tri")]'
+suggestion_xpath = '//div[contains(@class,"VerALSOTRY")]//a'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * 10 + 1
+
+ if params['language'] == 'all':
+ language = 'en'
+ else:
+ language = params['language'].split('_')[0]
+
+ params['url'] = search_url.format(offset=offset,
+ query=urlencode({'p': query}),
+ lang=language)
+
+ # TODO required?
+ params['cookies']['sB'] = '"v=1&vm=p&fl=1&vl=lang_{lang}&sh=1&pn=10&rw=new'\
+ .format(lang=language)
+ return params
+
+
+def sanitize_url(url):
+ if ".yahoo.com/" in url:
+ return re.sub(u"\\;\\_ylt\\=.+$", "", url)
+ else:
+ return url
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ urls = result.xpath(url_xpath)
+ if len(urls) != 1:
+ continue
+ url = sanitize_url(parse_url(extract_url(urls, search_url)))
+ title = extract_text(result.xpath(title_xpath)[0])
+ content = extract_text(result.xpath(content_xpath)[0])
+
+ # parse publishedDate
+ publishedDate = extract_text(result.xpath(publishedDate_xpath)[0])
+
+ # still useful ?
+ if re.match("^[0-9]+ minute(s|) ago$", publishedDate):
+ publishedDate = datetime.now() - timedelta(minutes=int(re.match(r'\d+', publishedDate).group()))
+ elif re.match("^[0-9]+ days? ago$", publishedDate):
+ publishedDate = datetime.now() - timedelta(days=int(re.match(r'\d+', publishedDate).group()))
+ elif re.match("^[0-9]+ hour(s|), [0-9]+ minute(s|) ago$", publishedDate):
+ timeNumbers = re.findall(r'\d+', publishedDate)
+ publishedDate = datetime.now()\
+ - timedelta(hours=int(timeNumbers[0]))\
+ - timedelta(minutes=int(timeNumbers[1]))
+ else:
+ try:
+ publishedDate = parser.parse(publishedDate)
+ except:
+ publishedDate = datetime.now()
+
+ if publishedDate.year == 1900:
+ publishedDate = publishedDate.replace(year=datetime.now().year)
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'publishedDate': publishedDate})
+
+ # return results
+ return results
diff --git a/searx/engines/yandex.py b/searx/engines/yandex.py
new file mode 100644
index 0000000..1c789f6
--- /dev/null
+++ b/searx/engines/yandex.py
@@ -0,0 +1,64 @@
+"""
+ Yahoo (Web)
+
+ @website https://yandex.ru/
+ @provide-api ?
+ @using-api no
+ @results HTML (using search portal)
+ @stable no (HTML can change)
+ @parse url, title, content
+"""
+
+from lxml import html
+from searx import logger
+from searx.url_utils import urlencode
+
+logger = logger.getChild('yandex engine')
+
+# engine dependent config
+categories = ['general']
+paging = True
+language_support = True # TODO
+
+default_tld = 'com'
+language_map = {'ru': 'ru',
+ 'ua': 'ua',
+ 'be': 'by',
+ 'kk': 'kz',
+ 'tr': 'com.tr'}
+
+# search-url
+base_url = 'https://yandex.{tld}/'
+search_url = 'search/?{query}&p={page}'
+
+results_xpath = '//li[@class="serp-item"]'
+url_xpath = './/h2/a/@href'
+title_xpath = './/h2/a//text()'
+content_xpath = './/div[@class="text-container typo typo_text_m typo_line_m organic__text"]//text()'
+
+
+def request(query, params):
+ lang = params['language'].split('-')[0]
+ host = base_url.format(tld=language_map.get(lang) or default_tld)
+ params['url'] = host + search_url.format(page=params['pageno'] - 1,
+ query=urlencode({'text': query}))
+ return params
+
+
+# get response from search-request
+def response(resp):
+ dom = html.fromstring(resp.text)
+ results = []
+
+ for result in dom.xpath(results_xpath):
+ try:
+ res = {'url': result.xpath(url_xpath)[0],
+ 'title': ''.join(result.xpath(title_xpath)),
+ 'content': ''.join(result.xpath(content_xpath))}
+ except:
+ logger.exception('yandex parse crash')
+ continue
+
+ results.append(res)
+
+ return results
diff --git a/searx/engines/youtube_api.py b/searx/engines/youtube_api.py
new file mode 100644
index 0000000..6de18aa
--- /dev/null
+++ b/searx/engines/youtube_api.py
@@ -0,0 +1,83 @@
+# Youtube (Videos)
+#
+# @website https://www.youtube.com/
+# @provide-api yes (https://developers.google.com/apis-explorer/#p/youtube/v3/youtube.search.list)
+#
+# @using-api yes
+# @results JSON
+# @stable yes
+# @parse url, title, content, publishedDate, thumbnail, embedded
+
+from json import loads
+from dateutil import parser
+from searx.url_utils import urlencode
+
+# engine dependent config
+categories = ['videos', 'music']
+paging = False
+language_support = True
+api_key = None
+
+# search-url
+base_url = 'https://www.googleapis.com/youtube/v3/search'
+search_url = base_url + '?part=snippet&{query}&maxResults=20&key={api_key}'
+
+embedded_url = '<iframe width="540" height="304" ' +\
+ 'data-src="//www.youtube-nocookie.com/embed/{videoid}" ' +\
+ 'frameborder="0" allowfullscreen></iframe>'
+
+base_youtube_url = 'https://www.youtube.com/watch?v='
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(query=urlencode({'q': query}),
+ api_key=api_key)
+
+ # add language tag if specified
+ if params['language'] != 'all':
+ params['url'] += '&relevanceLanguage=' + params['language'].split('-')[0]
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_results = loads(resp.text)
+
+ # return empty array if there are no results
+ if 'items' not in search_results:
+ return []
+
+ # parse results
+ for result in search_results['items']:
+ videoid = result['id']['videoId']
+
+ title = result['snippet']['title']
+ content = ''
+ thumbnail = ''
+
+ pubdate = result['snippet']['publishedAt']
+ publishedDate = parser.parse(pubdate)
+
+ thumbnail = result['snippet']['thumbnails']['high']['url']
+
+ content = result['snippet']['description']
+
+ url = base_youtube_url + videoid
+
+ embedded = embedded_url.format(videoid=videoid)
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'template': 'videos.html',
+ 'publishedDate': publishedDate,
+ 'embedded': embedded,
+ 'thumbnail': thumbnail})
+
+ # return results
+ return results
diff --git a/searx/engines/youtube_noapi.py b/searx/engines/youtube_noapi.py
new file mode 100644
index 0000000..9f01841
--- /dev/null
+++ b/searx/engines/youtube_noapi.py
@@ -0,0 +1,89 @@
+# Youtube (Videos)
+#
+# @website https://www.youtube.com/
+# @provide-api yes (https://developers.google.com/apis-explorer/#p/youtube/v3/youtube.search.list)
+#
+# @using-api no
+# @results HTML
+# @stable no
+# @parse url, title, content, publishedDate, thumbnail, embedded
+
+from lxml import html
+from searx.engines.xpath import extract_text
+from searx.utils import list_get
+from searx.url_utils import quote_plus
+
+# engine dependent config
+categories = ['videos', 'music']
+paging = True
+language_support = False
+time_range_support = True
+
+# search-url
+base_url = 'https://www.youtube.com/results'
+search_url = base_url + '?search_query={query}&page={page}'
+time_range_url = '&sp=EgII{time_range}%253D%253D'
+time_range_dict = {'day': 'Ag',
+ 'week': 'Aw',
+ 'month': 'BA',
+ 'year': 'BQ'}
+
+embedded_url = '<iframe width="540" height="304" ' +\
+ 'data-src="//www.youtube-nocookie.com/embed/{videoid}" ' +\
+ 'frameborder="0" allowfullscreen></iframe>'
+
+base_youtube_url = 'https://www.youtube.com/watch?v='
+
+# specific xpath variables
+results_xpath = "//ol/li/div[contains(@class, 'yt-lockup yt-lockup-tile yt-lockup-video vve-check')]"
+url_xpath = './/h3/a/@href'
+title_xpath = './/div[@class="yt-lockup-content"]/h3/a'
+content_xpath = './/div[@class="yt-lockup-content"]/div[@class="yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2"]'
+
+
+# returns extract_text on the first result selected by the xpath or None
+def extract_text_from_dom(result, xpath):
+ r = result.xpath(xpath)
+ if len(r) > 0:
+ return extract_text(r[0])
+ return None
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(query=quote_plus(query),
+ page=params['pageno'])
+ if params['time_range'] in time_range_dict:
+ params['url'] += time_range_url.format(time_range=time_range_dict[params['time_range']])
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ videoid = list_get(result.xpath('@data-context-item-id'), 0)
+ if videoid is not None:
+ url = base_youtube_url + videoid
+ thumbnail = 'https://i.ytimg.com/vi/' + videoid + '/hqdefault.jpg'
+
+ title = extract_text_from_dom(result, title_xpath) or videoid
+ content = extract_text_from_dom(result, content_xpath)
+
+ embedded = embedded_url.format(videoid=videoid)
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'template': 'videos.html',
+ 'embedded': embedded,
+ 'thumbnail': thumbnail})
+
+ # return results
+ return results
diff --git a/searx/exceptions.py b/searx/exceptions.py
new file mode 100644
index 0000000..c605ddc
--- /dev/null
+++ b/searx/exceptions.py
@@ -0,0 +1,32 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2017- by Alexandre Flament, <alex@al-f.net>
+'''
+
+
+class SearxException(Exception):
+ pass
+
+
+class SearxParameterException(SearxException):
+
+ def __init__(self, name, value):
+ if value == '' or value is None:
+ message = 'Empty ' + name + ' parameter'
+ else:
+ message = 'Invalid value "' + value + '" for parameter ' + name
+ super(SearxParameterException, self).__init__(message)
+ self.parameter_name = name
+ self.parameter_value = value
diff --git a/searx/languages.py b/searx/languages.py
new file mode 100644
index 0000000..22229f7
--- /dev/null
+++ b/searx/languages.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+# list of language codes
+# this file is generated automatically by utils/update_search_languages.py
+
+language_codes = (
+ (u"ar-SA", u"العربية", u"", u"Arabic"),
+ (u"bg-BG", u"Български", u"", u"Bulgarian"),
+ (u"cs-CZ", u"Čeština", u"", u"Czech"),
+ (u"da-DK", u"Dansk", u"", u"Danish"),
+ (u"de", u"Deutsch", u"", u"German"),
+ (u"de-AT", u"Deutsch", u"Österreich", u"German"),
+ (u"de-CH", u"Deutsch", u"Schweiz", u"German"),
+ (u"de-DE", u"Deutsch", u"Deutschland", u"German"),
+ (u"el-GR", u"Ελληνικά", u"", u"Greek"),
+ (u"en", u"English", u"", u"English"),
+ (u"en-AU", u"English", u"Australia", u"English"),
+ (u"en-CA", u"English", u"Canada", u"English"),
+ (u"en-CY", u"English", u"Cyprus", u"English"),
+ (u"en-GB", u"English", u"United Kingdom", u"English"),
+ (u"en-GD", u"English", u"Grenada", u"English"),
+ (u"en-ID", u"English", u"Indonesia", u"English"),
+ (u"en-IE", u"English", u"Ireland", u"English"),
+ (u"en-IN", u"English", u"India", u"English"),
+ (u"en-MY", u"English", u"Malaysia", u"English"),
+ (u"en-NZ", u"English", u"New Zealand", u"English"),
+ (u"en-PH", u"English", u"Philippines", u"English"),
+ (u"en-SG", u"English", u"Singapore", u"English"),
+ (u"en-US", u"English", u"United States", u"English"),
+ (u"en-ZA", u"English", u"South Africa", u"English"),
+ (u"es", u"Español", u"", u"Spanish"),
+ (u"es-AR", u"Español", u"Argentina", u"Spanish"),
+ (u"es-CL", u"Español", u"Chile", u"Spanish"),
+ (u"es-CO", u"Español", u"Colombia", u"Spanish"),
+ (u"es-ES", u"Español", u"España", u"Spanish"),
+ (u"es-MX", u"Español", u"México", u"Spanish"),
+ (u"es-PE", u"Español", u"Perú", u"Spanish"),
+ (u"es-US", u"Español", u"Estados Unidos", u"Spanish"),
+ (u"et-EE", u"Eesti", u"", u"Estonian"),
+ (u"fi-FI", u"Suomi", u"", u"Finnish"),
+ (u"fr", u"Français", u"", u"French"),
+ (u"fr-BE", u"Français", u"Belgique", u"French"),
+ (u"fr-CA", u"Français", u"Canada", u"French"),
+ (u"fr-CH", u"Français", u"Suisse", u"French"),
+ (u"fr-FR", u"Français", u"France", u"French"),
+ (u"he-IL", u"עברית", u"", u"Hebrew"),
+ (u"hr-HR", u"Hrvatski", u"", u"Croatian"),
+ (u"hu-HU", u"Magyar", u"", u"Hungarian"),
+ (u"id-ID", u"Bahasa Indonesia", u"", u"Indonesian"),
+ (u"it", u"Italiano", u"", u"Italian"),
+ (u"it-CH", u"Italiano", u"Svizzera", u"Italian"),
+ (u"it-IT", u"Italiano", u"Italia", u"Italian"),
+ (u"ja-JP", u"日本語", u"", u"Japanese"),
+ (u"ko-KR", u"한국어", u"", u"Korean"),
+ (u"lt-LT", u"Lietuvių", u"", u"Lithuanian"),
+ (u"lv-LV", u"Latviešu", u"", u"Latvian"),
+ (u"ms-MY", u"Bahasa Melayu", u"", u"Malay"),
+ (u"nl", u"Nederlands", u"", u"Dutch"),
+ (u"nl-BE", u"Nederlands", u"België", u"Dutch"),
+ (u"nl-NL", u"Nederlands", u"Nederland", u"Dutch"),
+ (u"no-NO", u"Norsk", u"", u"Norwegian"),
+ (u"pl-PL", u"Polski", u"", u"Polish"),
+ (u"pt", u"Português", u"", u"Portuguese"),
+ (u"pt-BR", u"Português", u"Brasil", u"Portuguese"),
+ (u"pt-PT", u"Português", u"Portugal", u"Portuguese"),
+ (u"ro-RO", u"Română", u"", u"Romanian"),
+ (u"ru-RU", u"Русский", u"", u"Russian"),
+ (u"sk-SK", u"Slovenčina", u"", u"Slovak"),
+ (u"sl", u"Slovenščina", u"", u"Slovenian"),
+ (u"sv-SE", u"Svenska", u"", u"Swedish"),
+ (u"th-TH", u"ไทย", u"", u"Thai"),
+ (u"tr-TR", u"Türkçe", u"", u"Turkish"),
+ (u"vi-VN", u"Tiếng Việt", u"", u"Vietnamese"),
+ (u"zh", u"中文", u"", u"Chinese"),
+ (u"zh-CN", u"中文", u"中国", u"Chinese"),
+ (u"zh-HK", u"中文", u"香港", u"Chinese"),
+ (u"zh-TW", u"中文", u"台湾", u"Chinese")
+)
diff --git a/searx/plugins/__init__.py b/searx/plugins/__init__.py
new file mode 100644
index 0000000..46c1f89
--- /dev/null
+++ b/searx/plugins/__init__.py
@@ -0,0 +1,88 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2015 by Adam Tauber, <asciimoo@gmail.com>
+'''
+from sys import exit, version_info
+from searx import logger
+
+if version_info[0] == 3:
+ unicode = str
+
+logger = logger.getChild('plugins')
+
+from searx.plugins import (doai_rewrite,
+ https_rewrite,
+ infinite_scroll,
+ open_results_on_new_tab,
+ self_info,
+ search_on_category_select,
+ tracker_url_remover,
+ vim_hotkeys)
+
+required_attrs = (('name', (str, unicode)),
+ ('description', (str, unicode)),
+ ('default_on', bool))
+
+optional_attrs = (('js_dependencies', tuple),
+ ('css_dependencies', tuple))
+
+
+class Plugin():
+ default_on = False
+ name = 'Default plugin'
+ description = 'Default plugin description'
+
+
+class PluginStore():
+
+ def __init__(self):
+ self.plugins = []
+
+ def __iter__(self):
+ for plugin in self.plugins:
+ yield plugin
+
+ def register(self, *plugins):
+ for plugin in plugins:
+ for plugin_attr, plugin_attr_type in required_attrs:
+ if not hasattr(plugin, plugin_attr) or not isinstance(getattr(plugin, plugin_attr), plugin_attr_type):
+ logger.critical('missing attribute "{0}", cannot load plugin: {1}'.format(plugin_attr, plugin))
+ exit(3)
+ for plugin_attr, plugin_attr_type in optional_attrs:
+ if not hasattr(plugin, plugin_attr) or not isinstance(getattr(plugin, plugin_attr), plugin_attr_type):
+ setattr(plugin, plugin_attr, plugin_attr_type())
+ plugin.id = plugin.name.replace(' ', '_')
+ self.plugins.append(plugin)
+
+ def call(self, ordered_plugin_list, plugin_type, request, *args, **kwargs):
+ ret = True
+ for plugin in ordered_plugin_list:
+ if hasattr(plugin, plugin_type):
+ ret = getattr(plugin, plugin_type)(request, *args, **kwargs)
+ if not ret:
+ break
+
+ return ret
+
+
+plugins = PluginStore()
+plugins.register(doai_rewrite)
+plugins.register(https_rewrite)
+plugins.register(infinite_scroll)
+plugins.register(open_results_on_new_tab)
+plugins.register(self_info)
+plugins.register(search_on_category_select)
+plugins.register(tracker_url_remover)
+plugins.register(vim_hotkeys)
diff --git a/searx/plugins/doai_rewrite.py b/searx/plugins/doai_rewrite.py
new file mode 100644
index 0000000..95efa8f
--- /dev/null
+++ b/searx/plugins/doai_rewrite.py
@@ -0,0 +1,31 @@
+from flask_babel import gettext
+import re
+from searx.url_utils import urlparse, parse_qsl
+
+regex = re.compile(r'10\.\d{4,9}/[^\s]+')
+
+name = gettext('DOAI rewrite')
+description = gettext('Avoid paywalls by redirecting to open-access versions of publications when available')
+default_on = False
+
+
+def extract_doi(url):
+ match = regex.search(url.path)
+ if match:
+ return match.group(0)
+ for _, v in parse_qsl(url.query):
+ match = regex.search(v)
+ if match:
+ return match.group(0)
+ return None
+
+
+def on_result(request, search, result):
+ doi = extract_doi(result['parsed_url'])
+ if doi and len(doi) < 50:
+ for suffix in ('/', '.pdf', '/full', '/meta', '/abstract'):
+ if doi.endswith(suffix):
+ doi = doi[:-len(suffix)]
+ result['url'] = 'http://doai.io/' + doi
+ result['parsed_url'] = urlparse(result['url'])
+ return True
diff --git a/searx/plugins/https_rewrite.py b/searx/plugins/https_rewrite.py
new file mode 100644
index 0000000..4462c86
--- /dev/null
+++ b/searx/plugins/https_rewrite.py
@@ -0,0 +1,232 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
+'''
+
+import re
+import sys
+from lxml import etree
+from os import listdir, environ
+from os.path import isfile, isdir, join
+from searx.plugins import logger
+from flask_babel import gettext
+from searx import searx_dir
+from searx.url_utils import urlparse
+
+if sys.version_info[0] == 3:
+ unicode = str
+
+name = "HTTPS rewrite"
+description = gettext('Rewrite HTTP links to HTTPS if possible')
+default_on = True
+
+if 'SEARX_HTTPS_REWRITE_PATH' in environ:
+ rules_path = environ['SEARX_rules_path']
+else:
+ rules_path = join(searx_dir, 'plugins/https_rules')
+
+logger = logger.getChild("https_rewrite")
+
+# https://gitweb.torproject.org/\
+# pde/https-everywhere.git/tree/4.0:/src/chrome/content/rules
+
+# HTTPS rewrite rules
+https_rules = []
+
+
+# load single ruleset from a xml file
+def load_single_https_ruleset(rules_path):
+ ruleset = ()
+
+ # init parser
+ parser = etree.XMLParser()
+
+ # load and parse xml-file
+ try:
+ tree = etree.parse(rules_path, parser)
+ except:
+ # TODO, error message
+ return ()
+
+ # get root node
+ root = tree.getroot()
+
+ # check if root is a node with the name ruleset
+ # TODO improve parsing
+ if root.tag != 'ruleset':
+ return ()
+
+ # check if rule is deactivated by default
+ if root.attrib.get('default_off'):
+ return ()
+
+ # check if rule does only work for specific platforms
+ if root.attrib.get('platform'):
+ return ()
+
+ hosts = []
+ rules = []
+ exclusions = []
+
+ # parse childs from ruleset
+ for ruleset in root:
+ # this child define a target
+ if ruleset.tag == 'target':
+ # check if required tags available
+ if not ruleset.attrib.get('host'):
+ continue
+
+ # convert host-rule to valid regex
+ host = ruleset.attrib.get('host')\
+ .replace('.', r'\.').replace('*', '.*')
+
+ # append to host list
+ hosts.append(host)
+
+ # this child define a rule
+ elif ruleset.tag == 'rule':
+ # check if required tags available
+ if not ruleset.attrib.get('from')\
+ or not ruleset.attrib.get('to'):
+ continue
+
+ # TODO hack, which convert a javascript regex group
+ # into a valid python regex group
+ rule_from = ruleset.attrib['from'].replace('$', '\\')
+ if rule_from.endswith('\\'):
+ rule_from = rule_from[:-1] + '$'
+ rule_to = ruleset.attrib['to'].replace('$', '\\')
+ if rule_to.endswith('\\'):
+ rule_to = rule_to[:-1] + '$'
+
+ # TODO, not working yet because of the hack above,
+ # currently doing that in webapp.py
+ # rule_from_rgx = re.compile(rule_from, re.I)
+
+ # append rule
+ try:
+ rules.append((re.compile(rule_from, re.I | re.U), rule_to))
+ except:
+ # TODO log regex error
+ continue
+
+ # this child define an exclusion
+ elif ruleset.tag == 'exclusion':
+ # check if required tags available
+ if not ruleset.attrib.get('pattern'):
+ continue
+
+ exclusion_rgx = re.compile(ruleset.attrib.get('pattern'))
+
+ # append exclusion
+ exclusions.append(exclusion_rgx)
+
+ # convert list of possible hosts to a simple regex
+ # TODO compress regex to improve performance
+ try:
+ target_hosts = re.compile('^(' + '|'.join(hosts) + ')', re.I | re.U)
+ except:
+ return ()
+
+ # return ruleset
+ return (target_hosts, rules, exclusions)
+
+
+# load all https rewrite rules
+def load_https_rules(rules_path):
+ # check if directory exists
+ if not isdir(rules_path):
+ logger.error("directory not found: '" + rules_path + "'")
+ return
+
+ # search all xml files which are stored in the https rule directory
+ xml_files = [join(rules_path, f)
+ for f in listdir(rules_path)
+ if isfile(join(rules_path, f)) and f[-4:] == '.xml']
+
+ # load xml-files
+ for ruleset_file in xml_files:
+ # calculate rewrite-rules
+ ruleset = load_single_https_ruleset(ruleset_file)
+
+ # skip if no ruleset returned
+ if not ruleset:
+ continue
+
+ # append ruleset
+ https_rules.append(ruleset)
+
+ logger.info('{n} rules loaded'.format(n=len(https_rules)))
+
+
+def https_url_rewrite(result):
+ skip_https_rewrite = False
+ # check if HTTPS rewrite is possible
+ for target, rules, exclusions in https_rules:
+
+ # check if target regex match with url
+ if target.match(result['parsed_url'].netloc):
+ # process exclusions
+ for exclusion in exclusions:
+ # check if exclusion match with url
+ if exclusion.match(result['url']):
+ skip_https_rewrite = True
+ break
+
+ # skip https rewrite if required
+ if skip_https_rewrite:
+ break
+
+ # process rules
+ for rule in rules:
+ try:
+ new_result_url = rule[0].sub(rule[1], result['url'])
+ except:
+ break
+
+ # parse new url
+ new_parsed_url = urlparse(new_result_url)
+
+ # continiue if nothing was rewritten
+ if result['url'] == new_result_url:
+ continue
+
+ # get domainname from result
+ # TODO, does only work correct with TLD's like
+ # asdf.com, not for asdf.com.de
+ # TODO, using publicsuffix instead of this rewrite rule
+ old_result_domainname = '.'.join(
+ result['parsed_url'].hostname.split('.')[-2:])
+ new_result_domainname = '.'.join(
+ new_parsed_url.hostname.split('.')[-2:])
+
+ # check if rewritten hostname is the same,
+ # to protect against wrong or malicious rewrite rules
+ if old_result_domainname == new_result_domainname:
+ # set new url
+ result['url'] = new_result_url
+
+ # target has matched, do not search over the other rules
+ break
+ return result
+
+
+def on_result(request, search, result):
+ if result['parsed_url'].scheme == 'http':
+ https_url_rewrite(result)
+ return True
+
+
+load_https_rules(rules_path)
diff --git a/searx/plugins/https_rules/00README b/searx/plugins/https_rules/00README
new file mode 100644
index 0000000..fcd8a77
--- /dev/null
+++ b/searx/plugins/https_rules/00README
@@ -0,0 +1,17 @@
+<!--
+This directory contains web site rewriting rules for the
+HTTPS Everywhere software, available from
+https://www.eff.org/https-everywhere
+
+These rules were contributed to the project by users and aim to
+enable routine secure access to as many different web sites as
+possible. They are automatically installed together with the
+HTTPS Everywhere software. The presence of these rules does not
+mean that an HTTPS Everywhere user accessed, or intended to
+access, any particular web site.
+
+For information about how to create additional HTTPS Everywhere
+rewriting rules to add support for new sites, please see
+
+https://www.eff.org/https-everywhere/rulesets
+-->
diff --git a/searx/plugins/https_rules/Bing.xml b/searx/plugins/https_rules/Bing.xml
new file mode 100644
index 0000000..8b403f1
--- /dev/null
+++ b/searx/plugins/https_rules/Bing.xml
@@ -0,0 +1,56 @@
+<!--
+ For other Microsoft coverage, see Microsoft.xml.
+
+
+ CDN buckets:
+
+ - a134.lm.akamai.net
+
+ - akam.bing.com
+ - *.mm.bing.net
+
+
+ Nonfunctional domains:
+
+ - m2.cn.bing.com
+ - origin.bj1.bing.com
+ - blogs.bing.com
+
+
+ Fully covered domains:
+
+ - bing.com subdomains:
+
+ - (www.)
+ - c.bing (tracking beacons)
+ - cn.bing
+ - h.bing
+ - ssl
+ - testfamilysafety.bing
+ - udc.bing
+ - (www.)bing
+
+ - *.mm.bing.net
+ - api.bing.com
+
+-->
+<ruleset name="Bing">
+
+ <target host="bing.com" />
+ <target host="*.bing.com" />
+ <target host="*.mm.bing.net" />
+
+
+ <securecookie host=".*\.bing\.com$" name=".+" />
+
+
+ <rule from="^http://((?:c|cn|h|ssl|testfamilysafety|udc|www)\.)?bing\.com/"
+ to="https://$1bing.com/" />
+
+ <rule from="^http://([^/:@]*)\.mm\.bing\.net/"
+ to="https://$1.mm.bing.com/"/>
+
+ <rule from="^http://([^/:@]*)\.api\.bing\.net/"
+ to="https://$1.api.bing.com/"/>
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Dailymotion.xml b/searx/plugins/https_rules/Dailymotion.xml
new file mode 100644
index 0000000..743100c
--- /dev/null
+++ b/searx/plugins/https_rules/Dailymotion.xml
@@ -0,0 +1,69 @@
+<!--
+ Nonfunctional domains:
+
+ - blog.dailymotion.com
+ - press.dailymotion.com (shows steaw.com, CN: www.steaw.com)
+ - proxy-46.dailymotion.com
+ - publicite.dailymotion.com
+ - publisher.dailymotion.com (reset)
+ - vid.ak.dmcdn.net (403, Akamai)
+ - vid2.ak.dmcdn.net (504, akamai)
+
+
+ Problematic domains:
+
+ - ak2.static.dailymotion.com (mismatched, CN: *.dmcdn.net)
+ - support.dmcloud.net (mismatched, CN: *.zendesk.com)
+
+
+ Partially covered domains:
+
+ - (www.)dailymotion.com
+
+ - cdn/manifest/video/\w+.mnft 403s
+ - crossdomain.xml breaks videos
+
+-->
+<ruleset name="Dailymotion (default off)" default_off="breaks some embedded videos">
+
+ <target host="dailymotion.com" />
+ <!--
+ * for cross-domain cookie.
+ -->
+ <target host="*.dailymotion.com" />
+ <!--
+ https://mail1.eff.org/pipermail/https-everywhere-rules/2012-July/001241.html
+ -->
+ <exclusion pattern="^http://(?:www\.)?dailymotion\.com/(?:cdn/[\w-]+/video/|crossdomain\.xml$)" />
+ <target host="ak2.static.dailymotion.com" />
+ <target host="*.dmcdn.net" />
+ <target host="dmcloud.net" />
+ <target host="*.dmcloud.net" />
+
+
+ <!-- Testing wrt embedded breakage.
+
+ securecookie host="^.*\.dailymotion\.com$" name=".+" /-->
+ <!--
+ Omniture tracking cookies:
+ -->
+ <securecookie host="^\.dailymotion\.com$" name="^s_\w+$" />
+ <securecookie host="^www\.dailymotion\.com$" name=".+" />
+
+
+ <rule from="^http://(erroracct\.|www\.)?dailymotion\.com/"
+ to="https://$1dailymotion.com/" />
+
+ <rule from="^http://(s\d|static(?:\d|s\d-ssl))\.dmcdn\.net/"
+ to="https://$1.dmcdn.net/" />
+
+ <rule from="^https?://ak2\.static\.dailymotion\.com/"
+ to="https://static1-ssl.dmcdn.net/" />
+
+ <rule from="^http://(s\.|www\.)?dmcloud\.net/"
+ to="https://$1dmcloud.net/" />
+
+ <rule from="^https?://support\.dmcloud\.net/"
+ to="https://dmcloud.zendesk.com/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Deviantart.xml b/searx/plugins/https_rules/Deviantart.xml
new file mode 100644
index 0000000..7830fc2
--- /dev/null
+++ b/searx/plugins/https_rules/Deviantart.xml
@@ -0,0 +1,53 @@
+<!--
+ For problematic rules, see Deviantart-mismatches.xml.
+
+
+ Other deviantArt rulesets:
+
+ - Sta.sh.xml
+
+
+ ToDo: Find edgecast URL for /(fc|th)\d+.
+
+
+ Mixed content:
+
+ - Images on *.....com from e.deviantart.net *
+
+ * Secured by us
+
+-->
+<ruleset name="DeviantArt (pending)" default_off="site operator says not ready yet">
+
+ <target host="deviantart.com" />
+ <target host="*.deviantart.com" />
+ <target host="deviantart.net" />
+ <target host="*.deviantart.net" />
+
+
+ <!-- Not secured by server:
+ -->
+ <!--securecookie host="^\.deviantart\.com$" name="^userinfo$" /-->
+
+ <securecookie host="^\.deviantart\.com$" name=".*" />
+
+
+ <!-- Redirects from com to net, but does so successfully by itself.
+ -->
+ <rule from="^http://([aei]|fc\d\d|s[ht]|th\d\d)\.deviantart\.(com|net)/"
+ to="https://$1.deviantart.$2/" />
+
+ <!-- This handles everything that isn't in the first rule.
+ Namely, usernames, backend, fc, th, and (www.).
+ These domains present a cert that is only
+ valid for .com.
+ Note that .net isn't used on DA, but.net does
+ redirect to .com, and we shouldn't break what would
+ otherwise work.
+ Mustn't rewrite from https here, as doing so
+ would conflict with the first rule.
+ -->
+ <rule from="^http://([^/:@\.]+\.)?deviantart\.(?:com|net)/"
+ to="https://$1deviantart.com/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/DuckDuckGo.xml b/searx/plugins/https_rules/DuckDuckGo.xml
new file mode 100644
index 0000000..173a9ad
--- /dev/null
+++ b/searx/plugins/https_rules/DuckDuckGo.xml
@@ -0,0 +1,38 @@
+<!--
+ Problematic domains:
+
+ - www.dukgo.com (mismatched, CN: dukgo.com)
+
+
+ Fully covered domains:
+
+ - (www.)dukgo.com (www → ^)
+
+-->
+<ruleset name="DuckDuckGo">
+ <target host="duckduckgo.com" />
+ <target host="*.duckduckgo.com" />
+ <target host="ddg.gg" />
+ <target host="duck.co" />
+ <target host="i.duck.co" />
+ <target host="dukgo.com" />
+ <target host="www.dukgo.com" />
+
+ <exclusion pattern="^http://(help|meme)\.duckduckgo\.com/" />
+
+ <securecookie host="^duck\.co$" name=".*"/>
+
+ <rule from="^http://duckduckgo\.com/" to="https://duckduckgo.com/"/>
+ <rule from="^http://([^/:@\.]+)\.duckduckgo\.com/" to="https://$1.duckduckgo.com/"/>
+ <!-- TODO: What does ddg.gg/foo do? Runs query foo, redirects to homepage, or error? -->
+ <rule from="^http://ddg\.gg/$" to="https://duckduckgo.com/" />
+
+ <rule from="^http://duck\.co/" to="https://duck.co/" />
+
+ <rule from="^http://i\.duck\.co/"
+ to="https://duckduckgo.com/"/>
+
+ <rule from="^http://(?:www\.)?dukgo\.com/"
+ to="https://dukgo.com/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Flickr.xml b/searx/plugins/https_rules/Flickr.xml
new file mode 100644
index 0000000..85c6e80
--- /dev/null
+++ b/searx/plugins/https_rules/Flickr.xml
@@ -0,0 +1,44 @@
+<!--
+ For other Yahoo coverage, see Yahoo.xml.
+
+
+ These altnames don't exist:
+
+ - www.blog.flickr.net
+ - www.code.flickr.net
+
+-->
+<ruleset name="Flickr">
+
+ <target host="flic.kr" />
+ <target host="*.flic.kr" />
+ <target host="flickr.com" />
+ <target host="*.flickr.com" />
+ <target host="*.flickr.net" />
+ <target host="*.staticflickr.com" />
+
+
+ <!-- Not secured by server:
+ -->
+ <!--securecookie host="^\.flic\.kr$" name="^BX$" /-->
+
+ <securecookie host="^\.flic\.kr$" name=".+" />
+ <securecookie host=".*\.flickr\.com$" name=".+" />
+
+
+ <rule from="^http://flic\.kr/"
+ to="https://flic.kr/" />
+
+ <rule from="^http://(api\.|www\.)?flickr\.com/"
+ to="https://$1flickr.com/" />
+
+ <rule from="^http://s(ecure|tatic)\.flickr\.com/"
+ to="https://s$1.flickr.com/" />
+
+ <rule from="^http://(c2|farm\d+)\.static(\.)?flickr\.com/"
+ to="https://$1.static$2flickr.com/" />
+
+ <rule from="^http://(blog|code)\.flickr\.net/"
+ to="https://$1.flickr.net/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Github-Pages.xml b/searx/plugins/https_rules/Github-Pages.xml
new file mode 100644
index 0000000..d3be58a
--- /dev/null
+++ b/searx/plugins/https_rules/Github-Pages.xml
@@ -0,0 +1,11 @@
+<!--
+ For other GitHub coverage, see Github.xml.
+-->
+<ruleset name="GitHub Pages">
+
+ <target host="*.github.io" />
+
+ <rule from="^http://([^/@:\.]+)\.github\.io/"
+ to="https://$1.github.io/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Github.xml b/searx/plugins/https_rules/Github.xml
new file mode 100644
index 0000000..a9a3a1e
--- /dev/null
+++ b/searx/plugins/https_rules/Github.xml
@@ -0,0 +1,94 @@
+<!--
+ Other GitHub rulesets:
+
+ - Github-Pages.xml
+ - Guag.es.xml
+ - Speaker_Deck.com.xml
+
+
+ CDN buckets:
+
+ - github-images.s3.amazonaws.com
+ - github.global.ssl.fastly.net
+ - a248.e.akamai.net/assets.github.com/
+ - a248.e.akamai.net/camo.github.com/
+ - s3.amazonaws.com/github/ | d24z2fz21y4fag.cloudfront.net
+ - github.myshopify.com
+
+
+ Fully covered domains:
+
+ - github.com subdomains:
+
+ - (www.)
+ - assets\d+
+ - assets-cdn
+ - bounty
+ - cloud
+ - f.cloud
+ - codeload
+ - developer
+ - eclipse
+ - enterprise
+ - gist
+ - gist-assets
+ - help
+ - identicons
+ - jobs
+ - mac
+ - mobile
+ - nodeload
+ - octodex
+ - pages
+ - raw
+ - rg3
+ - shop
+ - status
+ - support
+ - training
+ - try
+ - wiki
+ - windows
+
+ - collector.githubapp.com
+
+ - githubusercontent.com
+
+-->
+<ruleset name="GitHub">
+
+ <target host="github.com" />
+ <target host="*.github.com" />
+ <target host="github.io" />
+ <target host="*.githubusercontent.com" />
+ <target host="collector.githubapp.com" />
+
+
+ <!-- Secured by server:
+ -->
+ <!--securecookie host="^github\.com$" name="^(_gh_sess|tz|user_session)$" /-->
+ <!--securecookie host="^\.github\.com$" name="^(dotcom_user|logged_in)$" /-->
+ <!--securecookie host="^enterprise\.github\.com$" name="^(_enterprise_web|request_method)$" /-->
+ <!--securecookie host="^gist\.github\.com$" name="^_gist_session$" /-->
+ <!--securecookie host="^help\.github\.com$" name="^_help_session$" /-->
+ <!--
+ Not secured by server:
+ -->
+ <!--securecookie host="^status\.github\.com$" name="^rack\.session$" /-->
+
+ <securecookie host="^(?:.*\.)?github\.com$" name=".+" />
+
+
+ <rule from="^http://((?:assets\d+|assets-cdn|bounty|cloud|f\.cloud|codeload|developer|eclipse|enterprise|gist|gist-assets|help|identicons|jobs|mac|mobile|nodeload|octodex|pages|raw|rg3|shop|status|support|training|try|wiki|windows|www)\.)?github\.com/"
+ to="https://$1github.com/" />
+
+ <rule from="^http://collector\.githubapp\.com/"
+ to="https://collector.githubapp.com/" />
+
+ <rule from="^https?://github\.io/"
+ to="https://pages.github.com/" />
+
+ <rule from="^http://([^/@:\.]+)\.githubusercontent\.com/"
+ to="https://$1.githubusercontent.com/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Google-mismatches.xml b/searx/plugins/https_rules/Google-mismatches.xml
new file mode 100644
index 0000000..de9d3eb
--- /dev/null
+++ b/searx/plugins/https_rules/Google-mismatches.xml
@@ -0,0 +1,26 @@
+<!--
+
+ Problematic domains:
+
+ - (www.)apture.com (works, mismatched, CN: *.google.com)
+
+-->
+<ruleset name="Google (mismatches)" default_off="mismatches">
+
+ <!-- Akamai -->
+ <target host="js.admeld.com"/>
+ <target host="apture.com" />
+ <target host="www.apture.com" />
+ <target host="googleartproject.com"/>
+ <target host="www.googleartproject.com"/>
+
+ <rule from="^http://js\.admeld\.com/"
+ to="https://js.admeld.com/"/>
+
+ <rule from="^https?://(?:www\.)?apture\.com/"
+ to="https://apture.com/" />
+
+ <rule from="^http://(?:www\.)?googleartproject\.com/"
+ to="https://www.googleartproject.com/"/>
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Google.org.xml b/searx/plugins/https_rules/Google.org.xml
new file mode 100644
index 0000000..d6cc478
--- /dev/null
+++ b/searx/plugins/https_rules/Google.org.xml
@@ -0,0 +1,14 @@
+<!--
+ For other Google coverage, see GoogleServices.xml.
+
+-->
+<ruleset name="Google.org">
+
+ <target host="google.org" />
+ <target host="www.google.org" />
+
+
+ <rule from="^http://(www\.)?google\.org/"
+ to="https://$1google.org/" />
+
+</ruleset> \ No newline at end of file
diff --git a/searx/plugins/https_rules/GoogleAPIs.xml b/searx/plugins/https_rules/GoogleAPIs.xml
new file mode 100644
index 0000000..85a5a80
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleAPIs.xml
@@ -0,0 +1,143 @@
+<!--
+ For other Google coverage, see GoogleServices.xml.
+
+
+ Nonfunctional domains:
+
+ - hosted.gmodules.com *
+ - img0.gmodules.com *
+ - p.gmodules.com *
+
+ * 404; mismatched, CN: *.googleusercontent.com
+
+
+ Problematic domains:
+
+ - gmodules.com (503, CN: www.google.com)
+ - www.gmodules.com (503, CN: *.googleusercontent.com)
+ - gstatic.com (404, valid cert)
+ - api.recaptcha.net (works; mismatched, CN: google.com)
+
+
+ Partially covered domains:
+
+ - (www.)gmodules.com (→ www.google.com)
+ - (www.)google.com
+ - chart.apis.google.com (→ chart.googleapis.com)
+
+
+ Fully covered domains:
+
+ - api.google.com
+
+ - *.clients.google.com:
+
+ - linkhelp
+
+ - ssl.google-analytics.com
+ - www.google-analytics.com
+
+ - googleapis.com subdomains:
+
+ - ajax
+ - chart
+ - *.commondatastorage
+ - fonts
+ - *.storage
+ - www
+
+ - gstatic.com subdomains:
+
+ - (www.) (^ → www)
+ - csi
+ - encrypted-tbn\d
+ - g0
+ - *.metric
+ - ssl
+ - t\d
+
+ - api.recaptcha.net (→ www.google.com)
+ - api-secure.recaptcha.net
+ - gdata.youtube.com
+
+
+ ssl.google-analytics.com/ga.js sets __utm\w wildcard
+ cookies on whichever domain it is loaded from.
+
+-->
+<ruleset name="Google APIs">
+
+ <target host="gmodules.com" />
+ <target host="www.gmodules.com" />
+ <target host="google.com" />
+ <target host="apis.google.com" />
+ <target host="*.apis.google.com" />
+ <target host="*.clients.google.com" />
+ <target host="www.google.com" />
+ <target host="*.google-analytics.com" />
+ <target host="*.googleapis.com" />
+ <target host="gstatic.com" />
+ <target host="*.gstatic.com" />
+ <!-- Captive portal detection redirects to this URL, and many captive
+ portals break TLS, so exempt this redirect URL.
+ See GitHub bug #368
+ -->
+ <exclusion pattern="^http://www\.gstatic\.com/generate_204" />
+ <target host="*.recaptcha.net" />
+ <target host="gdata.youtube.com" />
+ <exclusion pattern="^http://gdata\.youtube\.com/crossdomain\.xml" />
+
+
+ <securecookie host="^ssl\.google-analytics\.com$" name=".+" />
+
+
+ <rule from="^http://(?:www\.)?gmodules\.com/ig/images/"
+ to="https://www.google.com/ig/images/" />
+
+ <!-- jsapi was causing problems on some sites that embed google maps:
+ https://trac.torproject.org/projects/tor/ticket/2335
+ Apparently now fixed; thanks, Google!
+ -->
+ <rule from="^http://(?:www\.)?google\.com/(afsonline/|chart|jsapi|recaptcha/|uds)"
+ to="https://www.google.com/$1" />
+
+ <rule from="^http://(api|[\w-]+\.client)s\.google\.com/"
+ to="https://$1s.google.com/" />
+
+ <rule from="^http://chart\.apis\.google\.com/chart"
+ to="https://chart.googleapis.com/chart" />
+
+ <rule from="^http://(ssl|www)\.google-analytics\.com/"
+ to="https://$1.google-analytics.com/" />
+
+ <rule from="^http://(ajax|chart|fonts|www)\.googleapis\.com/"
+ to="https://$1.googleapis.com/" />
+
+ <rule from="^http://([^@:\./]+\.)?(commondata)?storage\.googleapis\.com/"
+ to="https://$1$2storage.googleapis.com/" />
+
+ <!-- There is an interesting question about whether we should
+ append &strip=1 to all cache URLs. This causes them to load
+ without images and styles, which is more secure but can look
+ worse.
+ Without &strip=1, the images and styles from the cached
+ pages still load from the original, typically unencrypted, page.
+ With &strip=1, the cached page will be text-only and
+ will come exclusively from Google's HTTPS server.
+ -->
+ <rule from="^http://(?:www\.)?gstatic\.com/"
+ to="https://www.gstatic.com/" />
+
+ <rule from="^http://(csi|encrypted-tbn\d|g0|[\w-]+\.metric|ssl|t\d)\.gstatic\.com/"
+ to="https://$1.gstatic.com/" />
+
+ <rule from="^http://api\.recaptcha\.net/"
+ to="https://www.google.com/recaptcha/api/" />
+
+ <rule from="^http://api-secure\.recaptcha\.net/"
+ to="https://api-secure.recaptcha.net/" />
+
+ <rule from="^http://gdata\.youtube\.com/"
+ to="https://gdata.youtube.com/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleCanada.xml b/searx/plugins/https_rules/GoogleCanada.xml
new file mode 100644
index 0000000..d5eefe8
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleCanada.xml
@@ -0,0 +1,6 @@
+<ruleset name="GoogleCanada">
+ <target host="google.ca" />
+ <target host="*.google.ca" />
+ <rule from="^http://([^/:@\.]+)\.google\.ca/finance" to="https://$1.google.ca/finance"/>
+</ruleset>
+
diff --git a/searx/plugins/https_rules/GoogleImages.xml b/searx/plugins/https_rules/GoogleImages.xml
new file mode 100644
index 0000000..0112001
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleImages.xml
@@ -0,0 +1,65 @@
+<!--
+ For other Google coverage, see GoogleServices.xml.
+
+
+ Problematic domains:
+
+ - www.google.bo *
+ - www.google.co *
+ - www.google.ec *
+ - www.google.in *
+ - www.google.kr *
+ - www.google.com.kz **
+ - www.google.com.lk *
+ - www.google.mx **
+ - www.google.sg *
+ - www.google.sl *
+ - www.google.ug *
+ - www.google.vn *
+
+ * 404; mismatched, CN: google.com
+ ** Works; mismatched, CN: google.com
+
+-->
+<ruleset name="Google Images">
+
+ <target host="google.*" />
+ <target host="www.google.*" />
+ <target host="google.co.*" />
+ <target host="www.google.co.*" />
+ <target host="google.com" />
+ <target host="images.google.com" />
+ <target host="google.com.*" />
+ <target host="www.google.com.*" />
+ <!--
+ Only handle image-related paths in this ruleset:
+ -->
+ <exclusion pattern="^http://(?:www\.)?google(?:\.com?)?\.\w{2,3}/(?!(?:advanced_image_search|imghp|.*tb(?:m=isch|s=sbi)))" />
+
+
+ <rule from="^http://(?:www\.)?google\.com/"
+ to="https://www.google.com/" />
+
+ <rule from="^http://images\.google\.com/"
+ to="https://images.google.com/" />
+
+ <!-- First handle problematic domains:
+ -->
+ <rule from="^http://(?:www\.)?google\.co/"
+ to="https://www.google.com/" />
+
+ <rule from="^http://(?:www\.)?google\.(?:co\.)?(in|kr|ug)/"
+ to="https://www.google.co.$1/" />
+
+ <rule from="^http://(?:www\.)?google\.(?:com\.)?(kz|lk)/"
+ to="https://www.google.$1/" />
+
+ <rule from="^http://(?:www\.)?google\.(?:com\.)?(bo|ec|mx|sg|sl|vn)/"
+ to="https://www.google.com.$1/" />
+
+ <!-- And then the rest:
+ -->
+ <rule from="^http://(?:www\.)?google\.(com?\.)?(ae|ar|at|au|bg|bh|br|ca|ch|cl|co|cr|cu|de|eg|es|fi|fr|gh|gt|hr|id|ie|il|it|jo|jp|jm|ke|kw|lb|ly|my|na|ng|nl|no|nz|om|pa|pe|pk|pl|pt|py|qa|ro|ru|rw|sa|se|sv|th|tr|uk|uy|ve|za|zw)/"
+ to="https://www.google.$1$2/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleMainSearch.xml b/searx/plugins/https_rules/GoogleMainSearch.xml
new file mode 100644
index 0000000..df504d9
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleMainSearch.xml
@@ -0,0 +1,78 @@
+<ruleset name="Search www.google.com">
+
+<!--
+Enabling this ruleset should cause searches to go to
+https://www.google.com rather than https://encrypted.google.com. Note that
+the filename is important; it must be before GoogleSearch.xml in a bash
+expansion of src/chrome/content/rules/*.xml in order to take precedence.
+-->
+
+ <target host="*.google.com" />
+ <target host="google.com" />
+ <target host="www.google.com.*" />
+ <target host="google.com.*" />
+ <target host="www.google.co.*" />
+ <target host="google.co.*" />
+ <target host="www.google.*" />
+ <target host="google.*" />
+ <!-- beyond clients1 these do not currently exist in the ccTLDs,
+ but just in case... -->
+ <target host="clients1.google.com.*" />
+ <target host="clients2.google.com.*" />
+ <target host="clients3.google.com.*" />
+ <target host="clients4.google.com.*" />
+ <target host="clients5.google.com.*" />
+ <target host="clients6.google.com.*" />
+ <target host="clients1.google.co.*" />
+ <target host="clients2.google.co.*" />
+ <target host="clients3.google.co.*" />
+ <target host="clients4.google.co.*" />
+ <target host="clients5.google.co.*" />
+ <target host="clients6.google.co.*" />
+ <target host="clients1.google.*" />
+ <target host="clients2.google.*" />
+ <target host="clients3.google.*" />
+ <target host="clients4.google.*" />
+ <target host="clients5.google.*" />
+ <target host="clients6.google.*" />
+
+ <rule from="^http://www\.google\.com/$"
+ to="https://www.google.com/"/>
+
+ <!-- The most basic case. -->
+
+ <rule from="^http://(?:www\.)?google\.com/search"
+ to="https://www.google.com/search"/>
+
+ <!-- A very annoying exception that we seem to need for the basic case -->
+
+ <exclusion pattern="^http://(?:www\.)?google\.com/search.*tbs=shop" />
+ <exclusion pattern="^http://clients[0-9]\.google\.com/.*client=products.*" />
+ <exclusion pattern="^http://suggestqueries\.google\.com/.*client=.*" />
+
+ <!-- https://trac.torproject.org/projects/tor/ticket/9713 -->
+
+ <exclusion pattern="^http://clients[0-9]\.google\.com/ocsp" />
+
+ <!-- This is necessary for image results links from web search results -->
+
+ <exclusion pattern="^http://(?:www\.)?google\.com/search.*tbm=isch.*" />
+
+ <rule from="^http://(?:www\.)?google\.com/webhp"
+ to="https://www.google.com/webhp"/>
+
+ <rule from="^http://(?:www\.)?google\.com/#"
+ to="https://www.google.com/#"/>
+
+ <rule from="^http://(?:www\.)?google\.com/$"
+ to="https://www.google.com/"/>
+
+ <!-- Completion urls look like this:
+
+http://clients2.google.co.jp/complete/search?hl=ja&client=hp&expIds=17259,24660,24729,24745&q=m&cp=1 HTTP/1.1\r\n
+
+ -->
+ <rule from="^http://clients[0-9]\.google\.com/complete/search"
+ to="https://clients1.google.com/complete/search"/>
+
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleMaps.xml b/searx/plugins/https_rules/GoogleMaps.xml
new file mode 100644
index 0000000..0f82c52
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleMaps.xml
@@ -0,0 +1,67 @@
+<!--
+ Problematic domains:
+
+ - khms *
+ - khms[0-3] *
+
+ * $ 404s
+
+
+ Fully covered domains:
+
+ - google.com subdomains:
+
+ - khms
+ - khms[0-3]
+
+-->
+<ruleset name="Google Maps">
+
+ <target host="maps.google.*" />
+ <!--
+ https://trac.torproject.org/projects/tor/ticket/8627
+ -->
+ <exclusion pattern="^http://maps\.google\.com/local_url" />
+ <exclusion pattern="^http://maps\.google\.gr/transitathens" />
+ <target host="maps.google.co.*" />
+ <target host="khms.google.com" />
+ <target host="khms0.google.com" />
+ <target host="khms1.google.com" />
+ <target host="khms2.google.com" />
+ <target host="khms3.google.com" />
+ <target host="maps-api-ssl.google.com" />
+ <target host="mw2.google.com" />
+ <target host="maps.google.com.*" />
+ <target host="maps.googleapis.com" />
+ <!--
+ https://mail1.eff.org/pipermail/https-everywhere-rules/2012-September/001317.html
+ -->
+ <!--exclusion pattern="^http://maps\.googleapis\.com/map(files/lib/map_1_20\.swf|sapi/publicapi\?file=flashapi)" /-->
+ <exclusion pattern="^http://maps\.googleapis\.com/map(?:files/lib/map_\d+_\d+\.swf|sapi/publicapi\?file=flashapi)" />
+ <target host="maps.gstatic.com" />
+
+
+ <!--securecookie host="^maps\.google\.(com?\.)?(au|ca|gh|ie|in|jm|ke|lk|my|n[agz]|pk|rw|sl|sg|ug|uk|za|zw)$" name=".+" /-->
+ <securecookie host="^maps\.google\.[\w.]{2,6}$" name=".+" />
+ <securecookie host="^maps\.g(?:oogle|oogleapis|static)\.com$" name=".+" />
+ <securecookie host="^maps-api-ssl\.google\.com$" name=".+" />
+
+
+ <rule from="^http://maps\.google\.([^/]+)/"
+ to="https://maps.google.$1/" />
+
+ <!-- http://khms.../$ 404s:
+ -->
+ <rule from="^http://khms\d?\.google\.com/+\??$"
+ to="https://www.google.com/" />
+
+ <rule from="^http://(khms\d?|maps-api-ssl|mw2)\.google\.com/"
+ to="https://$1.google.com/" />
+
+ <rule from="^http://maps\.g(oogleapis|static)\.com/"
+ to="https://maps.g$1.com/" />
+
+ <rule from="^https://maps\.googleapis\.com/map(?=files/lib/map_\d+_\d+\.swf|sapi/publicapi\?file=flashapi)"
+ to="http://maps.googleapis.com/map" downgrade="1" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleMelange.xml b/searx/plugins/https_rules/GoogleMelange.xml
new file mode 100644
index 0000000..ec23cd4
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleMelange.xml
@@ -0,0 +1,6 @@
+<ruleset name="GoogleMelange">
+ <target host="www.google-melange.com" />
+ <target host="google-melange.com" />
+
+ <rule from="^http://(www\.)?google-melange\.com/" to="https://www.google-melange.com/" />
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleSearch.xml b/searx/plugins/https_rules/GoogleSearch.xml
new file mode 100644
index 0000000..66b7ffd
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleSearch.xml
@@ -0,0 +1,135 @@
+<ruleset name="Google Search">
+
+ <target host="google.com" />
+ <target host="*.google.com" />
+ <target host="google.com.*" />
+ <target host="www.google.com.*" />
+ <target host="google.co.*" />
+ <target host="www.google.co.*" />
+ <target host="google.*" />
+ <target host="www.google.*" />
+ <!--
+ Beyond clients1 these do not currently
+ exist in the ccTLDs, but just in case...
+ -->
+ <target host="clients1.google.com.*" />
+ <target host="clients2.google.com.*" />
+ <target host="clients3.google.com.*" />
+ <target host="clients4.google.com.*" />
+ <target host="clients5.google.com.*" />
+ <target host="clients6.google.com.*" />
+ <target host="clients1.google.co.*" />
+ <target host="clients2.google.co.*" />
+ <target host="clients3.google.co.*" />
+ <target host="clients4.google.co.*" />
+ <target host="clients5.google.co.*" />
+ <target host="clients6.google.co.*" />
+ <target host="clients1.google.*" />
+ <target host="clients2.google.*" />
+ <target host="clients3.google.*" />
+ <target host="clients4.google.*" />
+ <target host="clients5.google.*" />
+ <target host="clients6.google.*" />
+
+
+ <!-- Some Google pages can generate naive links back to the
+ unencrypted version of encrypted.google.com, which is
+ a 301 but theoretically vulnerable to SSL stripping.
+ -->
+ <rule from="^http://encrypted\.google\.com/"
+ to="https://encrypted.google.com/" />
+
+ <!-- The most basic case.
+ -->
+ <rule from="^http://(?:www\.)?google\.com/search"
+ to="https://encrypted.google.com/search" />
+
+ <!-- A very annoying exception that we
+ seem to need for the basic case
+ -->
+ <exclusion pattern="^http://(?:www\.)?google\.com/search.*tbs=shop" />
+ <exclusion pattern="^http://clients\d\.google\.com/.*client=products.*" />
+ <exclusion pattern="^http://suggestqueries\.google\.com/.*client=.*" />
+
+ <!-- https://trac.torproject.org/projects/tor/ticket/9713
+ -->
+
+ <exclusion pattern="^http://clients[0-9]\.google\.com/ocsp" />
+
+
+ <!-- This is necessary for image results
+ links from web search results
+ -->
+ <exclusion pattern="^http://(?:www\.)?google\.com/search.*tbm=isch.*" />
+
+ <rule from="^http://(?:www\.)?google\.com/about"
+ to="https://www.google.com/about" />
+
+ <!-- There are two distinct cases for these firefox searches -->
+
+ <rule from="^http://(?:www\.)?google(?:\.com?)?\.[a-z]{2}/firefox/?$"
+ to="https://encrypted.google.com/" />
+
+ <rule from="^http://(?:www\.)?google(?:\.com?)?\.[a-z]{2}/firefox"
+ to="https://encrypted.google.com/webhp" />
+
+ <rule from="^http://(?:www\.)?google\.com/webhp"
+ to="https://encrypted.google.com/webhp" />
+
+ <rule from="^http://codesearch\.google\.com/"
+ to="https://codesearch.google.com/" />
+
+ <rule from="^http://(?:www\.)?google\.com/codesearch"
+ to="https://www.google.com/codesearch" />
+
+ <rule from="^http://(?:www\.)?google\.com/#"
+ to="https://encrypted.google.com/#" />
+
+ <rule from="^http://(?:www\.)?google\.com/$"
+ to="https://encrypted.google.com/" />
+
+ <!-- Google supports IPv6 search, including
+ HTTPS with a valid certificate! -->
+ <rule from="^http://ipv6\.google\.com/"
+ to="https://ipv6.google.com/" />
+
+ <!-- most google international sites look like
+ "google.fr", some look like "google.co.jp",
+ and some crazy ones like "google.com.au" -->
+
+ <rule from="^http://(www\.)?google(\.com?)?\.([a-z]{2})/(search\?|#)"
+ to="https://$1google$2.$3/$4" />
+
+ <!-- Language preference setting -->
+ <rule from="^http://(www\.)?google(\.com?)?\.([a-z]{2})/setprefs"
+ to="https://$1google$2.$3/setprefs" />
+
+ <!-- Completion urls look like this:
+
+http://clients2.google.co.jp/complete/search?hl=ja&client=hp&expIds=17259,24660,24729,24745&q=m&cp=1 HTTP/1.1\r\n
+
+ -->
+ <rule from="^http://clients\d\.google\.com/complete/search"
+ to="https://clients1.google.com/complete/search" />
+
+ <rule from="^http://clients\d\.google(\.com?\.[a-z]{2})/complete/search"
+ to="https://clients1.google.$1/complete/search" />
+
+ <rule from="^http://clients\d\.google\.([a-z]{2})/complete/search"
+ to="https://clients1.google.$1/complete/search" />
+
+ <rule from="^http://suggestqueries\.google\.com/complete/search"
+ to="https://clients1.google.com/complete/search" />
+
+ <rule from="^http://(www\.)?google\.(com?\.)?([a-z]{2})/(?:webhp)?$"
+ to="https://$1google.$2$3/" />
+
+ <!-- If there are URL parameters, keep them. -->
+ <rule from="^http://(www\.)?google\.(com?\.)?([a-z]{2})/(?:webhp)?\?"
+ to="https://$1google.$2$3/webhp?" />
+
+ <!-- teapot -->
+ <rule from="^http://(www\.)?google(\.com?)?\.([a-z]{2})/teapot"
+ to="https://$1google$2.$3/teapot" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleServices.xml b/searx/plugins/https_rules/GoogleServices.xml
new file mode 100644
index 0000000..704646b
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleServices.xml
@@ -0,0 +1,345 @@
+<!--
+ Other Google rulesets:
+
+ - 2mdn.net.xml
+ - Admeld.xml
+ - ChannelIntelligence.com.xml
+ - Doubleclick.net.xml
+ - FeedBurner.xml
+ - Google.org.xml
+ - GoogleAPIs.xml
+ - Google_App_Engine.xml
+ - GoogleImages.xml
+ - GoogleShopping.xml
+ - Ingress.xml
+ - Meebo.xml
+ - Orkut.xml
+ - Postini.xml
+ - WebM_Project.org.xml
+
+
+ Nonfunctional domains:
+
+ - feedproxy.google.com (404, valid cert)
+ - partnerpage.google.com *
+ - safebrowsing.clients.google.com (404, mismatched)
+ - (www.)googlesyndicatedsearch.com (404; mismatched, CN: google.com)
+ - buttons.googlesyndication.com *
+
+ * 404, valid cert
+
+
+ Nonfunctional google.com paths:
+
+ - analytics (redirects to http)
+ - imgres
+ - gadgets *
+ - hangouts (404)
+ - u/ (404)
+
+ * Redirects to http
+
+
+ Problematic domains:
+
+ - www.goo.gl (404; mismatched, CN: *.google.com)
+
+ - google.com subdomains:
+
+ - books (googlebooks/, images/, & intl/ 404, but works when rewritten to www)
+ - cbks0 ****
+ - earth *
+ - gg ($ 404s)
+ - knoll *
+ - scholar **
+ - trends *
+
+ - news.google.cctld **
+ - scholar.google.cctld **
+ - *-opensocial.googleusercontent.com ***
+
+ **** $ 404s
+ * 404, valid cert
+ ** Redirects to http, valid cert
+ *** Breaks followers widget - https://trac.torproject.org/projects/tor/ticket/7294
+
+
+ Partially covered domains:
+
+ - google.cctld subdomains:
+
+ - scholar (→ www)
+
+ - google.com subdomains:
+
+ - (www.)
+ - cbks0 ($ 404s)
+ - gg ($ 404s)
+ - news (→ www)
+ - scholar (→ www)
+
+ - *.googleusercontent.com (*-opensocial excluded)
+
+
+ Fully covered domains:
+
+ - lh[3-6].ggpht.com
+ - (www.)goo.gl (www → ^)
+
+ - google.com subdomains:
+
+ - accounts
+ - adwords
+ - apis
+ - appengine
+ - books (→ encrypted)
+ - calendar
+ - checkout
+ - chrome
+ - clients[12]
+ - code
+ - *.corp
+ - developers
+ - dl
+ - docs
+ - docs\d
+ - \d.docs
+ - drive
+ - earth (→ www)
+ - encrypted
+ - encrypted-tbn[123]
+ - feedburner
+ - fiber
+ - finance
+ - glass
+ - groups
+ - health
+ - helpouts
+ - history
+ - hostedtalkgadget
+ - id
+ - investor
+ - knol
+ - knoll (→ knol)
+ - lh\d
+ - mail
+ - chatenabled.mail
+ - pack
+ - picasaweb
+ - pki
+ - play
+ - plus
+ - plusone
+ - productforums
+ - profiles
+ - safebrowsing-cache
+ - cert-test.sandbox
+ - plus.sandbox
+ - sb-ssl
+ - script
+ - security
+ - services
+ - servicessites
+ - sites
+ - spreadsheets
+ - spreadsheets\d
+ - support
+ - talk
+ - talkgadget
+ - tbn2 (→ encrypted-tbn2)
+ - tools
+ - trends (→ www)
+
+ - partner.googleadservices.com
+ - (www.)googlecode.com
+ - *.googlecode.com (per-project subdomains)
+ - googlesource.com
+ - *.googlesource.com
+ - pagead2.googlesyndication.com
+ - tpc.googlesyndication.com
+ - mail-attachment.googleusercontent.com
+ - webcache.googleusercontent.com
+
+
+ XXX: Needs more testing
+
+-->
+<ruleset name="Google Services">
+
+ <target host="*.ggpht.com" />
+ <target host="gmail.com" />
+ <target host="www.gmail.com" />
+ <target host="goo.gl" />
+ <target host="www.goo.gl" />
+ <target host="google.*" />
+ <target host="accounts.google.*" />
+ <target host="adwords.google.*" />
+ <target host="finance.google.*" />
+ <target host="groups.google.*" />
+ <target host="it.google.*" />
+ <target host="news.google.*" />
+ <exclusion pattern="^http://(?:news\.)?google\.com/(?:archivesearch|newspapers)" />
+ <target host="picasaweb.google.*" />
+ <target host="scholar.google.*" />
+ <target host="www.google.*" />
+ <target host="*.google.ca" />
+ <target host="google.co.*" />
+ <target host="accounts.google.co.*" />
+ <target host="adwords.google.co.*" />
+ <target host="finance.google.co.*" />
+ <target host="groups.google.co.*" />
+ <target host="id.google.co.*" />
+ <target host="news.google.co.*" />
+ <target host="picasaweb.google.co.*" />
+ <target host="scholar.google.co.*" />
+ <target host="www.google.co.*" />
+ <target host="google.com" />
+ <target host="*.google.com" />
+ <exclusion pattern="^http://(?:www\.)?google\.com/analytics/*(?:/[^/]+)?(?:\?.*)?$" />
+ <!--exclusion pattern="^http://books\.google\.com/(?!books/(\w+\.js|css/|javascript/)|favicon\.ico|googlebooks/|images/|intl/)" /-->
+ <exclusion pattern="^http://cbks0\.google\.com/(?:$|\?)" />
+ <exclusion pattern="^http://gg\.google\.com/(?!csi(?:$|\?))" />
+ <target host="google.com.*" />
+ <target host="accounts.google.com.*" />
+ <target host="adwords.google.com.*" />
+ <target host="groups.google.com.*" />
+ <target host="id.google.com.*" />
+ <target host="news.google.com.*" />
+ <target host="picasaweb.google.com.*" />
+ <target host="scholar.google.com.*" />
+ <target host="www.google.com.*" />
+ <target host="partner.googleadservices.com" />
+ <target host="googlecode.com" />
+ <target host="*.googlecode.com" />
+ <target host="googlemail.com" />
+ <target host="www.googlemail.com" />
+ <target host="googlesource.com" />
+ <target host="*.googlesource.com" />
+ <target host="*.googlesyndication.com" />
+ <target host="www.googletagservices.com" />
+ <target host="googleusercontent.com" />
+ <target host="*.googleusercontent.com" />
+ <!--
+ Necessary for the Followers widget:
+
+ https://trac.torproject.org/projects/tor/ticket/7294
+ -->
+ <exclusion pattern="http://[^@:\./]+-opensocial\.googleusercontent\.com" />
+
+
+ <!-- Can we secure any of these wildcard cookies safely?
+ -->
+ <!--securecookie host="^\.google\.com$" name="^(hl|I4SUserLocale|NID|PREF|S)$" /-->
+ <!--securecookie host="^\.google\.[\w.]{2,6}$" name="^(hl|I4SUserLocale|NID|PREF|S|S_awfe)$" /-->
+ <securecookie host="^(?:accounts|adwords|\.code|login\.corp|developers|docs|\d\.docs|fiber|mail|picasaweb|plus|\.?productforums|support)\.google\.[\w.]{2,6}$" name=".+" />
+ <securecookie host="^www\.google\.com$" name="^GoogleAccountsLocale_session$" />
+ <securecookie host="^mail-attachment\.googleusercontent\.com$" name=".+" />
+ <securecookie host="^gmail\.com$" name=".+" />
+ <securecookie host="^www\.gmail\.com$" name=".+" />
+ <securecookie host="^googlemail\.com$" name=".+" />
+ <securecookie host="^www\.googlemail\.com$" name=".+" />
+
+
+ <!-- - lh 3-6 exist
+ - All appear identical
+ - Identical to lh\d.googleusercontent.com
+ -->
+ <rule from="^http://lh(\d)\.ggpht\.com/"
+ to="https://lh$1.ggpht.com/" />
+
+ <rule from="^http://lh(\d)\.google\.ca/"
+ to="https://lh$1.google.ca/" />
+
+
+ <rule from="^http://(www\.)?g(oogle)?mail\.com/"
+ to="https://$1g$2mail.com/" />
+
+ <rule from="^http://(?:www\.)?goo\.gl/"
+ to="https://goo.gl/" />
+
+
+ <!-- Redirects to http when rewritten to www:
+ -->
+ <rule from="^http://books\.google\.com/"
+ to="https://encrypted.google.com/" />
+
+ <!-- tisp$ 404s:
+ -->
+ <rule from="^http://(?:www\.)?google\.((?:com?\.)?\w{2,3})/tisp(?=$|\?)"
+ to="https://www.google.$1/tisp/" />
+
+ <!-- Paths that work on all in google.*
+ -->
+ <rule from="^http://(?:www\.)?google\.((?:com?\.)?\w{2,3})/(accounts|adplanner|ads|adsense|adwords|analytics|bookmarks|chrome|contacts|coop|cse|css|culturalinstitute|doodles|earth|favicon\.ico|finance|get|goodtoknow|googleblogs|grants|green|hostednews|images|intl|js|landing|logos|mapmaker|newproducts|news|nexus|patents|policies|prdhp|profiles|products|reader|s2|settings|shopping|support|tisp|tools|transparencyreport|trends|urchin|webmasters)(?=$|[?/])"
+ to="https://www.google.$1/$2" />
+
+ <!-- Paths that 404 on .ccltd, but work on .com:
+ -->
+ <rule from="^http://(?:www\.)?google\.(?:com?\.)?\w{2,3}/(?=calendar|dictionary|doubleclick|help|ideas|pacman|postini|powermeter|url)"
+ to="https://www.google.com/" />
+
+ <rule from="^http://(?:www\.)?google\.(?:com?\.)?\w{2,3}/custom"
+ to="https://www.google.com/cse" />
+
+ <!-- Paths that only exist/work on .com
+ -->
+ <rule from="^http://(?:www\.)?google\.com/(\+|appsstatus|books|buzz|extern_js|glass|googlebooks|ig|insights|moderator|phone|safebrowsing|videotargetting|webfonts)(?=$|[?/])"
+ to="https://www.google.com/$1" />
+
+ <!-- Subdomains that work on all in google.*
+ -->
+ <rule from="^http://(accounts|adwords|finance|groups|id|picasaweb|)\.google\.((?:com?\.)?\w{2,3})/"
+ to="https://$1.google.$2/" />
+
+ <!-- Subdomains that only exist/work on .com
+ -->
+ <rule from="^http://(apis|appengine|books|calendar|cbks0|chat|checkout|chrome|clients[12]|code|[\w-]+\.corp|developers|dl|docs\d?|\d\.docs|drive|encrypted|encrypted-tbn[123]|feedburner|fiber|fonts|gg|glass||health|helpouts|history|(?:hosted)?talkgadget|investor|lh\d|(?:chatenabled\.)?mail|pack|pki|play|plus(?:\.sandbox)?|plusone|productforums|profiles|safebrowsing-cache|cert-test\.sandbox|sb-ssl|script|security|services|servicessites|sites|spreadsheets\d?|support|talk|tools)\.google\.com/"
+ to="https://$1.google.com/" />
+
+ <exclusion pattern="^http://clients[0-9]\.google\.com/ocsp"/>
+
+ <rule from="^http://earth\.google\.com/"
+ to="https://www.google.com/earth/" />
+
+ <rule from="^http://scholar\.google\.((?:com?\.)?\w{2,3})/intl/"
+ to="https://www.google.$1/intl/" />
+
+ <rule from="^http://(?:encrypted-)?tbn2\.google\.com/"
+ to="https://encrypted-tbn2.google.com/" />
+
+
+ <rule from="^http://knoll?\.google\.com/"
+ to="https://knol.google.com/" />
+
+
+ <rule from="^http://news\.google\.(?:com?\.)?\w{2,3}/(?:$|news|newshp)"
+ to="https://www.google.com/news" />
+
+ <rule from="^http://trends\.google\.com/"
+ to="https://www.google.com/trends" />
+
+
+ <rule from="^http://([^/:@\.]+\.)?googlecode\.com/"
+ to="https://$1googlecode.com/" />
+
+ <rule from="^http://([^\./]\.)?googlesource\.com/"
+ to="https://$1googlesource.com/" />
+
+
+ <rule from="^http://partner\.googleadservices\.com/"
+ to="https://partner.googleadservices.com/" />
+
+ <rule from="^http://(pagead2|tpc)\.googlesyndication\.com/"
+ to="https://$1.googlesyndication.com/" />
+
+ <!-- !www doesn't exist.
+ -->
+ <rule from="^http://www\.googletagservices\.com/tag/js/"
+ to="https://www.googletagservices.com/tag/js/" />
+
+
+ <rule from="^http://([^@:\./]+)\.googleusercontent\.com/"
+ to="https://$1.googleusercontent.com/" />
+
+
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleShopping.xml b/searx/plugins/https_rules/GoogleShopping.xml
new file mode 100644
index 0000000..6ba69a9
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleShopping.xml
@@ -0,0 +1,28 @@
+<!--
+ For other Google coverage, see GoogleServices.xml.
+
+-->
+<ruleset name="Google Shopping">
+
+ <target host="google.*" />
+ <target host="www.google.*" />
+ <target host="google.co.*" />
+ <target host="www.google.co.*" />
+ <target host="*.google.com" />
+ <target host="google.com.*" />
+ <target host="www.google.com.*" />
+
+
+ <rule from="^http://encrypted\.google\.com/(prdhp|shopping)"
+ to="https://www.google.com/$1" />
+
+ <rule from="^http://shopping\.google\.com/"
+ to="https://shopping.google.com/" />
+
+ <rule from="^http://(?:encrypted|www)\.google\.com/(.*tbm=shop)"
+ to="https://www.google.com/$1" />
+
+ <rule from="^http://(?:www\.)?google\.((?:com?\.)?(?:ae|ar|at|au|bg|bh|bo|br|ca|ch|cl|cr|co|cu|de|ec|eg|es|fi|fr|gh|gt|hr|id|ie|il|in|it|jm|jo|jp|ke|kr|kw|kz|lb|lk|ly|mx|my|na|ng|nl|no|nz|om|pa|pe|pk|pl|pt|py|qa|ro|ru|rw|sa|sg|sl|se|sv|th|tr|ug|uk|uy|ve|vn|za|zw))/(?=prdhp|shopping)"
+ to="https://www.google.com/$1" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleSorry.xml b/searx/plugins/https_rules/GoogleSorry.xml
new file mode 100644
index 0000000..72a1921
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleSorry.xml
@@ -0,0 +1,7 @@
+<ruleset name="GoogleSorry">
+ <target host="sorry.google.com" />
+ <target host="www.google.com" />
+ <target host="google.com" />
+
+ <rule from="^http://((sorry|www)\.)?google\.com/sorry/" to="https://sorry.google.com/sorry/" />
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleTranslate.xml b/searx/plugins/https_rules/GoogleTranslate.xml
new file mode 100644
index 0000000..a004025
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleTranslate.xml
@@ -0,0 +1,8 @@
+<ruleset name="Google Translate (broken)" default_off="redirect loops">
+ <target host="translate.googleapis.com" />
+ <target host="translate.google.com" />
+
+ <rule from="^http://translate\.googleapis\.com/" to="https://translate.googleapis.com/"/>
+ <rule from="^http://translate\.google\.com/"
+ to="https://translate.google.com/" />
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleVideos.xml b/searx/plugins/https_rules/GoogleVideos.xml
new file mode 100644
index 0000000..a5e88fc
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleVideos.xml
@@ -0,0 +1,83 @@
+<ruleset name="Google Videos">
+ <target host="*.google.com" />
+ <target host="google.com" />
+ <target host="www.google.com.*" />
+ <target host="google.com.*" />
+ <target host="www.google.co.*" />
+ <target host="google.co.*" />
+ <target host="www.google.*" />
+ <target host="google.*" />
+
+ <rule from="^http://encrypted\.google\.com/videohp"
+ to="https://encrypted.google.com/videohp" />
+
+ <!-- https://videos.google.com is currently broken; work around that... -->
+ <rule from="^https?://videos?\.google\.com/$"
+ to="https://encrypted.google.com/videohp" />
+ <rule from="^http://(?:www\.)?google\.com/videohp"
+ to="https://encrypted.google.com/videohp" />
+ <rule from="^http://(?:images|www|encrypted)\.google\.com/(.*tbm=isch)"
+ to="https://encrypted.google.com/$1" />
+
+ <rule
+ from="^http://(?:www\.)?google\.(?:com?\.)?(?:au|ca|gh|ie|in|jm|ke|lk|my|na|ng|nz|pk|rw|sl|sg|ug|uk|za|zw)/videohp"
+ to="https://encrypted.google.com/videohp" />
+ <rule
+ from="^http://(?:www\.)?google\.(?:com?\.)?(?:ar|bo|cl|co|cu|cr|ec|es|gt|mx|pa|pe|py|sv|uy|ve)/videohp$"
+ to="https://encrypted.google.com/videohp?hl=es" />
+ <rule
+ from="^http://(?:www\.)?google\.(?:com\.)?(?:ae|bh|eg|jo|kw|lb|ly|om|qa|sa)/videohp$"
+ to="https://encrypted.google.com/videohp?hl=ar" />
+ <rule from="^http://(?:www\.)?google\.(?:at|ch|de)/videohp$"
+ to="https://encrypted.google.com/videohp?hl=de" />
+ <rule from="^http://(?:www\.)?google\.(fr|nl|it|pl|ru|bg|pt|ro|hr|fi|no)/videohp$"
+ to="https://encrypted.google.com/videohp?hl=$1" />
+ <rule from="^http://(?:www\.)?google\.com?\.(id|th|tr)/videohp$"
+ to="https://encrypted.google.com/videohp?hl=$1" />
+ <rule from="^http://(?:www\.)?google\.com\.il/videohp$"
+ to="https://encrypted.google.com/videohp?hl=he" />
+ <rule from="^http://(?:www\.)?google\.com\.kr/videohp$"
+ to="https://encrypted.google.com/videohp?hl=ko" />
+ <rule from="^http://(?:www\.)?google\.com\.kz/videohp$"
+ to="https://encrypted.google.com/videohp?hl=kk" />
+ <rule from="^http://(?:www\.)?google\.com\.jp/videohp$"
+ to="https://encrypted.google.com/videohp?hl=ja" />
+ <rule from="^http://(?:www\.)?google\.com\.vn/videohp$"
+ to="https://encrypted.google.com/videohp?hl=vi" />
+ <rule from="^http://(?:www\.)?google\.com\.br/videohp$"
+ to="https://encrypted.google.com/videohp?hl=pt-BR" />
+ <rule from="^http://(?:www\.)?google\.se/videohp$"
+ to="https://encrypted.google.com/videohp?hl=sv" />
+
+<!-- If there are URL parameters, keep them. -->
+ <rule
+ from="^http://(?:www\.)?google\.(?:com?\.)?(?:ar|bo|cl|co|cu|cr|ec|es|gt|mx|pa|pe|py|sv|uy|ve)/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=es&#38;" />
+ <rule
+ from="^http://(?:www\.)?google\.(?:com\.)?(?:ae|bh|eg|jo|kw|lb|ly|om|qa|sa)/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=ar&#38;" />
+ <rule from="^http://(?:www\.)?google\.(?:at|ch|de)/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=de&#38;" />
+ <rule from="^http://(?:www\.)?google\.(fr|nl|it|pl|ru|bg|pt|ro|hr|fi|no)/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=$1&#38;" />
+ <rule from="^http://(?:www\.)?google\.com?\.(id|th|tr)/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=$1&#38;" />
+ <rule from="^http://(?:www\.)?google\.com\.il/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=he&#38;" />
+ <rule from="^http://(?:www\.)?google\.com\.kr/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=ko&#38;" />
+ <rule from="^http://(?:www\.)?google\.com\.kz/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=kk&#38;" />
+ <rule from="^http://(?:www\.)?google\.com\.jp/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=ja&#38;" />
+ <rule from="^http://(?:www\.)?google\.com\.vn/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=vi&#38;" />
+ <rule from="^http://(?:www\.)?google\.com\.br/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=pt-BR&#38;" />
+ <rule from="^http://(?:www\.)?google\.se/videohp\?"
+ to="https://encrypted.google.com/videohp?hl=sv&#38;" />
+
+ <rule from="^http://video\.google\.com/ThumbnailServer2"
+ to="https://video.google.com/ThumbnailServer2" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/GoogleWatchBlog.xml b/searx/plugins/https_rules/GoogleWatchBlog.xml
new file mode 100644
index 0000000..afec70c
--- /dev/null
+++ b/searx/plugins/https_rules/GoogleWatchBlog.xml
@@ -0,0 +1,17 @@
+<!--
+ gwbhrd.appspot.com
+
+-->
+<ruleset name="GoogleWatchBlog">
+
+ <target host="googlewatchblog.de" />
+ <target host="*.googlewatchblog.de" />
+
+
+ <securecookie host="^(?:www)?\.googlewatchblog\.de$" name=".+" />
+
+
+ <rule from="^http://(static\.|www\.)?googlewatchblog\.de/"
+ to="https://$1googlewatchblog.de/" />
+
+</ruleset> \ No newline at end of file
diff --git a/searx/plugins/https_rules/Google_App_Engine.xml b/searx/plugins/https_rules/Google_App_Engine.xml
new file mode 100644
index 0000000..851e051
--- /dev/null
+++ b/searx/plugins/https_rules/Google_App_Engine.xml
@@ -0,0 +1,21 @@
+<!--
+ For other Google coverage, see GoogleServices.xml.
+
+-->
+<ruleset name="Google App Engine">
+
+ <target host="appspot.com" />
+ <target host="*.appspot.com" />
+ <!--
+ Redirects to http for some reason.
+ -->
+ <exclusion pattern="^http://photomunchers\.appspot\.com/" />
+
+
+ <securecookie host="^.+\.appspot\.com$" name=".+" />
+
+
+ <rule from="^http://([^@:\./]+\.)?appspot\.com/"
+ to="https://$1appspot.com/" />
+
+</ruleset> \ No newline at end of file
diff --git a/searx/plugins/https_rules/Googleplex.com.xml b/searx/plugins/https_rules/Googleplex.com.xml
new file mode 100644
index 0000000..7ddbb5b
--- /dev/null
+++ b/searx/plugins/https_rules/Googleplex.com.xml
@@ -0,0 +1,16 @@
+<!-- This rule was automatically generated based on an HSTS
+ preload rule in the Chromium browser. See
+ https://src.chromium.org/viewvc/chrome/trunk/src/net/base/transport_security_state.cc
+ for the list of preloads. Sites are added to the Chromium HSTS
+ preload list on request from their administrators, so HTTPS should
+ work properly everywhere on this site.
+
+ Because Chromium and derived browsers automatically force HTTPS for
+ every access to this site, this rule applies only to Firefox. -->
+<ruleset name="Googleplex.com (default off)" platform="firefox" default_off="Certificate error">
+ <target host="googleplex.com" />
+
+ <securecookie host="^googleplex\.com$" name=".+" />
+
+ <rule from="^http://googleplex\.com/" to="https://googleplex.com/" />
+</ruleset>
diff --git a/searx/plugins/https_rules/OpenStreetMap.xml b/searx/plugins/https_rules/OpenStreetMap.xml
new file mode 100644
index 0000000..58a6618
--- /dev/null
+++ b/searx/plugins/https_rules/OpenStreetMap.xml
@@ -0,0 +1,15 @@
+<ruleset name="OpenStreetMap">
+
+ <target host="openstreetmap.org"/>
+ <target host="*.openstreetmap.org"/>
+
+ <rule from="^http://(?:www\.)?openstreetmap\.org/"
+ to="https://www.openstreetmap.org/"/>
+
+ <rule from="^http://tile\.openstreetmap\.org/"
+ to="https://a.tile.openstreetmap.org/"/>
+
+ <rule from="^http://(blog|help|lists|nominatim|piwik|taginfo|[abc]\.tile|trac|wiki)\.openstreetmap\.org/"
+ to="https://$1.openstreetmap.org/"/>
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Rawgithub.com.xml b/searx/plugins/https_rules/Rawgithub.com.xml
new file mode 100644
index 0000000..3868f33
--- /dev/null
+++ b/searx/plugins/https_rules/Rawgithub.com.xml
@@ -0,0 +1,14 @@
+<!--
+ www: cert only matches ^rawgithub.com
+
+-->
+<ruleset name="rawgithub.com">
+
+ <target host="rawgithub.com" />
+ <target host="www.rawgithub.com" />
+
+
+ <rule from="^http://(?:www\.)?rawgithub\.com/"
+ to="https://rawgithub.com/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Soundcloud.xml b/searx/plugins/https_rules/Soundcloud.xml
new file mode 100644
index 0000000..6958e8c
--- /dev/null
+++ b/searx/plugins/https_rules/Soundcloud.xml
@@ -0,0 +1,101 @@
+<!--
+
+ CDN buckets:
+
+ - akmedia-a.akamaihd.net
+
+ - soundcloud.assistly.com
+
+ - help.soundcloud.com
+
+ - cs70.wac.edgecastcdn.net
+
+ - a1.sndcdn.com
+ - i1.sndcdn.com
+ - w1.sndcdn.com
+
+ - wpc.658D.edgecastcdn.net
+ - m-a.sndcdn.com.edgesuite.net
+ - soundcloud.gettyimages.com
+
+ - scbackstage.wpengine.netdna-cdn.com
+
+ - ssl doesn't exist
+ - backstage.soundcloud.com
+
+ - soundcloud.wpengine.netdna-cdn.com
+
+ - -ssl doesn't exist
+ - blog.soundcloud.com
+
+ - gs1.wpc.v2cdn.netcdn.net
+ - gs1.wpc.v2cdn.net
+
+ - ec-media.soundcloud.com
+
+ Nonfunctional soundcloud.com subdomains:
+
+ - help (redirects to http, mismatched, CN: *.assistly.com)
+ - m (redirects to http)
+ - media
+ - status (times out)
+
+
+ Problematic domains:
+
+ - m-a.sndcdn.com (works, akamai)
+
+
+ Partially covered domains:
+
+ - backstage.soundcloud.com
+
+
+ Fully covered domains:
+
+ - sndcdn.com subdomains:
+
+ - a[12]
+ - api
+ - i[1-4]
+ - w[12]
+ - wis
+
+ - soundcloud.com subdomains:
+
+ - (www.)
+ - api
+ - blog
+ - connect
+ - developers
+ - ec-media
+ - eventlogger
+ - help-assets
+ - media
+ - visuals
+ - w
+
+-->
+<ruleset name="Soundcloud (partial)">
+
+ <target host="scbackstage.wpengine.netdna-cdn.com" />
+ <target host="soundcloud.wpengine.netdna-cdn.com" />
+ <target host="*.sndcdn.com" />
+ <target host="soundcloud.com" />
+ <target host="*.soundcloud.com" />
+ <exclusion pattern="^https?://(?:scbackstage\.wpengine\.netdna-cdn|backstage\.soundcloud)\.com/(?!wp-content/)" />
+
+
+ <rule from="^http://([aiw]\d|api|wis)\.sndcdn\.com/"
+ to="https://$1.sndcdn.com/" />
+
+ <rule from="^http://((?:api|backstage|blog|connect|developers|ec-media|eventlogger|help-assets|media|visuals|w|www)\.|)soundcloud\.com/"
+ to="https://$1soundcloud.com/" />
+
+ <rule from="^https?://scbackstage\.wpengine\.netdna-cdn\.com/"
+ to="https://backstage.soundcloud.com/" />
+
+ <rule from="^https?://soundcloud\.wpengine\.netdna-cdn\.com/"
+ to="https://blog.soundcloud.com/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/ThePirateBay.xml b/searx/plugins/https_rules/ThePirateBay.xml
new file mode 100644
index 0000000..010387b
--- /dev/null
+++ b/searx/plugins/https_rules/ThePirateBay.xml
@@ -0,0 +1,36 @@
+<!--
+ Nonfunctional:
+
+ - image.bayimg.com
+ - (www.)thepiratebay.sx (http reply)
+
+
+ For problematic rules, see ThePirateBay-mismatches.xml.
+
+-->
+<ruleset name="The Pirate Bay (partial)">
+
+ <target host="suprbay.org" />
+ <target host="*.suprbay.org" />
+ <!-- * for cross-domain cookie -->
+ <target host="*.forum.suprbay.org" />
+ <target host="thepiratebay.org"/>
+ <target host="*.thepiratebay.org"/>
+ <target host="thepiratebay.se"/>
+ <target host="*.thepiratebay.se"/>
+
+ <securecookie host="^.*\.suprbay\.org$" name=".*" />
+ <securecookie host="^(.*\.)?thepiratebay\.se$" name=".*"/>
+
+
+ <!-- Cert doesn't match (www.), redirects like so. -->
+ <rule from="^https?://(?:forum\.|www\.)?suprbay\.org/"
+ to="https://forum.suprbay.org/" />
+
+ <rule from="^http://(?:www\.)?thepiratebay\.(?:org|se)/"
+ to="https://thepiratebay.se/"/>
+
+ <rule from="^http://(rss|static|torrents)\.thepiratebay\.(?:org|se)/"
+ to="https://$1.thepiratebay.se/"/>
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Torproject.xml b/searx/plugins/https_rules/Torproject.xml
new file mode 100644
index 0000000..69269af
--- /dev/null
+++ b/searx/plugins/https_rules/Torproject.xml
@@ -0,0 +1,18 @@
+<ruleset name="Tor Project">
+
+ <target host="torproject.org" />
+ <target host="*.torproject.org" />
+ <exclusion pattern="^http://torperf\.torproject\.org/" />
+
+
+ <!-- Not secured by server:
+ -->
+ <!--securecookie host="^\.blog\.torproject\.org$" name="^SESS[0-9a-f]{32}$" /-->
+
+ <securecookie host="^(?:.*\.)?torproject\.org$" name=".+" />
+
+
+ <rule from="^http://([^/:@\.]+\.)?torproject\.org/"
+ to="https://$1torproject.org/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Twitter.xml b/searx/plugins/https_rules/Twitter.xml
new file mode 100644
index 0000000..3285f44
--- /dev/null
+++ b/searx/plugins/https_rules/Twitter.xml
@@ -0,0 +1,169 @@
+<!--
+ Other Twitter rulesets:
+
+ - Twitter_Community.com.xml
+
+
+ Nonfunctional domains:
+
+ - status.twitter.com *
+ - status.twitter.jp *
+
+ * Tumblr
+
+
+ CDN buckets:
+
+ - a1095.g.akamai.net/=/1095/134446/1d/platform.twitter.com/ | platform2.twitter.com.edgesuite.net
+
+ - platform2.twitter.com
+
+ - twitter-any.s3.amazonaws.com
+ - twitter-blog.s3.amazonaws.com
+
+ - d2rdfnizen5apl.cloudfront.net
+
+ - s.twimg.com
+
+ - ssl2.twitter.com.edgekey.net
+ - twitter.github.com
+
+
+ Problematic domains:
+
+ - twimg.com subdomains:
+
+ - a5 *
+ - s (cloudfront)
+
+ - twitter.com subdomains:
+
+ - platform[0-3] (403, akamai)
+
+ * akamai
+
+
+ Fully covered domains:
+
+ - (www.)t.co (www → ^)
+
+ - twimg.com subdomains:
+
+ - a[5-9] (→ si0)
+ - a\d
+ - abs
+ - dnt
+ - ea
+ - g
+ - g2
+ - gu
+ - hca
+ - jp
+ - ma
+ - ma[0123]
+ - o
+ - p
+ - pbs
+ - r
+ - s (→ d2rdfnizen5apl.cloudfront.net)
+ - si[0-5]
+ - syndication
+ - cdn.syndication
+ - tailfeather
+ - ton
+ - v
+ - widgets
+
+ - twitter.com subdomains:
+
+ - (www.)
+ - 201[012]
+ - about
+ - ads
+ - analytics
+ - api
+ - cdn.api
+ - urls.api
+ - blog
+ - business
+ - preview.cdn
+ - preview-dev.cdn
+ - preview-stage.cdn
+ - de
+ - dev
+ - en
+ - engineering
+ - es
+ - firefox
+ - fr
+ - it
+ - ja
+ - jp
+ - m
+ - media
+ - mobile
+ - music
+ - oauth
+ - p
+ - pic
+ - platform
+ - platform[0-3] (→ platform)
+ - widgets.platform
+ - search
+ - static
+ - support
+ - transparency
+ - upload
+
+
+ These altnames don't exist:
+
+ - i3.twimg.com
+ - p-dev.twimg.com
+ - vmtc.twimg.com
+
+ - cdn-dev.api.twitter.com
+
+-->
+<ruleset name="Twitter">
+
+ <target host="t.co" />
+ <target host="*.t.co" />
+ <target host="*.twimg.com" />
+ <target host="twitter.com" />
+ <target host="*.twitter.com" />
+
+
+ <!-- Secured by server:
+ -->
+ <!--securecookie host="^\.twitter\.com$" name="^_twitter_sess$" /-->
+ <!--securecookie host="^support\.twitter\.com$" name="^_help_center_session$" /-->
+ <!--
+ Not secured by server:
+ -->
+ <!--securecookie host="^\.t\.co$" name="^muc$" /-->
+ <!--securecookie host="^\.twitter\.com$" name="^guest_id$" /-->
+
+ <securecookie host="^\.t\.co$" name=".+" />
+ <securecookie host="^(?:.*\.)?twitter\.com$" name=".+" />
+
+
+ <rule from="^http://(?:www\.)?t\.co/"
+ to="https://t.co/" />
+
+ <rule from="^http://a[5-9]\.twimg\.com/"
+ to="https://si0.twimg.com/" />
+
+ <rule from="^http://(abs|a\d|dnt|ea|g[2u]?|hca|jp|ma\d?|o|p|pbs|r|si\d|(?:cdn\.)?syndication|tailfeather|ton|v|widgets)\.twimg\.com/"
+ to="https://$1.twimg.com/" />
+
+ <rule from="^http://s\.twimg\.com/"
+ to="https://d2rdfnizen5apl.cloudfront.net/" />
+
+ <rule from="^http://((?:201\d|about|ads|analytics|blog|(?:cdn\.|urls\.)?api|business|preview(?:-dev|-stage)?\.cdn|de|dev|engineering|en|es|firefox|fr|it|ja|jp|m|media|mobile|music|oauth|p|pic|platform|widgets\.platform|search|static|support|transparency|upload|www)\.)?twitter\.com/"
+ to="https://$1twitter.com/" />
+
+ <rule from="^http://platform\d\.twitter\.com/"
+ to="https://platform.twitter.com/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/Vimeo.xml b/searx/plugins/https_rules/Vimeo.xml
new file mode 100644
index 0000000..f2a3e57
--- /dev/null
+++ b/searx/plugins/https_rules/Vimeo.xml
@@ -0,0 +1,75 @@
+<!--
+ CDN buckets:
+
+ - av.vimeo.com.edgesuite.net
+
+ - a808.g.akamai.net
+
+ - pdl.vimeocdn.com.edgesuite.net
+
+ - a1189.g.akamai.net
+
+
+ Problematic subdomains:
+
+ - av (pdl.../crossdomain.xml restricts to port 80)
+ - pdl (works, akamai)
+
+
+ Partially covered subdomains:
+
+ - developer (some pages redirect to http)
+ - pdl (→ akamai)
+
+
+ Fully covered subdomains:
+
+ - (www.)
+ - secure
+
+
+Default off per https://trac.torproject.org/projects/tor/ticket/7569 -->
+<ruleset name="Vimeo (default off)" default_off="breaks some video embedding">
+
+ <target host="vimeo.com" />
+ <target host="*.vimeo.com" />
+ <exclusion pattern="^http://av\.vimeo\.com/crossdomain\.xml" />
+ <!--exclusion pattern="^http://developer\.vimeo\.com/($|\?|(apps|guidelines|help|player)($|[?/]))" /-->
+ <exclusion pattern="^http://developer\.vimeo\.com/(?!apis(?:$|[?/])|favicon\.ico)" />
+ <target host="*.vimeocdn.com" />
+ <!--
+ Uses crossdomain.xml from s3.amazonaws.com, which sets secure="false"
+
+ https://mail1.eff.org/pipermail/https-everywhere/2012-October/001583.html
+ -->
+ <exclusion pattern="^http://a\.vimeocdn\.com/p/flash/moogaloop/" />
+
+ <!-- We cannot secure streams because crossdomain.xml
+ restricts to port 80 :(
+ -->
+ <exclusion pattern="^http://pdl\.vimeocdn\.com/(?!crossdomain\.xml)" />
+
+
+ <!-- Tracking cookies:
+ -->
+ <securecookie host="^\.(?:player\.)?vimeo\.com$" name="^__utm\w$" />
+
+
+ <rule from="^http://((?:developer|player|secure|www)\.)?vimeo\.com/"
+ to="https://$1vimeo.com/" />
+
+ <rule from="^http://av\.vimeo\.com/"
+ to="https://a248.e.akamai.net/f/808/9207/8m/av.vimeo.com/" />
+
+ <!-- a & b: Akamai -->
+ <rule from="^http://(?:secure-)?([ab])\.vimeocdn\.com/"
+ to="https://secure-$1.vimeocdn.com/" />
+
+ <rule from="^http://i\.vimeocdn\.com/"
+ to="https://i.vimeocdn.com/" />
+
+ <rule from="^http://pdl\.vimeocdn\.com/"
+ to="https://a248.e.akamai.net/f/1189/4415/8d/pdl.vimeocdn.com/" />
+
+</ruleset>
+
diff --git a/searx/plugins/https_rules/WikiLeaks.xml b/searx/plugins/https_rules/WikiLeaks.xml
new file mode 100644
index 0000000..977709d
--- /dev/null
+++ b/searx/plugins/https_rules/WikiLeaks.xml
@@ -0,0 +1,13 @@
+<ruleset name="WikiLeaks">
+
+ <target host="wikileaks.org" />
+ <target host="*.wikileaks.org" />
+
+
+ <securecookie host="^(?:w*\.)?wikileaks\.org$" name=".+" />
+
+
+ <rule from="^http://((?:chat|search|shop|www)\.)?wikileaks\.org/"
+ to="https://$1wikileaks.org/" />
+
+</ruleset> \ No newline at end of file
diff --git a/searx/plugins/https_rules/Wikimedia.xml b/searx/plugins/https_rules/Wikimedia.xml
new file mode 100644
index 0000000..9f25831
--- /dev/null
+++ b/searx/plugins/https_rules/Wikimedia.xml
@@ -0,0 +1,107 @@
+<!--
+ Wikipedia and other Wikimedia Foundation wikis previously had no real HTTPS support, and
+ URLs had to be rewritten to https://secure.wikimedia.org/$wikitype/$language/ . This is no
+ longer the case, see https://blog.wikimedia.org/2011/10/03/native-https-support-enabled-for-all-wikimedia-foundation-wikis/ ,
+ so this file is a lot simpler these days.
+
+
+ Mixed content:
+
+ - Images, on:
+
+ - stats.wikimedia.org from upload.wikimedia.org *
+ - stats.wikimedia.org from wikimediafoundation.org *
+
+ * Secured by us
+
+-->
+<ruleset name="Wikimedia">
+
+ <target host="enwp.org" />
+ <target host="frwp.org" />
+
+ <target host="mediawiki.org" />
+ <target host="www.mediawiki.org" />
+ <target host="wikimedia.org" />
+ <target host="*.wikimedia.org" />
+ <exclusion pattern="^http://(?:apt|cs|cz|parsoid-lb\.eqiad|status|torrus|ubuntu)\.wikimedia\.org" />
+ <!-- https://mail1.eff.org/pipermail/https-everywhere-rules/2012-June/001189.html -->
+ <exclusion pattern="^http://lists\.wikimedia\.org/pipermail(?:$|/)" />
+ <target host="wikimediafoundation.org" />
+ <target host="www.wikimediafoundation.org" />
+
+ <!-- Wikimedia projects (also some wikimedia.org subdomains) -->
+ <target host="wikibooks.org" />
+ <target host="*.wikibooks.org" />
+ <target host="wikidata.org" />
+ <target host="*.wikidata.org" />
+ <target host="wikinews.org" />
+ <target host="*.wikinews.org" />
+ <target host="wikipedia.org" />
+ <target host="*.wikipedia.org" />
+ <target host="wikiquote.org" />
+ <target host="*.wikiquote.org" />
+ <target host="wikisource.org" />
+ <target host="*.wikisource.org" />
+ <target host="wikiversity.org" />
+ <target host="*.wikiversity.org" />
+ <target host="wikivoyage.org" />
+ <target host="*.wikivoyage.org" />
+ <target host="wiktionary.org" />
+ <target host="*.wiktionary.org" />
+
+ <!-- Wikimedia chapters -->
+ <target host="wikimedia.ca" />
+ <target host="www.wikimedia.ca" />
+
+ <!-- Wikimedia Tool Labs -->
+ <target host="tools.wmflabs.org" />
+ <target host="icinga.wmflabs.org" />
+ <target host="ganglia.wmflabs.org" />
+
+ <!-- Not secured by server:
+ -->
+ <!--securecookie host="^\.wiki(books|ipedia)\.org$" name="^GeoIP$" /-->
+
+ <securecookie host="^^\.wik(?:ibooks|idata|imedia|inews|ipedia|iquote|isource|iversity|ivoyage|tionary)\.org$" name="^GeoIP$" />
+ <securecookie host="^([^@:/]+\.)?wik(ibooks|idata|inews|ipedia|iquote|isource|iversity|ivoyage|tionary)\.org$" name=".*" />
+ <securecookie host="^(species|commons|meta|incubator|wikitech).wikimedia.org$" name=".*" />
+ <securecookie host="^(?:www\.)?mediawiki\.org$" name=".*" />
+ <securecookie host="^wikimediafoundation.org$" name=".*" />
+
+ <rule from="^http://(en|fr)wp\.org/"
+ to="https://$1.wikipedia.org/wiki/" />
+
+ <rule from="^http://(?:www\.)?mediawiki\.org/"
+ to="https://www.mediawiki.org/" />
+
+ <rule from="^https?://download\.wikipedia\.org/"
+ to="https://dumps.wikimedia.org/" />
+
+ <rule from="^https?://(download|dataset2|sitemap)\.wikimedia\.org/"
+ to="https://dumps.wikimedia.org/" />
+
+ <rule from="^https?://(labs-ns[01]|virt0)\.wikimedia\.org/"
+ to="https://wikitech.wikimedia.org/" />
+
+ <rule from="^https?://noboard\.chapters\.wikimedia\.org/"
+ to="https://noboard-chapters.wikimedia.org/" />
+
+ <rule from="^https?://wg\.en\.wikipedia\.org/"
+ to="https://wg-en.wikipedia.org/" />
+
+ <rule from="^https?://arbcom\.(de|en|fi|nl)\.wikipedia\.org/"
+ to="https://arbcom-$1.wikipedia.org/" />
+
+ <rule from="^http://([^@:/]+\.)?wik(ibooks|idata|imedia|inews|ipedia|iquote|isource|iversity|ivoyage|tionary)\.org/"
+ to="https://$1wik$2.org/" />
+
+ <rule from="^http://(www\.)?wikimediafoundation\.org/"
+ to="https://$1wikimediafoundation.org/" />
+
+ <rule from="^http://(www\.)?wikimedia\.ca/"
+ to="https://wikimedia.ca/" />
+
+ <rule from="^http://([^@:/]+)\.wmflabs\.org/"
+ to="https://$1.wmflabs.org/" />
+</ruleset>
diff --git a/searx/plugins/https_rules/Yahoo.xml b/searx/plugins/https_rules/Yahoo.xml
new file mode 100644
index 0000000..33548c4
--- /dev/null
+++ b/searx/plugins/https_rules/Yahoo.xml
@@ -0,0 +1,2450 @@
+<!--
+ Other Yahoo rulesets:
+
+ - Flickr.xml
+ - Lexity.com.xml
+ - Right-Media.xml
+ - Yahoo.com.tw.xml
+ - Yahoo.net.xml
+ - Yahoo_APIs.xml
+ - Yahoo_Japan.xml
+ - Yho.com.xml
+ - Yimg.com.xml
+ - YUI_Library.xml
+
+
+ CDN buckets:
+
+ - ipgcdn-a.akamaihd.net
+ - yahootv.flyingfishes.com.br
+ - yahoosports.teamfanshop.com
+
+
+ Nonfunctional domains:
+
+ - yahoo.com subdomains:
+
+ - account ⁵
+ - cn.adspecs ¹
+ - tw.adspecs ¹
+ - alerts ¹
+
+ - co.astrology ⁵
+ - espanol.astrology ⁵
+ - mx.astrology ⁵
+
+ - auction ¹
+
+ - biz subdomains:
+
+ - au.rss ¹
+ - nz.rss ¹
+
+ - bookmarks ⁵
+ - buzz ¹
+
+ - cn subdomains:
+
+ - ^ ¹
+ - help ¹
+ - news ¹
+
+ - docs subdomains:
+
+ - ^ ⁵
+ - ar ⁵
+ - fr ⁵
+ - uk ⁵
+
+ - au.rss.food (403, valid cert)
+ - au.forums ¹
+ - ar.games ⁵
+ - help.cc.hk ⁵
+ - hsrd ¹
+ - labs ¹
+
+ - lifestyle subdomains:
+
+ - tw.ipeen ¹
+ - au.rss ³
+ - nz.rss ³
+ - tw ⁵
+
+ - cn.overview.mail ¹
+
+ - cf.maps (404; mismatched, CN: www.yahoo.com)
+ - gws2.maps ¹
+ - kr.mobile ⁵
+ - tw.music ⁵
+
+ - my subdomains:
+
+ - ar ⁵
+ - au ²
+ - br ²
+ - ca ²
+ - de ²
+ - es ²
+ - fr ²
+ - hk ²
+ - ie ¹
+ - in ²
+ - it ²
+ - kr ¹
+ - mx ²
+ - nz ²
+ - qc ²
+ - sg ²
+ - tw ²
+ - cm.tw ⁸
+ - uk ²
+
+ - \w\w.news:
+
+ - cn ¹
+ - kr ¹
+ - se ¹
+
+ - opi ¹
+ - au.pfinance ²
+ - ar.rd ¹
+ - research ¹
+ - rightmedia (shows speakers.watersmartinnovations.com; mismatched, CN: *.watersmartinnovations.com)
+
+ - search subdomains:
+
+ - us.recipes ¹
+ - gossip-ss.us ¹
+
+ - \w\w.yhs:
+
+ - ar ¹
+ - au ¹
+ - br ¹
+ - ca ¹
+ - de ¹
+ - es ¹
+ - fr ¹
+ - hk ¹
+ - in ¹
+ - it ¹
+ - kr ¹
+ - mx ¹
+ - my ¹
+ - nz ¹
+ - ph ¹
+ - se ¹
+ - sg ¹
+ - tw ¹
+ - uk ¹
+ - us ¹
+ - vn ¹
+
+ - searchmarketing ¹
+ - au.shopping ⁹
+ - es.shopping ⁵
+ - suggestions ⁵
+ - au.rss.thehype ³
+
+ - video subdomains:
+
+ - malaysia ¹
+ - my ¹
+ - ph ¹
+ - sg ¹
+ - tw ¹
+
+ - voices ⁵
+ - cn.weather ¹
+ - visit.webhosting ⁵
+ - count.yisou ¹
+
+ - youth subdomains:
+
+ - au.rss ³
+ - nz.rss ³
+
+ - ypolicyblog.com (reset)
+ - www.ypolicyblog.com
+
+ ¹ Refused
+ ² Redirects to http, valid cert
+ ³ 404, valid cert
+ ⁴ Redirects to http; mismatched, CN: www.yahoo.com
+ ⁵ Dropped
+ ⁶ Recursive redirect
+ ⁷ 404; mismatched, CN: *.news.yahoo.com
+ ⁸ Redirects to http; mismatched, CN: *.news.yahoo.com
+ ⁹ "Incorrect Host in URL"
+
+ Problematic domains:
+
+ - i.acdn.us ¹
+ - cm.npc-morris.overture.com ²
+ - cm.npc-nydn.overture.com ²
+ - totaltravel.co.uk ³
+ - www.totaltravel.co.uk ⁴
+ - totaltravel.com ³
+ - www.totaltravel.com ⁴
+
+ yahoo.com subdomains:
+
+ - fr.actualites ⁴
+ - advertisingcentral ⁴
+
+ - cl.answers ⁴
+ - co.answers ⁴
+ - pe.answers ⁴
+ - ve.answers ⁴
+
+ - au.astrology ⁷
+ - ca.astrology ⁴
+ - nz.astrology ⁷
+
+ - ar.autos ⁴
+ - de.autos ⁴
+ - fr.autos ⁴
+ - mx.autos ⁴
+
+ - axis ¹
+ - id.berita ⁵
+
+ - au.biz ⁷
+ - nz.biz ⁷
+
+ - \w\w.careers: (works; mismatched, CN: www.yahoo.com)
+
+ - au
+ - ca
+ - de
+ - fr
+ - hk
+ - id
+ - ie
+ - in
+ - it
+ - jp
+ - my
+ - no
+ - ph
+ - qc ¹
+ - sg
+ - tw
+ - uk
+ - us
+ - vn
+
+ - malaysia.careers ¹
+ - cars ¹
+ - tw.help.cc ¹
+ - cine ¹
+ - cn (reset)
+ - connectedtv (works; mismatched, CN: smarttv.yahoo.com)
+ - cl.deportes ⁴
+ - co.deportes ⁴
+ - es.deportes ⁴
+ - pe.deportes ⁴
+ - ve.deportes ⁴
+ - au.dir ⁷
+ - au.docs (works; mismatched, CN: *.yahoo7.com.au)
+ - hk.ent ⁴
+ - br.esportes ⁴
+ - es.everything ⁴
+ - fr.eurosport ⁴
+ - fr.divertissement ⁵
+ - dk ⁴
+ - fantasysports ⁴
+ - es.laliga.fantasysports ⁴
+ - tw.fashion ⁵
+ - feedback ⁴
+ - chart.finance ⁴
+ - ichart.finance ⁴
+ - ie.finance ⁴
+ - kr.finance (404, valid cert)
+ - au.food (403; mismatched, CN: *.yahoo7.com.au)
+ - nz.food (403; mismatched, CN: *.yahoo7.com.au)
+ - au.forums ⁷
+
+ - games subdomains:
+
+ - br ⁴
+ - de ⁴
+ - es ⁴
+ - fr ⁴
+ - id ⁴
+ - it ⁴
+ - malaysia ⁴
+ - nz ⁴
+ - ph ⁴
+
+ - it.giochi ⁵
+ - ie.groups ⁴
+ - kr.gugi ⁴
+ - au.gwn7 (mixed css from l.yimg.com)
+ - fr.help ⁴
+ - help.cc.hk ⁴
+ - fr.jeux ⁵
+ - es.juegos ⁵
+ - kr ⁴
+
+ - lifestyle subdomains:
+
+ - ar ⁴
+ - br ⁴
+ - ca ⁴
+ - es ⁴
+ - es-us ⁴
+ - fr ⁴
+ - ie ⁴
+ - it ⁴
+
+ - ca.local (dropped, redirect destination cert mismatched)
+ - fr.local ⁴
+ - es.maps ⁴
+ - in.maps ⁴
+ - kr.maps ⁴
+ - mx.maps ⁴
+ - nz.maps ⁴
+
+ - external.global.media ⁵
+ - au.messages ⁷
+ - ie.messenger ⁴
+ - nz.messenger ⁷
+ - tw.messenger ⁴
+ - dk.mobile ⁴
+ - ie.mobile ⁴
+ - no.mobile ⁴
+ - webservices.mobile (works, self-signed)
+ - tw.atm.money (works; mismatched, CN: tw.campaign.money.yahoo.com)
+
+ - br.movies ¹
+ - fr.movies ¹
+ - es.movies ⁴
+ - es-us.movies ⁴
+ - it.movies ⁴
+
+ - br.mulher ⁵
+ - hk.music ¹
+ - tw.music ⁵
+ - fr.musique ⁵
+
+ - news subdomains:
+
+ - ar ⁴
+ - br ⁴
+ - cl ⁴
+ - co ⁴
+ - de ⁴
+ - dk ⁴
+ - id ⁴
+ - ie ⁴
+ - it ⁴
+ - mx ⁴
+ - pe ⁴
+ - qc ⁴
+ - au.rss (mixed css from l.yimg.com)
+ - ve ⁴
+
+ - no ⁴
+ - notepad (works; mismatched, CN: *.calendar.yahoo.com)
+ - it.notizie ⁵
+
+ - on ⁴
+ - it.oroscopo ⁵
+ - fr.pourelles ⁵
+ - br.esporteinterativo ⁵
+ - id.olahraga ⁵
+ - au.prime7 (mixed css from l.yimg.com)
+ - ru ⁴
+
+ - safely subdomains: ⁴
+
+ - ar
+ - br
+ - cl
+ - es
+ - es-us
+ - malaysia
+ - pe
+ - ve
+ - vn
+
+ - cn.search ⁴
+ - my.images.search ⁴
+ - kr.images.search ⁴
+ - nz.maps.search ⁴
+ - my.search ⁴
+ - my.video.search ⁴
+ - kr.searchad ¹
+
+ - ph.she ⁵
+ - fr.sites ⁵
+
+ - de.solutions ¹
+ - es.solutions ¹
+ - fr.solutions ¹
+ - it.solutions ¹
+ - nz.solutions ⁷
+ - uk.solutions ¹
+
+ - sport ⁴
+
+ - sports subdomains:
+
+ - ar ⁴
+ - br ⁴
+ - de ⁴
+ - es ⁴
+ - id ⁴
+ - in ⁴
+ - uk ⁴
+
+ - br.tempo ⁵
+ - es.tendencias ⁵
+ - au.todaytonight (403, valid cert)
+
+ - au.travel ⁷
+ - ca.travel ⁴
+ - id.travel ⁴
+ - my.travel ⁴
+ - nz.travel ⁷
+ - ph.travel ⁴
+ - uk.travel ⁴
+ - ca.tv ⁴
+ - pe.tv ⁴
+
+ - video subdomains:
+
+ - ^ ⁴
+ - ar ⁴
+ - au ⁴
+ - br ⁴
+ - ca ⁴
+ - co ⁴
+ - de ⁴
+ - es ⁴
+ - es-us ⁴
+ - fr ⁴
+ - hk ⁴
+ - in ⁴
+ - it ⁴
+ - pe ⁴
+ - mx ⁴
+ - uk ⁴
+ - ve ⁴
+
+ - fr.voyage (works; expired 2013-01-08, mismatched, CN: uk.travel.yahoo.com)
+
+ - weather subdomains:
+
+ - ar ⁴
+ - au ⁴
+ - br ⁴
+ - cl ⁴
+ - co ⁴
+ - de ⁴
+ - es ⁴
+ - espanol ⁴
+ - fr ⁴
+ - it ⁴
+ - kr ⁴
+ - mx ⁴
+ - pe ⁴
+ - tw ⁴
+ - mx ⁴
+ - ve ⁴
+
+ - widgets (works; mismatched, CN: smarttv.yahoo.com)
+ - au.youth (works; mismatched, CN: yahoo.com.au)
+
+
+ - (www.)yhoo.it ⁴ (bit.ly alias)
+
+ ¹ Dropped
+ ² Works, mismatched, CN: *.ysm.yahoo.com
+ ³ Works; mismatched, CN: builder.totaltravel.com
+ ⁴ Refused
+ ⁵ Works; mismatched, CN: *.news.yahoo.com
+ ⁶ Works; mismatched, CN: address.yahoo.com
+ ⁷ "Incorrect Host in URL"
+
+
+ Partially covered domains:
+
+ - (www.)totaltravel.com (→ au.totaltravel.yahoo.com, haven't found images/)
+
+ - yahoo.com subdomains:
+
+ - advertisingcentral ¹ (→ advertising)
+ - fantasysports ¹ (-> sports)
+ - in.sports (→ cricket, /*(?!$) doesn't redirect)
+ - nz.video (→ nz.news, \w.* 404s)
+
+ ¹ Some paths other than root don't redirect
+ ⁵ Avoiding false/broken MCB
+
+
+ Fully covered domains:
+
+ - i.acdn.us (→ s.yimg.com/ck/)
+
+ - (www.)totaltravel.co.uk (→ au.totaltravel.yahoo.com)
+
+ - yahoo.com subdomains:
+
+ - (www.)
+
+ - \w\w:
+
+ - ar
+ - au
+ - br
+ - ca
+ - cl
+ - cn (→ sg)
+ - co
+ - de
+ - dk (→ www)
+ - e1 (→ espanol)
+ - es
+ - fr
+ - gr
+ - hk
+ - id
+ - ie
+ - in
+ - it
+ - kr (→ tools.search)
+ - mx
+ - no (→ www)
+ - nz
+ - pe
+ - ph
+ - qc
+ - ru (→ www)
+ - se
+ - sg
+ - tw
+ - ve
+ - vn
+ - uk
+ - us
+
+ - fr.actualites (→ fr.news)
+ - fr-ca.actualites
+ - address
+
+ - \w\w.address:
+
+ - ca
+ - e1
+ - fr
+ - hk
+ - nz
+
+ - admanager
+
+ - \w\w.adserver:
+
+ - au
+ - uk
+ - us
+
+ - global.adserver
+ - adspecs
+
+ - \w+.adspecs:
+
+ - au
+ - de
+ - es
+ - fr
+ - hk
+ - in
+ - it
+ - latam
+ - nz
+ - sea
+ - uk
+
+ - \w+.adspecs-new:
+
+ - in
+ - sea
+
+ - advertising
+
+ - \w\w.advertising:
+
+ - au
+ - ca
+ - fr
+ - nz
+
+ - beap.adx
+ - c5.ah
+ - c5a.ah
+ - cookex.amp
+ - s-cookex.amp
+
+ - analytics subdomains:
+
+ - [aoyz]
+ - apac
+ - y3
+
+ - anc
+ - answers
+
+ - \w\w.answers:
+
+ - ar
+ - au
+ - br
+ - ca
+ - cl (→ espanol.answers)
+ - co (→ espanol.answers)
+ - de
+ - es
+ - fr
+ - id
+ - in
+ - it
+ - mx
+ - nz
+ - pe (→ espanol.answers)
+ - ph
+ - qc
+ - sg
+ - uk
+ - ve (→ espanol.answers)
+ - vn
+
+ - espanol.answers
+ - malaysia.answers
+
+ - antispam
+
+ - \w\w.antispam:
+
+ - ca
+ - dk
+ - fr
+ - in
+
+ - vn.antoan
+ - au.apps
+ - global.ard
+
+ - \w\w.astrology:
+
+ - au (→ au.lifestyle)
+ - ca (→ ca.shine)
+ - es
+ - fr
+ - nz (→ nz.lifestyle)
+ - uk
+
+ - auctions subdomains:
+
+ - hk
+ - hk.info
+ - hk.f1.master
+ - hk.f1.page
+ - hk.search
+ - hk.store
+ - hk.edit.store
+ - hk.user
+
+ - autos
+
+ - \w\w.autos:
+
+ - ca
+ - ar (→ ar.autocosmos.yahoo.net)
+ - de (→ de.cars)
+ - fr (→ fr.cars)
+ - mx (→ mx.autocosmos.yahoo.net)
+ - tw
+
+ - bc subdomains:
+
+ - clicks.beap
+ - csc.beap
+ - pn1
+ - row
+ - us
+
+ - axis (→ www)
+ - ar.ayuda
+
+ - bid subdomains:
+
+ - tw.campaign
+ - tw.master
+ - tw.mb
+ - tw.page
+ - tw.search
+ - tw.store
+ - tw
+ - tw.user
+
+ - tw.bigdeals
+ - m.tw.bigdeals
+ - tw.billing
+ - biz
+ - au.biz (→ au.finance)
+ - nz.biz (→ nz.finance)
+ - boss
+ - tw.partner.buy
+ - tw.buy
+ - calendar
+
+ - \w\w.calendar:
+
+ - ar
+ - au
+ - br
+ - ca
+ - de
+ - dk
+ - es
+ - fr
+ - gr
+ - hk
+ - ie
+ - in
+ - it
+ - no
+ - nz
+ - se
+ - sg
+ - tw
+ - uk
+ - us
+
+ - careers
+
+ - \w\w.careers (→ careers)
+
+ - ar
+ - au
+ - br
+ - ca
+ - cl
+ - de
+ - fr
+ - es
+ - hk
+ - id
+ - ie
+ - in
+ - it
+ - jp
+ - mx
+ - my
+ - no
+ - ph
+ - qc
+ - nz
+ - sg
+ - tw
+ - uk
+ - us
+ - vn
+
+ - malaysia.careers (→ careers)
+
+ - cars (→ autos)
+
+ - \w\w.cars:
+
+ - de
+ - es
+ - fr
+ - it
+ - uk
+
+ - \w\w.celebridades:
+
+ - ar
+ - br
+ - co
+ - mx
+
+ - es-us.celebridades
+
+ - celebrity
+
+ - \w\w.celebrity:
+
+ - ca
+ - es
+ - gr
+ - id
+ - in
+ - it
+ - hk
+ - ph
+ - tw
+ - uk
+
+ - tw.help.cc (→ help)
+ - tw.charity
+ - chart
+ - cine (→ es-us.cine)
+
+ - \w\w.cine:
+
+ - cl
+ - co
+ - es
+ - mx
+ - pe
+ - ve
+
+ - es-us.cine
+
+ - \w\w.cinema:
+
+ - br
+ - fr
+ - it
+
+ - \w\w.clima:
+
+ - cl
+ - co
+ - mx
+ - pe
+ - ve
+
+ - es-us.clima
+ - migration.cn
+ - commercecentral
+ - developers.commercecentral
+ - connectedtv (→ smarttv)
+ - br.contribuidores
+ - contributor
+ - uk.contributor
+ - cricket
+ - au.dating
+
+ - \w\w.deportes:
+
+ - ar
+ - cl (→ es-us.deportes)
+ - co (→ es-us.deportes)
+ - es (→ es.eurosport)
+ - mx
+ - pe (→ pe-us.deportes)
+ - ve (→ ve-us.deportes)
+
+ - es-us.deportes
+ - developer
+ - tw.dictionary
+ - dir
+ - au.dir (→ au.search)
+ - downloads
+ - s-b.dp
+
+ - edit subdomains:
+
+ - ^
+ - eu
+ - na
+ - sa
+ - tw
+
+ - tw.emarketing
+ - tw.ysm.emarketing
+ - en-maktoob
+ - hk.ent (→ hk.celebrity)
+
+ - \w\w.entertainment:
+
+ - my
+ - nz
+
+ - espanol
+ - edit.europe
+ - java.europe (→ adgallery.zenfs.com)
+
+ - eurosport subdomains:
+
+ - ^
+ - de
+ - es
+ - fr (→ fr.sports)
+ - it
+ - uk
+
+ - everything
+
+ - \w\w.everything:
+
+ - ca
+ - es (→ es.todo)
+ - nz
+ - ph
+ - pt
+ - tw
+ - uk
+
+ - au.fango
+
+ - \w+.fantasysports:
+
+ - baseball
+ - football
+ - golf
+ - hockey
+ - racing
+
+ - es.laliga.fantasysports (→ es.eurosport)
+ - tw.fashion
+ - feedback (→ yahoo.uservoice.com)
+ - br.financas
+ - finance
+
+ - \w\w.finance:
+
+ - ar
+ - au
+ - br
+ - ca
+ - de
+ - es
+ - fr
+ - hk
+ - ie (→ uk.finance)
+ - in
+ - it
+ - kr (→ tools.search)
+ - mx
+ - nz
+ - sg
+ - tw
+ - uk
+
+ - chart.finance (→ chart)
+ - tw.chart.finance
+ - espanol.finance
+ - tw.futures.finance
+ - ichart.finance (→ ichart)
+ - streamerapi.finance
+
+ - \w\w.finanzas:
+
+ - ar
+ - mx
+
+ - es-us.finanzas
+
+ - food subdomains:
+
+ - au (→ au.lifestyle)
+ - nz (→ nz.lifestyle)
+ - nz.rss
+
+ - au.forums (→ au.answers)
+ - nz.forums
+
+ - games subdomains:
+
+ - ^
+ - au
+ - ca
+ - de (→ de.spiele)
+ - id (→ games)
+ - malaysia (→ games)
+ - nz.games (→ games)
+ - ph (→ games)
+ - uk
+
+ - geo
+ - gma
+ - groups
+
+ - \w\w.groups:
+
+ - ar
+ - au
+ - br
+ - ca
+ - de
+ - dk
+ - es
+ - fr
+ - hk
+ - ie (→ uk.groups)
+ - in
+ - it
+ - kr
+ - mx
+ - nz
+ - ph
+ - sg
+ - tw
+ - uk
+ - us
+
+ - asia.groups
+ - espanol.groups
+ - es-us.groups
+ - fr-ca.groups
+ - moderators.groups
+ - kr.gugi (→ tools.search)
+ - health
+ - help
+
+ - \w\w.help:
+
+ - au
+ - br
+ - ca
+ - dk
+ - fr (→ help)
+ - hk
+ - io
+ - tw
+ - uk
+
+ - secure.help
+ - help.cc.hk (→ help)
+ - homes
+ - tw.house
+ - tw.v2.house
+ - ichart
+ - info
+
+ - \w\w.info:
+
+ - tw
+
+ - tw.tool.ks
+ - au.launch
+ - legalredirect
+
+ - \w\w.lifestyle:
+
+ - ar (→ ar.mujer)
+ - au
+ - ca (→ ca.shine)
+ - de
+ - hk
+ - ie (→ uk.lifestyle)
+ - in
+ - it
+ - mx (→ mx.mujer)
+ - nz
+ - uk
+
+ - es-us.lifestyle (→ ar.mujer)
+ - login
+ - gh.bouncer.login
+ - us.lrd
+ - local
+
+ - \w\w.local:
+
+ - au
+ - de
+ - fr (→ fr)
+ - uk
+
+ - m
+ - r.m
+
+ - \w\w.m:
+
+ - ar
+ - au
+ - br
+ - ca
+ - cn
+ - de
+ - es
+ - fr
+ - hk
+ - id
+ - ie
+ - in
+ - it
+ - kr
+ - ph
+ - qc
+ - se
+ - sg
+ - mx
+ - tw
+ - uk
+ - us
+ - vn
+
+ - mail
+
+ - *.mail:
+
+ - ar
+ - au
+ - co
+ - e1
+ - es
+ - fr
+ - it
+ - mrd
+ - my
+ - overview
+
+ - \w\w.overview:
+
+ - br
+ - ca
+ - co
+ - e1
+ - hk
+ - ph
+ - tw
+ - uk
+ - us
+
+ - ph
+ - th
+ - tw
+ - us-mg6
+ - vn
+ - c.c.yom
+ - \w+-c.c.yom
+
+ - maktoob
+ - malaysia
+ - tw.mall
+ - tw.user.mall
+ - maps
+
+ - \w\w.maps:
+
+ - au
+ - ca
+ - de
+ - es (→ es.search)
+ - fr
+ - in (→ maps)
+ - it
+ - kr (→ tools.search)
+ - mx (→ espanol.maps)
+ - nz (→ nz.search)
+ - qc
+ - tw
+ - uk
+
+ - espanol.maps
+ - sgws2.maps
+ - au.messages (→ au.answers)
+ - messenger
+
+ - \w\w.messenger:
+
+ - ar
+ - au
+ - br
+ - ca
+ - cf
+ - cl
+ - co
+ - de
+ - e1
+ - es
+ - fr
+ - hk
+ - id
+ - ie (→ uk.messenger)
+ - in
+ - it
+ - kr
+ - mx
+ - my
+ - nz (→ messenger)
+ - pe
+ - ph
+ - qc
+ - sg
+ - th
+ - tw (→ hk)
+ - uk
+ - us
+ - ve
+ - vn
+
+ - malaysia.messenger
+ - \w\w.meteo:
+
+ - fr
+ - it
+
+ - mlogin
+ - mobile
+
+ - \w\w.mobile:
+
+ - ar
+ - au
+ - br
+ - ca
+ - de
+ - dk (→ www)
+ - es
+ - fr
+ - hk
+ - id
+ - ie (→ uk.mobile)
+ - in
+ - it
+ - mx
+ - my
+ - nz
+ - no (→ www)
+ - ph
+ - qc
+ - sg
+ - th
+ - tw
+ - uk
+ - us
+ - vn
+
+ - espanol.mobile
+ - malaysia.mobile
+ - tw.campaign.money
+ - tw.money
+
+ - tw.movie
+
+ - movies subdomains:
+
+ - ^
+ - au
+ - br (→ br.cinema)
+ - ca
+ - es (→ es.cine)
+ - espanol (→ es-us.cine)
+ - fr (→ fr.cinema)
+ - it (→ it.cinema)
+ - nz
+ - au.rss
+ - nz.rss
+ - tw
+ - uk
+
+ - *.msg:
+
+ - dps (→ ycpi-mail-dps)
+ - prod2.rest-core
+ - prod1.rest-notify
+ - ycpi-mail-dps
+ - ycpi-mail-preferences
+ - ycpi-mail-rest-core
+ - ycpi-mail-rest-core2
+
+ - \w\w.mujer:
+
+ - ar
+ - co
+ - mx
+
+ - es-us.mujer
+
+ - music subdomains:
+
+ - ^
+ - ca
+ - hk (→ hk.celebrity)
+ - tw (→ tw.music.yahoo.net)
+
+ - [\w-]+\.musica:
+
+ - es-us
+ - mx
+
+ - my
+ - us.my
+ - de.nachrichten
+ - ucs.netsvs
+
+ - news subdomains:
+
+ - ^
+ - ar (→ ar.noticias)
+ - au
+ - br (→ br.noticias)
+ - au
+ - ca
+ - cl (→ cl.noticias)
+ - co (→ co.noticias)
+ - dk (→ www)
+ - es (→ es.noticias)
+ - fr
+ - gr
+ - hk
+ - ie (→ uk.news)
+ - in
+ - mx (→ mx.noticias)
+ - my
+ - nz
+ - pe (→ pe.noticias)
+ - ph
+ - nz.rss
+ - sg
+ - tw
+ - uk
+ - ve (→ ve.noticias)
+ - vn
+
+ - cookiex.ngd
+
+ - \w\w.noticias
+
+ - ar
+ - br
+ - cl
+ - co
+ - es
+ - mx
+ - pe
+ - ve
+
+ - es-us.noticias
+ - omg
+
+ - \w\w.omg:
+
+ - ar
+ - br
+ - co
+ - es
+ - it
+ - mx
+ - ph
+ - tw
+
+ - es-us.omg
+ - on (→ pilotx1)
+ - au.oztips
+ - rtb.pclick
+ - pilotx1
+ - pipes
+ - play
+ - playerio
+ - privacy
+ - profile
+ - tw.promo
+
+ - au.promotions
+ - hk.promotions
+ - nz.promotions
+
+ - publishing
+
+ - query subdomains:
+
+ - analytics
+ - mailapps
+ - media
+ - ucs
+ - us-locdrop
+ - video
+
+ - tw.rd
+ - us.rd
+
+ - safely
+
+ - \w\w.safely:
+
+ - ar (→ ar.seguridad)
+ - au
+ - ca
+ - cl (→ cl.seguridad)
+ - co
+ - de
+ - fr
+ - hk
+ - id
+ - in
+ - it
+ - mx (→ mx.seguridad)
+ - my
+ - nz
+ - pe (→ pe.seguridad)
+ - ph
+ - sg
+ - tw
+ - uk
+ - ve (→ ve.seguridad)
+
+ - es-us.safely (→ es.us.seguridad)
+ - fr-ca.safely
+ - malaysia.safely (→ my.safely)
+
+ - screen
+
+ - \w\w.screen:
+
+ - ar
+ - br
+ - ca
+ - co
+ - de
+ - es
+ - fr
+ - hk
+ - in
+ - it
+ - mx
+ - tw
+ - uk
+
+ - es-us.screen
+ - scribe
+
+ - search subdomains:
+
+ - ^
+
+ - \w\w:
+
+ - ar
+ - au
+ - be
+ - br
+ - ca
+ - cl
+ - cn (→ sg)
+ - co
+ - de
+ - dk
+ - es
+ - fi
+ - fr
+ - gr
+ - hk
+ - id
+ - ie
+ - in
+ - it
+ - kr
+ - mx
+ - my (→ malaysia)
+ - nl
+ - no
+ - nz
+ - pe
+ - ph
+ - ru
+ - se
+ - sg
+ - tw
+ - uk
+ - ve
+ - vn
+
+ - \w\w.blog:
+
+ - tw
+
+ - \w\w.dictionary:
+
+ - tw
+
+ - finance
+
+ - \w\w.finance:
+
+ - au
+ - nz
+
+ - images
+
+ - \w\w.images:
+
+ - ar
+ - au
+ - br
+ - ca
+ - cn (→ sg.images.search)
+ - de
+ - dk
+ - es
+ - fi
+ - fr
+ - hk
+ - id
+ - in
+ - it
+ - kr (→ kr.search)
+ - nl
+ - mx
+ - my (→ malaysia.images.search)
+ - no
+ - nz
+ - pe
+ - ph
+ - qc
+ - ru
+ - se
+ - sg
+ - tw
+ - uk
+ - ve
+ - vn
+
+ - malaysia.images
+
+ - \w\w.knowledge:
+
+ - tw
+
+ - \w\w.lifestyle:
+
+ - au
+ - nz
+
+ - \w\w.local:
+
+ - tw
+
+ - malaysia
+
+ - nz.maps (→ nz.search)
+
+ - \w\w.news:
+
+ - ar
+ - au
+ - ca
+ - de
+ - fr
+ - sg
+ - tw
+ - uk
+
+ - malaysia.news
+
+ - movies
+
+ - \w\w.movies:
+
+ - au
+ - ca
+ - es
+ - fr
+ - it
+ - nz
+ - sg
+ - uk
+
+ - news
+
+ - \w\w.news:
+
+ - ar
+ - au
+ - br
+ - es
+ - fr
+ - it
+ - nz
+ - pe
+ - sg
+ - uk
+
+ - r
+ - recipes
+
+ - \w\w.recipes:
+
+ - ar
+ - au
+ - br
+ - es
+ - fr
+ - it
+ - mx
+ - nz
+ - tw
+ - uk
+
+ - shine
+ - shopping
+
+ - \w\w.shopping:
+
+ - tw
+
+ - sports
+
+ - \w\w.sports:
+
+ - au
+ - nz
+
+ - profiles.sports
+ - tools
+ - au.tv
+ - video
+
+ - \w\w.video:
+
+ - ar
+ - au
+ - br
+ - ca
+ - de
+ - es
+ - fr
+ - hk
+ - id
+ - in
+ - it
+ - mx
+ - my (→ malaysia.video)
+ - nz
+ - ph
+ - qc
+ - sg
+ - tw
+ - uk
+ - vn
+
+ - malaysia.video
+
+ - kr.searchad (→ tools.search)
+ - rtb.pclick.secure
+ - security
+ - tw.security
+
+ - \w\w.seguranca:
+
+ - br
+
+ - \w\w.seguridad:
+
+ - ar
+ - cl
+ - co
+ - mx
+ - pe
+ - ve
+
+ - es-us.seguridad
+
+ - \w\w.seguro:
+
+ - seguro
+
+ - tw.serviceplus
+ - settings
+ - shine
+ - ca.shine
+ - shopping
+ - ca.shopping
+
+ - \w+.sitios:
+
+ - co
+ - mx
+
+ - dashboard.slingstone
+
+ - smallbusiness
+ - au.smallbusiness
+ - order.smallbusiness
+
+ - smarttv
+
+ - de.solutions (→ de.adspecs)
+ - es.solutions (→ es.adspecs)
+ - fr.solutions (→ fr.adspecs)
+ - it.solutions (→ it.adspecs)
+ - nz.solutions (→ nz.advertising)
+ - uk.solutions (→ uk.adspecs)
+
+ - rd.software
+ - de.spiele
+
+ - sport (→ sports)
+
+ - sports subdomains:
+
+ - ^
+ - au
+ - ca
+ - de (→ de.eurosport)
+ - es (→ es.eurosport)
+ - fr
+ - hk
+ - nz
+ - ph
+ - au.rss
+ - nz.rss
+ - tw
+ - uk (→ uk.eurosport)
+
+ - tw.stock
+ - au.thehype
+
+ - \w\w.tiempo:
+
+ - ar
+ - es
+
+ - au.todaytonight (→ au.news)
+ - es.todo
+ - toolbar
+
+ - \w\w.toolbar:
+
+ - ar
+ - au
+ - br
+ - ca
+ - cl
+ - cn
+ - co
+ - de
+ - es
+ - fr
+ - hk
+ - id
+ - in
+ - it
+ - mx
+ - my
+ - nz
+ - pe
+ - ph
+ - sg
+ - tw
+ - uk
+ - ve
+ - vn
+
+ - data.toolbar
+ - malaysia.toolbar
+ - au.totaltravel
+ - nz.totaltravel
+ - transparency
+ - travel
+ - au.travel (→ au.totaltravel)
+ - ca.travel (→ travel)
+ - my.travel (→ my.news)
+ - nz.travel (→ nz.totaltravel)
+ - ph.travel (→ ph.news)
+ - tw.travel
+ - uk.travel (→ uk.lifestyle)
+
+ - tv subdomains:
+
+ - ^
+ - ar
+ - au
+ - br
+ - ca (→ tv)
+ - de
+ - es
+ - es-us
+ - fr
+ - hk (→ hk.celebrity)
+ - it
+ - mx
+ - nz
+ - pe (→ es-us.tv)
+ - au.rss
+ - uk
+
+ - tw.uwant
+
+ - video subdomains:
+
+ - ^ (→ screen)
+ - ar (→ ar.screen)
+ - au (→ au.tv)
+ - br (→ br.screen)
+ - ca (→ ca.screen)
+ - co (→ co.screen)
+ - de (→ de.screen)
+ - es (→ es.screen)
+ - es-us (→ es-us.screen)
+ - fr (→ fr.screen)
+ - hk (→ help)
+ - in (→ in.screen)
+ - it (→ it.screen)
+ - mh
+ - mx (→ mx.screen)
+ - nz
+ - pe (→ es-us.screen)
+ - qos
+ - uk (→ uk.screen)
+ - ve (→ es-us.screen)
+ - yep
+
+ - weather subdomains:
+
+ - ^
+ - ar (→ ar.tiempo)
+ - au
+ - ca
+ - cl (→ cl.clima)
+ - co (→ co.clima)
+ - es (→ es.tiempo)
+ - espanol (→ es-us.clima)
+ - fr (→ fr.meteo)
+ - hk
+ - in
+ - it (→ it.meteo)
+ - mx (→ mx.clima)
+ - nz
+ - pe (→ pe.clima)
+ - ph
+ - sg
+ - tw (→ tw.news)
+ - uk
+ - us
+ - ve (→ ve.clima)
+
+ - de.wetter
+ - widgets (→ www)
+ - au.yel
+ - video.media.yql
+ - dmros.ysm
+
+
+ These altnames don't exist:
+
+ - manhattan.yahoo.com
+ - tw.moderation.money.yahoo.com
+
+
+ Observed cookie domains:
+
+ - . ¹
+ - .answers ²
+ - .auctions ¹
+ - .bid ¹
+ - .buy ⁴
+ - commercecentral
+ - developers.commercecentral ²
+ - .contributor ⁵
+ - tw.ysm.emarketing ³
+ - games ³
+ - homes ³
+ - au.local ³
+ - .maps ³
+ - .playerio ³
+ - profile ³
+ - .search ⁴
+ - .\w\w.tv ³
+ - tw.uwant ³
+ - .voices ⁵
+ - .www ³
+
+ ¹ Partially secured by us <= accounting for possible use on unsecurable domains
+ ² Secured by server
+ ⁵ Some secured by server, rest by us
+ ³ Secured by us <= not secured by server
+ ⁴ Not secured by us <= accounting for possible use on unsecurable domains
+ ⁵ Not secured by us <= no tls support
+
+
+ Mixed content:
+
+ - css, on:
+
+ - au.gwn7, tw.money, au.rss.news, and au.prime7 from l[13]?.yimg.com ¹
+
+ - Ads/web bugs, on:
+
+ - au.games from secure-us.imrworldwide.com ¹
+ - \w\w.celebrity, m, \w\w.m, and ar.mujer from csc.beap.bc.yahoo.com ¹
+ - au.news from au.adserver.yahoo.com ¹
+ - shine from www.facebook.com ¹
+
+ - Images, on:
+
+ - au.local from dacsisb9yvy2v.cloudfront.net ¹
+ - au.advertising, nz.advertising, au.answers, nz.answers, ph.answers, sg.answers, au, biz, \w\w.celebrity, cricket, nz.entertainment, eurosport, \w\w.eurosport, everything, au.fango, games, ichart, au.launch, nz.lifestyle, au.local, sg.messenger, tw.money, au.movies, nz.movies, au.news, nz.news, au.oztips, au.promotions, \w\w.safely, fr-ca.safely, search, \w\w.seguridad, es-us.seguridad, es.seguro, au.smallbusiness, au.rss.sports, nz.rss.sports, au.thehype, tw.toolbar, au.totaltravel, nz.totaltravel, au.tv, nz.tv, au.rss.tv, and nz.weather from l.yimg.com ¹
+ - ca.autos from yui.yahooapis.com ¹
+ - tw.info from l.yimg.com ¹
+ - tw.knowledge from tw.tool.ks ¹
+ - tw.knowledge from l.yimg.com ¹
+ - tw.money from ichart ¹
+ - tw.money from tw.news2.yimg.com ²
+ - tw.promo from www.adobe.com ¹
+ - au.totaltravel and nz.totaltravel from www.totaltravel.com ²
+ - \w\w.weather and de.wetter from media.zenfs.com ¹
+
+ - faivcon on tw from tw *
+
+ - Ads, on:
+
+ - fr.finance from www.borse.it ³
+ - tw.promo from www.facebook.com ¹
+ - de.kino from yahoo.quizaction.de ¹
+ - my.news from widgets.wego.com ²
+
+ ¹ Secured by us
+ ² Unsecurable
+ ³ Unsecurable <= redirects to http
+
+
+ Reported to fix bug
+
+ https://trac.torproject.org/projects/tor/ticket/4441
+
+
+ If you have a Yahoo Mail account, please test this ruleset!
+
+-->
+<ruleset name="Yahoo! (partial)">
+
+ <target host="i.acdn.us" />
+ <target host="rocketmail.com" />
+ <target host="www.rocketmail.com" />
+ <target host="totaltravel.co.uk" />
+ <target host="www.totaltravel.co.uk" />
+ <target host="totaltravel.com" />
+ <target host="*.totaltravel.com" />
+ <exclusion pattern="^http://(?:www\.)?totaltravel\.com/images/" />
+ <target host="yahoo.com" />
+ <target host="*.yahoo.com" />
+ <!--
+ Refused:
+ -->
+ <exclusion pattern="^http://(?:(?:cn|kr|tw)\.adspecs|(?:co|espanol|mx)\.astrology|kr\.mobile)\.yahoo\.com/" />
+ <!--
+ Redirect destination cert mismatched:
+ -->
+ <exclusion pattern="^http://ca\.local\.yahoo\.com/" />
+ <!--
+ Refused:
+ -->
+ <exclusion pattern="^http://cn\.overview\.mail\.yahoo\.com/" />
+ <!--exclusion pattern="^http://(cn|de|dk|id|ie|it|qc)\.news\.yahoo\.com/" /-->
+ <!--
+ Destination has mismatched cert:
+ -->
+ <exclusion pattern="^http://(?:br|es)\.safely\.yahoo\.com/" />
+ <target host="*.yahoofs.com" />
+ <target host="yhoo.it" />
+ <target host="ymail.com" />
+ <target host="www.ymail.com" />
+ <target host="*.zenfs.com" />
+
+
+ <!-- Some Yahoo cookies are cross-domain cookies.
+ It's a case of figuring out which ones
+ aren't needed on unsecurable pages.
+
+ - .yahoo.com
+ - AO
+ - B
+ - Set by y3.analytics.yahoo.com/itr.pl & us.bc.yahoo.com/b
+
+ - BA
+
+ - t=\d{10}
+
+ - CH
+ - \w{59}/
+ - F
+
+ - HP
+
+ - 0
+
+ - MSC
+ - t=\d{10}X
+ - PH (set by hjsal)
+ - SSL
+
+ - ucs (set by ucs.query)
+
+ - bnas=\d
+
+ - V
+
+ - v=\d.\d&cc=0&m=0
+
+ - Y
+
+ -->
+ <!--
+ Secured by server:
+ -->
+ <!--securecookie host="^\.answers\.yahoo\.com$" name="^answers3$" /-->
+ <!--securecookie host="^(developers\.)?commercecentral\.yahoo\.com$" name="^_rockstar_session$" /-->
+ <!--securecookie host="^\.contributor\.yahoo\.com$" name="^c$" /-->
+ <!--
+ Not secured by server:
+ -->
+ <!--securecookie host="^\.yahoo\.com$" name="^(AO|B|PH|au_ytv|tt_currency)$" /-->
+ <!--securecookie host="^\.auctions\.yahoo\.com$" name="^hkRecentHistory$" /-->
+ <!--securecookie host="^\.bid\.yahoo\.com$" name="^twRecentHistory$" /-->
+ <!--securecookie host="^commercecentral\.yahoo\.com$" name="^first_referer$" /-->
+ <!--securecookie host="^\.contributor\.yahoo\.com$" name="^ACSESS$" /-->
+ <!--securecookie host="^(\w\w\.celebridades|\w\w\.cinema|everything|\w\w\.financas|games|homes|\w\w\.news)\.yahoo\.com$" name="^AO$" /-->
+ <!--securecookie host="^tw\.ysm\.emarketing\.yahoo\.com$" name="^(device|is_c|tw_ysm_soeasy)$" /-->
+ <!--securecookie host="^(uk\.)?help\.yahoo\.com$" name="^(JSESSIONID|scav|scwysiwygparams)$" /-->
+ <!--securecookie host="^au\.local\.yahoo\.com$" name="^(aunz\.aulocal\.cookie|au_yloc)$" /-->
+ <!--securecookie host="^\.maktoob\.yahoo\.com$" name="^hpc$" /-->
+ <!--securecookie host="^\.maps\.yahoo\.com$" name="^MYCFL$" /-->
+ <!--securecookie host="^\.playerio\.yahoo\.com$" name="^playcodes-\d+$" /-->
+ <!--securecookie host="^profile\.yahoo\.com$" name="^YPRF$" /-->
+ <!--securecookie host="^\.search\.yahoo\.com$" name="^sSN$" /-->
+ <!--securecookie host="^\.es\.tv\.yahoo\.com$" name="^tv_listings_last_time$" /-->
+ <!--securecookie host="^tw\.uwant\.yahoo\.com$" name="^uwwtutorial$" /-->
+ <!--securecookie host="^\.www\.yahoo\.com$" name="^fpc$" /-->
+
+ <securecookie host="^\.yahoo\.com$" name="^(?:AO|B|SSL)$" />
+ <securecookie host="^(?:\.analytics|\w\w\.celebridades|\w\w\.cinema|commercecentral|\.contributor|tw\.ysm\.emarketing|everything|\w\w\.financas|games|help|\w\w\.help|homes|\w\w\.local|\.mail|\.maps|\.maktoob|movies|\.?news|\w\w.news|\.playerio|profile|(?:us-locdrop|video)\.query|images\.search|fr\.images\.search|\.toolbar|\.\w\w\.tv|\.uk|\.?us|tw\.uwant|\.www)\.yahoo\.com$" name=".+" />
+ <securecookie host="^\.bid\.yahoo\.com$" name="^twRecentHistory$" />
+ <securecookie host="^\.auctions\.yahoo\.com$" name="^hkRecentHistory$" />
+ <securecookie host="^\.zenfs\.com$" name="^BX$" />
+
+ <!-- Could we secure any of these safely?
+ -->
+ <!--securecookie host="^\.yahoo\.com$" name="^(DK|PH|au_ytv|tt_currency)$" /-->
+ <!--securecookie host="^\.buy\.yahoo\.com$" name="^YAct$" /-->
+ <!--securecookie host="^\.my\.yahoo\.com$" name="^(myc|MYTMI|U_mtupes)$" /-->
+ <!--securecookie host="^\.search\.yahoo\.com$" name="^sSN$" /-->
+
+
+ <rule from="^http://i\.acdn\.us/"
+ to="https://s.yimg.com/ck/" />
+
+ <rule from="^http://(?:www\.)?(?:rocket|y)mail\.com/"
+ to="https://mail.yahoo.com/" />
+
+ <rule from="^http://(?:www\.)?totaltravel\.co(?:m|\.uk)/"
+ to="https://au.totaltravel.yahoo.com/" />
+
+ <rule from="^http://builder\.totaltravel\.com/"
+ to="https://builder.totaltravel.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://fr\.actualites\.yahoo\.com/.*"
+ to="https://fr.news.yahoo.com/" />
+
+ <rule from="^http://advertisingcentral\.yahoo\.com/+(?=$|\?)"
+ to="https://advertising.yahoo.com/" />
+
+ <!-- Redirect preserves path and args:
+ -->
+ <rule from="^http://(?:cl|co|pe|ve)\.answers\.yahoo\.com/+"
+ to="https://espanol.answers.yahoo.com/" />
+
+ <!-- Redirect drops path but not args:
+ -->
+ <rule from="^http://(au|nz)\.astrology\.yahoo\.com/[^?]*"
+ to="https://$1.lifestyle.yahoo.com/horoscopes/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://ca\.astrology\.yahoo\.com/.*"
+ to="https://ca.shine.yahoo.com/horoscope/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(ar|mx)\.autos\.yahoo\.com/+"
+ to="https://$1.autocosmos.yahoo.net/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(de|fr)\.autos\.yahoo\.com/+"
+ to="https://$1.cars.yahoo.com/" />
+
+ <!-- Redirect drops path but not args:
+ -->
+ <rule from="^http://(au|nz)\.biz\.yahoo\.com/[^?]*"
+ to="https://$1.finance.yahoo.com/news" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(ar|au|br|ca|cl|de|fr|es|hk|id|ie|in|it|jp|mx|my|no|nz|ph|sg|tw|uk|us|vn)\.careers\.yahoo\.com/+"
+ to="https://careers.yahoo.com/$1/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://malaysia\.careers\.yahoo\.com/+"
+ to="https://careers.yahoo.com/my/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://qc\.careers\.yahoo\.com/+"
+ to="https://careers.yahoo.com/ca/" />
+
+ <!-- Redirect preserves forward slash, path, and args:
+ -->
+ <rule from="^http://cars\.yahoo\.com/"
+ to="https://autos.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://(?:tw\.help\.cc|help\.cc\.tw)\.yahoo\.com/.*"
+ to="https://help.yahoo.com/kb/index?page=home&amp;locale=zh_TW" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://cn\.yahoo\.com/+"
+ to="https://sg.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(?:cine|espanol\.movies)\.yahoo\.com/+"
+ to="https://es-us.cine.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(?:cl|co|pe|ve)\.deportes\.yahoo\.com/+"
+ to="https://es-us.deportes.yahoo.com/" />
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://es\.deportes\.yahoo\.com/+"
+ to="https://es.eurosport.yahoo.com/" />
+
+ <!-- Redirect keeps path but not args:
+ -->
+ <rule from="^http://au\.dir\.yahoo\.com/+([^?]*).*"
+ to="https://au.search.yahoo.com/web?fr=" />
+
+ <rule from="^http://(?:dk|no|ru)\.yahoo\.com/+"
+ to="https://www.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://e1\.yahoo\.com/+"
+ to="https://espanol.yahoo.com/" />
+
+ <rule from="^http://hk\.ent\.yahoo\.com/+"
+ to="https://hk.celebrity.yahoo.com/" />
+
+ <rule from="^http://java\.europe\.yahoo\.com/"
+ to="https://adgallery.zenfs.com/" />
+
+ <rule from="^http://fr\.eurosport\.yahoo\.com/"
+ to="https://fr.sports.yahoo.com/" />
+
+ <!-- Server drops path and args:
+ -->
+ <rule from="^http://es\.everything\.yahoo\.com/.*"
+ to="https://es.todo.yahoo.com/" />
+
+ <rule from="^http://fantasysports\.yahoo\.com/(?=$|\?)"
+ to="https://sports.yahoo.com/fantasy" />
+
+ <!-- Server drops path but not args:
+ -->
+ <rule from="^http://es\.laliga\.fantasysports\.yahoo\.com/+"
+ to="https://es.eurosport.yahoo.com/fantasy/la-liga/" />
+
+ <rule from="^http://feedback\.yahoo\.com/"
+ to="https://yahoo.uservoice.com/" />
+
+ <rule from="^http://(i)?chart\.finance\.yahoo\.com/"
+ to="https://$1chart.yahoo.com/" />
+
+ <!-- Redirect drops path buy not args:
+ -->
+ <rule from="^http://connectedtv\.yahoo\.com/[^?]*"
+ to="https://smarttv.yahoo.com/" />
+
+ <!-- Server keeps path and args:
+ -->
+ <rule from="^http://kr\.finance\.yahoo\.com/"
+ to="https://tools.search.yahoo.com/kr-eol.html" />
+
+ <rule from="^http://(au|nz)\.food\.yahoo\.com/"
+ to="https://$1.lifestyle.yahoo.com/food/" />
+
+ <!-- Server keeps path and args:
+ -->
+ <rule from="^http://de\.games\.yahoo\.com/+"
+ to="https://de.spiele.yahoo.com/" />
+
+ <!-- Server keeps path and args:
+ -->
+ <rule from="^http://(?:id|malaysia|nz|ph)\.games\.yahoo\.com/+"
+ to="https://games.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://ie\.(finance|groups|lifestyle)\.yahoo\.com/.*"
+ to="https://uk.$1.yahoo.com/" />
+
+ <!-- Redirect drops path but not args:
+ -->
+ <rule from="^http://au\.(?:answer|forum)s\.yahoo\.com/[^?]*"
+ to="https://au.answers.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://kr\.(?:gugi|maps|searchad)\.yahoo\.com/.*"
+ to="https://tools.search.yahoo.com/kr-eol.html" />
+
+ <rule from="^http://fr\.help\.yahoo\.com/+"
+ to="https://help.yahoo.com/l/fr/yahoo/helpcentral/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://help\.cc\.hk\.yahoo\.com/.*"
+ to="https://help.yahoo.com/kb/index?page=home&amp;locale=zh_HK" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(ar|es-us|mx)\.lifestyle\.yahoo\.com/+"
+ to="https://$1.mujer.yahoo.com/" />
+
+ <rule from="^http://ca\.(?:lifestyle|shine)\.yahoo\.com/"
+ to="https://ca.shine.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://fr\.local\.yahoo\.com/.*"
+ to="https://fr.yahoo.com/" />
+
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://es\.maps\.yahoo\.com/.*"
+ to="https://es.search.yahoo.com/search/es?p=callejero+itinerarios&amp;y=y" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://in\.maps\.yahoo\.com/.*"
+ to="https://maps.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://mx\.maps\.yahoo\.com/+"
+ to="https://espanol.maps.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://nz\.maps\.yahoo\.com/+"
+ to="https://nz.search.yahoo.com/search/maps/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://ie\.messenger\.yahoo\.com/.*"
+ to="https://uk.messenger.yahoo.com/" />
+
+ <!-- Redirect drops path but not args:
+ -->
+ <rule from="^http://nz\.messenger\.yahoo\.com/[^?].*"
+ to="https://messenger.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://ie\.mobile\.yahoo\.com/.*"
+ to="https://uk.mobile.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://tw\.music\.yahoo\.com/+"
+ to="https://tw.music.yahoo.net/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://(?:axis|(?:dk|no)\.mobile|dk\.news)\.yahoo\.com/.*"
+ to="https://www.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://es\.movies\.yahoo\.com/+"
+ to="https://es.cine.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(br|fr|it)\.movies\.yahoo\.com/+"
+ to="https://$1.cinema.yahoo.com/" />
+
+ <!-- This rule must be above the main one:
+ -->
+ <rule from="^http://dps\.msg\.yahoo\.com/"
+ to="https://ycpi-mail-dps.msg.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://hk\.(?:music|tv)\.yahoo\.com/.*"
+ to="https://hk.celebrity.yahoo.com/music/" />
+
+ <rule from="^http://(ar|br|co|es|mx|pe)\.news\.yahoo\.com/+"
+ to="https://$1.noticias.yahoo.com/" />
+
+ <!-- Redirect drops paths and args:
+ -->
+ <rule from="^http://ie\.news\.yahoo\.com/.*"
+ to="https://uk.news.yahoo.com/n/news_ireland.html" />
+
+ <rule from="^http://on\.yahoo\.com/+"
+ to="https://pilotx1.yahoo.com/" />
+
+ <!-- Cert only matches us.rd,
+ all appear equivalent.
+ -->
+ <rule from="^http://rds?\.yahoo\.com/"
+ to="https://us.rd.yahoo.com/" />
+
+ <rule from="^http://(ar|cl|co|es-us|mx|pe|ve)\.safely\.yahoo\.com/+"
+ to="https://$1.seguridad.yahoo.com/" />
+
+ <rule from="^http://malaysia\.safely\.yahoo\.com/+"
+ to="https://my.safely.yahoo.com/" />
+
+ <!-- Redirect drops paths and args:
+ -->
+ <rule from="^http://cn\.search\.yahoo\.com/.*"
+ to="https://sg.search.yahoo.com/" />
+
+ <!-- Redirect drops paths and args:
+ -->
+ <rule from="^http://kr\.(?:images\.)?search\.yahoo\.com/.*"
+ to="https://kr.search.yahoo.com/" />
+
+ <rule from="^http://my\.images\.search\.yahoo\.com/"
+ to="https://malaysia.images.search.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://nz\.maps\.search\.yahoo\.com/+"
+ to="https://nz.search.yahoo.com/" />
+
+ <rule from="^http://my\.search\.yahoo\.com/+"
+ to="https://malaysia.search.yahoo.com/" />
+
+ <!-- Redirect drops path but not args:
+ -->
+ <rule from="^http://(de|es|fr|it|uk)\.solutions\.yahoo\.com/[^?]*"
+ to="https://$1.adspecs.yahoo.com/" />
+
+ <rule from="^http://sport\.yahoo\.com/+"
+ to="https://sports.yahoo.com/" />
+
+ <rule from="^http://(de|es|uk)\.sports\.yahoo\.com/+"
+ to="https://$1.eurosport.yahoo.com/" />
+
+ <rule from="^http://in\.sports\.yahoo\.com/+$"
+ to="https://cricket.yahoo.com/" />
+
+ <!-- Server drops paths but not args:
+ -->
+ <rule from="^http://au\.todaytonight\.yahoo\.com/+\??$"
+ to="https://au.news.yahoo.com/today-tonight/" />
+
+ <rule from="^http://au\.todaytonight\.yahoo\.com/[^?]*"
+ to="https://au.news.yahoo.com/today-tonight/" />
+
+ <!-- Redirect drops path but not args:
+ -->
+ <rule from="^http://(au|nz)\.travel\.yahoo\.com/[^?]*"
+ to="https://$1.totaltravel.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://ca\.travel\.yahoo\.com/+"
+ to="https://travel.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://(my|ph)\.travel\.yahoo\.com/.*"
+ to="https://$1.news.yahoo.com/travel/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://uk\.travel\.yahoo\.com/.*"
+ to="https://uk.lifestyle.yahoo.com/travel/" />
+
+ <rule from="^http://ca\.tv\.yahoo\.com/+"
+ to="https://tv.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://pe\.tv\.yahoo\.com/+"
+ to="https://es-us.tv.yahoo.com/" />
+
+ <rule from="^http://((?:br|ca|de|es|es-us|fr|it|mx|uk)\.)?video\.yahoo\.com/+"
+ to="https://$1screen.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://(ar|co|in)\.video\.yahoo\.com/.*"
+ to="https://$1.screen.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://au\.video\.yahoo\.com/.*"
+ to="https://au.tv.yahoo.com/plus7/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://[pv]e\.video\.yahoo\.com/+"
+ to="https://es-us.screen.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://hk\.video\.yahoo\.com/.*"
+ to="https://help.yahoo.com/kb/index?page=home&amp;locale=zh_HK" />
+
+ <!-- Server doesn't redirect:
+ -->
+ <rule from="^http://my\.video\.yahoo\.com/"
+ to="https://malaysia.video.yahoo.com/" />
+
+ <rule from="^http://nz\.video\.yahoo\.com/+(?:\?.*)?$"
+ to="https://nz.news.yahoo.com/video/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(ar|es)\.weather\.yahoo\.com/+"
+ to="https://$1.tiempo.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(cl|co|mx|pe|ve)\.weather\.yahoo\.com/+"
+ to="https://$1.clima.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://espanol\.weather\.yahoo\.com/+"
+ to="https://es-us.clima.yahoo.com/" />
+
+ <!-- Redirect keeps path and args:
+ -->
+ <rule from="^http://(fr|it)\.weather\.yahoo\.com/+"
+ to="https://$1.meteo.yahoo.com/" />
+
+ <!-- Redirect drops path and args:
+ -->
+ <rule from="^http://tw\.weather\.yahoo\.com/.*"
+ to="https://tw.news.yahoo.com/weather-forecast/" />
+
+ <!-- Redirect drops path but not args:
+ -->
+ <rule from="^http://widgets\.yahoo\.com/[^?]*"
+ to="https://www.yahoo.com/" />
+
+ <rule from="^http://((?:\w\w|fr-ca\.actualites|address|\w\w\.address|admanager|(?:\w\w|global)\.adserver|adspecs|\w+\.adspecs|\w+\.adspecs-new|advertising|\w\w\.advertising|beap\.adx|c5a?\.ah|(?:s-)?cookex\.amp|(?:[aosz]|apac|y3?)\.analytics|anc|answers|(?:\w\w|espanol|malaysia)\.answers|antispam|\w\w\.antispam|vn\.antoan|au\.apps|global\.ard|astrology|\w\w\.astrology|hk\.(?:(?:info|f1\.master|f1\.page|search|store|edit\.store|user)\.)?auctions|autos|\w\w\.autos|ar\.ayuda|(?:clicks\.beap|csc\.beap|pn1|row|us)\.bc|tw\.bid|tw\.(?:campaign|master|mb|page|search|store|user)\.bid|(?:m\.)?tw\.bigdeals|tw\.billing|biz|boss|(?:tw\.partner|tw)\.buy|(?:\w\w\.)?calendar|careers|\w\w\.cars|(?:\w\w|es-us)\.celebridades|(?:\w\w\.)?celebrity|tw\.charity|i?chart|(?:\w\w|es-us)\.cine|\w\w\.cinema|(?:\w\w|es-us)\.clima|migration\.cn|(?:deveopers\.)?commercecentral|br\.contribuidores|(?:uk\.)?contributor|au\.dating|(?:\w\w|es-us)\.deportes|developer|tw\.dictionary|dir|downloads|s-b\.dp|(?:eu\.|na\.|sa\.|tw\.)?edit|tw\.(?:ysm\.)?emarketing|en-maktoob|\w\w\.entertainment|espanol|edit\.europe|eurosport|(?:de|es|it|uk)\.eurosport|everything|\w\w\.everything|\w+\.fantasysports|au\.fango|tw\.fashion|br\.financas|finance|(?:\w\w|tw\.chart|espanol|tw\.futures|streamerapi)\.finance|(?:\w\w|es-us)\.finanzas|nz\.rss\.food|nz\.forums|games|(?:au|ca|uk)\.games|geo|gma|groups|(?:\w\w|asia|espanol|es-us|fr-ca|moderators)\.groups|health|help|(?:\w\w|secure)\.help|homes|(?:tw|tw\.v2)\.house|info|\w\w\.info|tw\.tool\.ks|au\.launch|legalredirect|(?:\w\w)\.lifestyle|(?:gh\.bouncer\.)?login|us\.l?rd|local|\w\w\.local|m|r\.m|\w\w\.m|mail|(?:\w\w\.overview|[\w-]+(?:\.c\.yom)?)\.mail|maktoob|malaysia|tw\.(?:user\.)?mall|maps|(?:\w\w|espanol|sgws2)\.maps|messenger|(?:\w\w|malaysia)\.messenger|\w\w\.meteo|mlogin|mobile|(?:\w\w|espanol|malaysia)\.mobile|tw\.(?:campaign\.)?money|tw\.movie|movies|(?:au|ca|nz|au\.rss|nz\.rss|tw|uk)\.movies|[\w.-]+\.msg|(?:\w\w|es-us)\.mujer|music|ca\.music|[\w-]+\.musica|my|us\.my|de\.nachrichten|ucs\.netsvs|news|(?:au|ca|fr|gr|hk|in|nz|ph|nz\.rss|sg|tw|uk)\.news|cookiex\.ngd|(?:\w\w|es-us)\.noticias|omg|(?:\w\w|es-us)\.omg|au\.oztips|rtb\.pclick|pilotx1|pipes|play|playerio|privacy|profile|tw\.promo|(?:au|hk|nz)\.promotions|publishing|(?:analytics|mailapps|media|ucs|us-locdrop|video)\.query|hk\.rd|(?:\w\w\.|fr-ca\.)?safely|screen|(?:\w\w|es-us)\.screen|scribe|search|(?:\w\w|w\w\.blog|\w\w\.dictionary|finance|\w\w\.finance|images|\w\w\.images|\w\w\.knowledge|\w\w\.lifestyle|\w\w\.local|malaysia|movies|\w\w\.movies|news|\w\w\.news|malaysia\.news|r|recipes|\w\w\.recipes|shine|shopping|\w\w\.shopping|sports|\w\w\.sports|tools|au\.tv|video|\w\w\.video|malaysia\.video)\.search|sec|rtb\.pclick\.secure|security|tw\.security|\w\w\.seguranca|\w\w\.seguridad|es-us\.seguridad|\w\w\.seguro|tw\.serviceplus|settings|shine|ca\.shine|shopping|ca\.shopping|\w+\.sitios|dashboard\.slingstone|(?:au\.|order\.)?smallbusiness|smarttv|rd\.software|de\.spiele|sports|(?:au|ca|fr|hk|nz|ph|profiles|au\.rss|nz\.rss|tw)\.sports|tw\.stock|au\.thehype|\w\w\.tiempo|es\.todo|toolbar|(?:\w\w|data|malaysia)\.toolbar|(?:au|nz)\.totaltravel|transparency|travel|tw\.travel||tv|(?:ar|au|de|fr|es|es-us|it|mx|nz|au\.rss|uk)\.tv|tw\.uwant|(?:mh|nz|qos|yep)\.video|weather|(?:au|ca|hk|in|nz|sg|ph|uk|us)\.weather|de\.wetter|www|au\.yel|video\.media\.yql|dmros\.ysm)\.)?yahoo\.com/"
+ to="https://$1yahoo.com/" />
+
+ <rule from="^http://([\w-]+)\.yahoofs\.com/"
+ to="https://$1.yahoofs.com/" />
+
+ <rule from="^http://yhoo\.it/"
+ to="https://bit.ly/" />
+
+ <rule from="^http://(\w+)\.zenfs\.com/"
+ to="https://$1.zenfs.com/" />
+
+</ruleset>
diff --git a/searx/plugins/https_rules/YouTube.xml b/searx/plugins/https_rules/YouTube.xml
new file mode 100644
index 0000000..bddc2a5
--- /dev/null
+++ b/searx/plugins/https_rules/YouTube.xml
@@ -0,0 +1,46 @@
+<ruleset name="YouTube (partial)">
+
+ <target host="youtube.com" />
+ <target host="*.youtube.com" />
+ <exclusion pattern="^http://(?:www\.)?youtube\.com/crossdomain\.xml"/>
+ <exclusion pattern="^http://(?:www\.)?youtube\.com/(?:apiplayer|api_video_info)"/>
+ <exclusion pattern="^http://(?:[^/@:\.]+\.)?ytimg\.com/.*apiplayer[0-9]*\.swf"/>
+ <target host="*.ytimg.com" />
+ <target host="youtu.be" />
+ <target host="youtube-nocookie.com"/>
+ <target host="www.youtube-nocookie.com"/>
+ <target host="*.googlevideo.com"/>
+ <exclusion pattern="^http://([^/@:\.]+)\.googlevideo\.com/crossdomain\.xml"/>
+
+
+ <!-- Not secured by server:
+ -->
+ <!--securecookie host="^\.youtube\.com$" name="^(GEUP|PREF|VISITOR_INFO1_LIVE|YSC)$" /-->
+
+ <!-- observed ^. cookies:
+ - use_hitbox
+ - VISITOR_INFO1_LIVE
+ - recently_watched_video_id_list
+ - .youtube.com -->
+ <securecookie host="^\.youtube\.com" name=".*"/>
+
+
+ <rule from="^http://(www\.)?youtube\.com/"
+ to="https://$1youtube.com/"/>
+
+ <rule from="^http://(br|de|es|fr|il|img|insight|jp|m|nl|uk)\.youtube\.com/"
+ to="https://$1.youtube.com/"/>
+
+ <rule from="^http://([^/@:\.]+)\.ytimg\.com/"
+ to="https://$1.ytimg.com/"/>
+
+ <rule from="^http://youtu\.be/"
+ to="https://youtu.be/"/>
+
+ <rule from="^http://(?:www\.)?youtube-nocookie\.com/"
+ to="https://www.youtube-nocookie.com/"/>
+
+ <rule from="^http://([^/@:\.]+)\.googlevideo\.com/"
+ to="https://$1.googlevideo.com/"/>
+
+</ruleset>
diff --git a/searx/plugins/infinite_scroll.py b/searx/plugins/infinite_scroll.py
new file mode 100644
index 0000000..422a4be
--- /dev/null
+++ b/searx/plugins/infinite_scroll.py
@@ -0,0 +1,8 @@
+from flask_babel import gettext
+
+name = gettext('Infinite scroll')
+description = gettext('Automatically load next page when scrolling to bottom of current page')
+default_on = False
+
+js_dependencies = ('plugins/js/infinite_scroll.js',)
+css_dependencies = ('plugins/css/infinite_scroll.css',)
diff --git a/searx/plugins/open_results_on_new_tab.py b/searx/plugins/open_results_on_new_tab.py
new file mode 100644
index 0000000..ae27ea2
--- /dev/null
+++ b/searx/plugins/open_results_on_new_tab.py
@@ -0,0 +1,24 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2016 by Adam Tauber, <asciimoo@gmail.com>
+'''
+from flask_babel import gettext
+name = gettext('Open result links on new browser tabs')
+description = gettext('Results are opened in the same window by default. '
+ 'This plugin overwrites the default behaviour to open links on new tabs/windows. '
+ '(JavaScript required)')
+default_on = False
+
+js_dependencies = ('plugins/js/open_results_on_new_tab.js',)
diff --git a/searx/plugins/search_on_category_select.py b/searx/plugins/search_on_category_select.py
new file mode 100644
index 0000000..f72c63d
--- /dev/null
+++ b/searx/plugins/search_on_category_select.py
@@ -0,0 +1,23 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2015 by Adam Tauber, <asciimoo@gmail.com>
+'''
+from flask_babel import gettext
+name = gettext('Search on category select')
+description = gettext('Perform search immediately if a category selected. '
+ 'Disable to select multiple categories. (JavaScript required)')
+default_on = True
+
+js_dependencies = ('plugins/js/search_on_category_select.js',)
diff --git a/searx/plugins/self_info.py b/searx/plugins/self_info.py
new file mode 100644
index 0000000..8d6c661
--- /dev/null
+++ b/searx/plugins/self_info.py
@@ -0,0 +1,46 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2015 by Adam Tauber, <asciimoo@gmail.com>
+'''
+from flask_babel import gettext
+import re
+name = "Self Informations"
+description = gettext('Displays your IP if the query is "ip" and your user agent if the query contains "user agent".')
+default_on = True
+
+
+# Self User Agent regex
+p = re.compile(b'.*user[ -]agent.*', re.IGNORECASE)
+
+
+# attach callback to the post search hook
+# request: flask request object
+# ctx: the whole local context of the pre search hook
+def post_search(request, search):
+ if search.search_query.pageno > 1:
+ return True
+ if search.search_query.query == b'ip':
+ x_forwarded_for = request.headers.getlist("X-Forwarded-For")
+ if x_forwarded_for:
+ ip = x_forwarded_for[0]
+ else:
+ ip = request.remote_addr
+ search.result_container.answers.clear()
+ search.result_container.answers.add(ip)
+ elif p.match(search.search_query.query):
+ ua = request.user_agent
+ search.result_container.answers.clear()
+ search.result_container.answers.add(ua)
+ return True
diff --git a/searx/plugins/tracker_url_remover.py b/searx/plugins/tracker_url_remover.py
new file mode 100644
index 0000000..a840128
--- /dev/null
+++ b/searx/plugins/tracker_url_remover.py
@@ -0,0 +1,44 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2015 by Adam Tauber, <asciimoo@gmail.com>
+'''
+
+from flask_babel import gettext
+import re
+from searx.url_utils import urlunparse
+
+regexes = {re.compile(r'utm_[^&]+&?'),
+ re.compile(r'(wkey|wemail)[^&]+&?'),
+ re.compile(r'&$')}
+
+name = gettext('Tracker URL remover')
+description = gettext('Remove trackers arguments from the returned URL')
+default_on = True
+
+
+def on_result(request, search, result):
+ query = result['parsed_url'].query
+
+ if query == "":
+ return True
+
+ for reg in regexes:
+ query = reg.sub('', query)
+
+ if query != result['parsed_url'].query:
+ result['parsed_url'] = result['parsed_url']._replace(query=query)
+ result['url'] = urlunparse(result['parsed_url'])
+
+ return True
diff --git a/searx/plugins/vim_hotkeys.py b/searx/plugins/vim_hotkeys.py
new file mode 100644
index 0000000..8f06f13
--- /dev/null
+++ b/searx/plugins/vim_hotkeys.py
@@ -0,0 +1,10 @@
+from flask_babel import gettext
+
+name = gettext('Vim-like hotkeys')
+description = gettext('Navigate search results with Vim-like hotkeys '
+ '(JavaScript required). '
+ 'Press "h" key on main or result page to get help.')
+default_on = False
+
+js_dependencies = ('plugins/js/vim_hotkeys.js',)
+css_dependencies = ('plugins/css/vim_hotkeys.css',)
diff --git a/searx/poolrequests.py b/searx/poolrequests.py
new file mode 100644
index 0000000..f268df2
--- /dev/null
+++ b/searx/poolrequests.py
@@ -0,0 +1,112 @@
+import requests
+
+from itertools import cycle
+from threading import RLock
+from searx import settings
+
+
+class HTTPAdapterWithConnParams(requests.adapters.HTTPAdapter):
+
+ def __init__(self, pool_connections=requests.adapters.DEFAULT_POOLSIZE,
+ pool_maxsize=requests.adapters.DEFAULT_POOLSIZE,
+ max_retries=requests.adapters.DEFAULT_RETRIES,
+ pool_block=requests.adapters.DEFAULT_POOLBLOCK,
+ **conn_params):
+ if max_retries == requests.adapters.DEFAULT_RETRIES:
+ self.max_retries = requests.adapters.Retry(0, read=False)
+ else:
+ self.max_retries = requests.adapters.Retry.from_int(max_retries)
+ self.config = {}
+ self.proxy_manager = {}
+
+ super(requests.adapters.HTTPAdapter, self).__init__()
+
+ self._pool_connections = pool_connections
+ self._pool_maxsize = pool_maxsize
+ self._pool_block = pool_block
+ self._conn_params = conn_params
+
+ self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block, **conn_params)
+
+ def __setstate__(self, state):
+ # Can't handle by adding 'proxy_manager' to self.__attrs__ because
+ # because self.poolmanager uses a lambda function, which isn't pickleable.
+ self.proxy_manager = {}
+ self.config = {}
+
+ for attr, value in state.items():
+ setattr(self, attr, value)
+
+ self.init_poolmanager(self._pool_connections, self._pool_maxsize,
+ block=self._pool_block, **self._conn_params)
+
+
+connect = settings['outgoing'].get('pool_connections', 100) # Magic number kept from previous code
+maxsize = settings['outgoing'].get('pool_maxsize', requests.adapters.DEFAULT_POOLSIZE) # Picked from constructor
+if settings['outgoing'].get('source_ips'):
+ http_adapters = cycle(HTTPAdapterWithConnParams(pool_connections=connect, pool_maxsize=maxsize,
+ source_address=(source_ip, 0))
+ for source_ip in settings['outgoing']['source_ips'])
+ https_adapters = cycle(HTTPAdapterWithConnParams(pool_connections=connect, pool_maxsize=maxsize,
+ source_address=(source_ip, 0))
+ for source_ip in settings['outgoing']['source_ips'])
+else:
+ http_adapters = cycle((HTTPAdapterWithConnParams(pool_connections=connect, pool_maxsize=maxsize), ))
+ https_adapters = cycle((HTTPAdapterWithConnParams(pool_connections=connect, pool_maxsize=maxsize), ))
+
+
+class SessionSinglePool(requests.Session):
+
+ def __init__(self):
+ super(SessionSinglePool, self).__init__()
+
+ # reuse the same adapters
+ with RLock():
+ self.adapters.clear()
+ self.mount('https://', next(https_adapters))
+ self.mount('http://', next(http_adapters))
+
+ def close(self):
+ """Call super, but clear adapters since there are managed globaly"""
+ self.adapters.clear()
+ super(SessionSinglePool, self).close()
+
+
+def request(method, url, **kwargs):
+ """same as requests/requests/api.py request(...) except it use SessionSinglePool and force proxies"""
+ session = SessionSinglePool()
+ kwargs['proxies'] = settings['outgoing'].get('proxies', None)
+ response = session.request(method=method, url=url, **kwargs)
+ session.close()
+ return response
+
+
+def get(url, **kwargs):
+ kwargs.setdefault('allow_redirects', True)
+ return request('get', url, **kwargs)
+
+
+def options(url, **kwargs):
+ kwargs.setdefault('allow_redirects', True)
+ return request('options', url, **kwargs)
+
+
+def head(url, **kwargs):
+ kwargs.setdefault('allow_redirects', False)
+ return request('head', url, **kwargs)
+
+
+def post(url, data=None, **kwargs):
+ return request('post', url, data=data, **kwargs)
+
+
+def put(url, data=None, **kwargs):
+ return request('put', url, data=data, **kwargs)
+
+
+def patch(url, data=None, **kwargs):
+ return request('patch', url, data=data, **kwargs)
+
+
+def delete(url, **kwargs):
+ return request('delete', url, **kwargs)
diff --git a/searx/preferences.py b/searx/preferences.py
new file mode 100644
index 0000000..b6a2ec4
--- /dev/null
+++ b/searx/preferences.py
@@ -0,0 +1,304 @@
+from searx import settings, autocomplete
+from searx.languages import language_codes as languages
+
+
+COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5 # 5 years
+LANGUAGE_CODES = [l[0] for l in languages]
+LANGUAGE_CODES.append('all')
+DISABLED = 0
+ENABLED = 1
+
+
+class MissingArgumentException(Exception):
+ pass
+
+
+class ValidationException(Exception):
+ pass
+
+
+class Setting(object):
+ """Base class of user settings"""
+
+ def __init__(self, default_value, **kwargs):
+ super(Setting, self).__init__()
+ self.value = default_value
+ for key, value in kwargs.items():
+ setattr(self, key, value)
+
+ self._post_init()
+
+ def _post_init(self):
+ pass
+
+ def parse(self, data):
+ self.value = data
+
+ def get_value(self):
+ return self.value
+
+ def save(self, name, resp):
+ resp.set_cookie(name, self.value, max_age=COOKIE_MAX_AGE)
+
+
+class StringSetting(Setting):
+ """Setting of plain string values"""
+ pass
+
+
+class EnumStringSetting(Setting):
+ """Setting of a value which can only come from the given choices"""
+
+ def _validate_selection(self, selection):
+ if selection not in self.choices:
+ raise ValidationException('Invalid value: "{0}"'.format(selection))
+
+ def _post_init(self):
+ if not hasattr(self, 'choices'):
+ raise MissingArgumentException('Missing argument: choices')
+ self._validate_selection(self.value)
+
+ def parse(self, data):
+ self._validate_selection(data)
+ self.value = data
+
+
+class MultipleChoiceSetting(EnumStringSetting):
+ """Setting of values which can only come from the given choices"""
+
+ def _validate_selections(self, selections):
+ for item in selections:
+ if item not in self.choices:
+ raise ValidationException('Invalid value: "{0}"'.format(selections))
+
+ def _post_init(self):
+ if not hasattr(self, 'choices'):
+ raise MissingArgumentException('Missing argument: choices')
+ self._validate_selections(self.value)
+
+ def parse(self, data):
+ if data == '':
+ self.value = []
+ return
+
+ elements = data.split(',')
+ self._validate_selections(elements)
+ self.value = elements
+
+ def parse_form(self, data):
+ self.value = []
+ for choice in data:
+ if choice in self.choices and choice not in self.value:
+ self.value.append(choice)
+
+ def save(self, name, resp):
+ resp.set_cookie(name, ','.join(self.value), max_age=COOKIE_MAX_AGE)
+
+
+class SearchLanguageSetting(EnumStringSetting):
+ """Available choices may change, so user's value may not be in choices anymore"""
+
+ def parse(self, data):
+ if data not in self.choices and data != self.value:
+ # hack to give some backwards compatibility with old language cookies
+ data = str(data).replace('_', '-')
+ lang = data.split('-')[0]
+ if data in self.choices:
+ pass
+ elif lang in self.choices:
+ data = lang
+ elif data == 'nb-NO':
+ data = 'no-NO'
+ elif data == 'ar-XA':
+ data = 'ar-SA'
+ else:
+ data = self.value
+ self.value = data
+
+
+class MapSetting(Setting):
+ """Setting of a value that has to be translated in order to be storable"""
+
+ def _post_init(self):
+ if not hasattr(self, 'map'):
+ raise MissingArgumentException('missing argument: map')
+ if self.value not in self.map.values():
+ raise ValidationException('Invalid default value')
+
+ def parse(self, data):
+ if data not in self.map:
+ raise ValidationException('Invalid choice: {0}'.format(data))
+ self.value = self.map[data]
+ self.key = data
+
+ def save(self, name, resp):
+ if hasattr(self, 'key'):
+ resp.set_cookie(name, self.key, max_age=COOKIE_MAX_AGE)
+
+
+class SwitchableSetting(Setting):
+ """ Base class for settings that can be turned on && off"""
+
+ def _post_init(self):
+ self.disabled = set()
+ self.enabled = set()
+ if not hasattr(self, 'choices'):
+ raise MissingArgumentException('missing argument: choices')
+
+ def transform_form_items(self, items):
+ return items
+
+ def transform_values(self, values):
+ return values
+
+ def parse_cookie(self, data):
+ if data[DISABLED] != '':
+ self.disabled = set(data[DISABLED].split(','))
+ if data[ENABLED] != '':
+ self.enabled = set(data[ENABLED].split(','))
+
+ def parse_form(self, items):
+ items = self.transform_form_items(items)
+
+ self.disabled = set()
+ self.enabled = set()
+ for choice in self.choices:
+ if choice['default_on']:
+ if choice['id'] in items:
+ self.disabled.add(choice['id'])
+ else:
+ if choice['id'] not in items:
+ self.enabled.add(choice['id'])
+
+ def save(self, resp):
+ resp.set_cookie('disabled_{0}'.format(self.value), ','.join(self.disabled), max_age=COOKIE_MAX_AGE)
+ resp.set_cookie('enabled_{0}'.format(self.value), ','.join(self.enabled), max_age=COOKIE_MAX_AGE)
+
+ def get_disabled(self):
+ disabled = self.disabled
+ for choice in self.choices:
+ if not choice['default_on'] and choice['id'] not in self.enabled:
+ disabled.add(choice['id'])
+ return self.transform_values(disabled)
+
+ def get_enabled(self):
+ enabled = self.enabled
+ for choice in self.choices:
+ if choice['default_on'] and choice['id'] not in self.disabled:
+ enabled.add(choice['id'])
+ return self.transform_values(enabled)
+
+
+class EnginesSetting(SwitchableSetting):
+
+ def _post_init(self):
+ super(EnginesSetting, self)._post_init()
+ transformed_choices = []
+ for engine_name, engine in self.choices.items():
+ for category in engine.categories:
+ transformed_choice = dict()
+ transformed_choice['default_on'] = not engine.disabled
+ transformed_choice['id'] = '{}__{}'.format(engine_name, category)
+ transformed_choices.append(transformed_choice)
+ self.choices = transformed_choices
+
+ def transform_form_items(self, items):
+ return [item[len('engine_'):].replace('_', ' ').replace(' ', '__') for item in items]
+
+ def transform_values(self, values):
+ if len(values) == 1 and next(iter(values)) == '':
+ return list()
+ transformed_values = []
+ for value in values:
+ engine, category = value.split('__')
+ transformed_values.append((engine, category))
+ return transformed_values
+
+
+class PluginsSetting(SwitchableSetting):
+
+ def _post_init(self):
+ super(PluginsSetting, self)._post_init()
+ transformed_choices = []
+ for plugin in self.choices:
+ transformed_choice = dict()
+ transformed_choice['default_on'] = plugin.default_on
+ transformed_choice['id'] = plugin.id
+ transformed_choices.append(transformed_choice)
+ self.choices = transformed_choices
+
+ def transform_form_items(self, items):
+ return [item[len('plugin_'):] for item in items]
+
+
+class Preferences(object):
+ """Stores, validates and saves preferences to cookies"""
+
+ def __init__(self, themes, categories, engines, plugins):
+ super(Preferences, self).__init__()
+
+ self.key_value_settings = {'categories': MultipleChoiceSetting(['general'], choices=categories),
+ 'language': SearchLanguageSetting(settings['search']['language'],
+ choices=LANGUAGE_CODES),
+ 'locale': EnumStringSetting(settings['ui']['default_locale'],
+ choices=list(settings['locales'].keys()) + ['']),
+ 'autocomplete': EnumStringSetting(settings['search']['autocomplete'],
+ choices=list(autocomplete.backends.keys()) + ['']),
+ 'image_proxy': MapSetting(settings['server']['image_proxy'],
+ map={'': settings['server']['image_proxy'],
+ '0': False,
+ '1': True}),
+ 'method': EnumStringSetting('POST', choices=('GET', 'POST')),
+ 'safesearch': MapSetting(settings['search']['safe_search'], map={'0': 0,
+ '1': 1,
+ '2': 2}),
+ 'theme': EnumStringSetting(settings['ui']['default_theme'], choices=themes),
+ 'results_on_new_tab': MapSetting(False, map={'0': False, '1': True})}
+
+ self.engines = EnginesSetting('engines', choices=engines)
+ self.plugins = PluginsSetting('plugins', choices=plugins)
+ self.unknown_params = {}
+
+ def parse_cookies(self, input_data):
+ for user_setting_name, user_setting in input_data.items():
+ if user_setting_name in self.key_value_settings:
+ self.key_value_settings[user_setting_name].parse(user_setting)
+ elif user_setting_name == 'disabled_engines':
+ self.engines.parse_cookie((input_data.get('disabled_engines', ''),
+ input_data.get('enabled_engines', '')))
+ elif user_setting_name == 'disabled_plugins':
+ self.plugins.parse_cookie((input_data.get('disabled_plugins', ''),
+ input_data.get('enabled_plugins', '')))
+
+ def parse_form(self, input_data):
+ disabled_engines = []
+ enabled_categories = []
+ disabled_plugins = []
+ for user_setting_name, user_setting in input_data.items():
+ if user_setting_name in self.key_value_settings:
+ self.key_value_settings[user_setting_name].parse(user_setting)
+ elif user_setting_name.startswith('engine_'):
+ disabled_engines.append(user_setting_name)
+ elif user_setting_name.startswith('category_'):
+ enabled_categories.append(user_setting_name[len('category_'):])
+ elif user_setting_name.startswith('plugin_'):
+ disabled_plugins.append(user_setting_name)
+ else:
+ self.unknown_params[user_setting_name] = user_setting
+ self.key_value_settings['categories'].parse_form(enabled_categories)
+ self.engines.parse_form(disabled_engines)
+ self.plugins.parse_form(disabled_plugins)
+
+ # cannot be used in case of engines or plugins
+ def get_value(self, user_setting_name):
+ if user_setting_name in self.key_value_settings:
+ return self.key_value_settings[user_setting_name].get_value()
+
+ def save(self, resp):
+ for user_setting_name, user_setting in self.key_value_settings.items():
+ user_setting.save(user_setting_name, resp)
+ self.engines.save(resp)
+ self.plugins.save(resp)
+ for k, v in self.unknown_params.items():
+ resp.set_cookie(k, v, max_age=COOKIE_MAX_AGE)
+ return resp
diff --git a/searx/query.py b/searx/query.py
new file mode 100644
index 0000000..828a6fb
--- /dev/null
+++ b/searx/query.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
+'''
+
+from searx.languages import language_codes
+from searx.engines import (
+ categories, engines, engine_shortcuts
+)
+import re
+import string
+import sys
+
+if sys.version_info[0] == 3:
+ unicode = str
+
+VALID_LANGUAGE_CODE = re.compile(r'^[a-z]{2,3}(-[a-zA-Z]{2})?$')
+
+
+class RawTextQuery(object):
+ """parse raw text query (the value from the html input)"""
+
+ def __init__(self, query, disabled_engines):
+ self.query = query
+ self.disabled_engines = []
+
+ if disabled_engines:
+ self.disabled_engines = disabled_engines
+
+ self.query_parts = []
+ self.engines = []
+ self.languages = []
+ self.specific = False
+
+ # parse query, if tags are set, which
+ # change the serch engine or search-language
+ def parse_query(self):
+ self.query_parts = []
+
+ # split query, including whitespaces
+ raw_query_parts = re.split(r'(\s+)', self.query)
+
+ parse_next = True
+
+ for query_part in raw_query_parts:
+ if not parse_next:
+ self.query_parts[-1] += query_part
+ continue
+
+ parse_next = False
+
+ # part does only contain spaces, skip
+ if query_part.isspace()\
+ or query_part == '':
+ parse_next = True
+ self.query_parts.append(query_part)
+ continue
+
+ # this force a language
+ if query_part[0] == ':':
+ lang = query_part[1:].lower().replace('_', '-')
+
+ # user may set a valid, yet not selectable language
+ if VALID_LANGUAGE_CODE.match(lang):
+ self.languages.append(lang)
+ parse_next = True
+
+ # check if any language-code is equal with
+ # declared language-codes
+ for lc in language_codes:
+ lang_id, lang_name, country, english_name = map(unicode.lower, lc)
+
+ # if correct language-code is found
+ # set it as new search-language
+ if lang == lang_id\
+ or lang_id.startswith(lang)\
+ or lang == lang_name\
+ or lang == english_name\
+ or lang.replace('-', ' ') == country:
+ parse_next = True
+ self.languages.append(lang_id)
+ # to ensure best match (first match is not necessarily the best one)
+ if lang == lang_id:
+ break
+
+ # this force a engine or category
+ if query_part[0] == '!' or query_part[0] == '?':
+ prefix = query_part[1:].replace('-', ' ').replace('_', ' ')
+
+ # check if prefix is equal with engine shortcut
+ if prefix in engine_shortcuts:
+ parse_next = True
+ self.engines.append({'category': 'none',
+ 'name': engine_shortcuts[prefix]})
+
+ # check if prefix is equal with engine name
+ elif prefix in engines:
+ parse_next = True
+ self.engines.append({'category': 'none',
+ 'name': prefix})
+
+ # check if prefix is equal with categorie name
+ elif prefix in categories:
+ # using all engines for that search, which
+ # are declared under that categorie name
+ parse_next = True
+ self.engines.extend({'category': prefix,
+ 'name': engine.name}
+ for engine in categories[prefix]
+ if (engine.name, prefix) not in self.disabled_engines)
+
+ if query_part[0] == '!':
+ self.specific = True
+
+ # append query part to query_part list
+ self.query_parts.append(query_part)
+
+ def changeSearchQuery(self, search_query):
+ if len(self.query_parts):
+ self.query_parts[-1] = search_query
+ else:
+ self.query_parts.append(search_query)
+
+ def getSearchQuery(self):
+ if len(self.query_parts):
+ return self.query_parts[-1]
+ else:
+ return ''
+
+ def getFullQuery(self):
+ # get full querry including whitespaces
+ return string.join(self.query_parts, '')
+
+
+class SearchQuery(object):
+ """container for all the search parameters (query, language, etc...)"""
+
+ def __init__(self, query, engines, categories, lang, safesearch, pageno, time_range):
+ self.query = query.encode('utf-8')
+ self.engines = engines
+ self.categories = categories
+ self.lang = lang
+ self.safesearch = safesearch
+ self.pageno = pageno
+ self.time_range = time_range
+
+ def __str__(self):
+ return str(self.query) + ";" + str(self.engines)
diff --git a/searx/results.py b/searx/results.py
new file mode 100644
index 0000000..6abffb5
--- /dev/null
+++ b/searx/results.py
@@ -0,0 +1,306 @@
+import re
+import sys
+from collections import defaultdict
+from operator import itemgetter
+from threading import RLock
+from searx.engines import engines
+from searx.url_utils import urlparse, unquote
+
+if sys.version_info[0] == 3:
+ basestring = str
+
+CONTENT_LEN_IGNORED_CHARS_REGEX = re.compile(r'[,;:!?\./\\\\ ()-_]', re.M | re.U)
+WHITESPACE_REGEX = re.compile('( |\t|\n)+', re.M | re.U)
+
+
+# return the meaningful length of the content for a result
+def result_content_len(content):
+ if isinstance(content, basestring):
+ return len(CONTENT_LEN_IGNORED_CHARS_REGEX.sub('', content))
+ else:
+ return 0
+
+
+def compare_urls(url_a, url_b):
+ # ignore www. in comparison
+ if url_a.netloc.startswith('www.'):
+ host_a = url_a.netloc.replace('www.', '', 1)
+ else:
+ host_a = url_a.netloc
+ if url_b.netloc.startswith('www.'):
+ host_b = url_b.netloc.replace('www.', '', 1)
+ else:
+ host_b = url_b.netloc
+
+ if host_a != host_b or url_a.query != url_b.query or url_a.fragment != url_b.fragment:
+ return False
+
+ # remove / from the end of the url if required
+ path_a = url_a.path[:-1]\
+ if url_a.path.endswith('/')\
+ else url_a.path
+ path_b = url_b.path[:-1]\
+ if url_b.path.endswith('/')\
+ else url_b.path
+
+ return unquote(path_a) == unquote(path_b)
+
+
+def merge_two_infoboxes(infobox1, infobox2):
+ # get engines weights
+ if hasattr(engines[infobox1['engine']], 'weight'):
+ weight1 = engines[infobox1['engine']].weight
+ else:
+ weight1 = 1
+ if hasattr(engines[infobox2['engine']], 'weight'):
+ weight2 = engines[infobox2['engine']].weight
+ else:
+ weight2 = 1
+
+ if weight2 > weight1:
+ infobox1['engine'] = infobox2['engine']
+
+ if 'urls' in infobox2:
+ urls1 = infobox1.get('urls', None)
+ if urls1 is None:
+ urls1 = []
+
+ for url2 in infobox2.get('urls', []):
+ unique_url = True
+ for url1 in infobox1.get('urls', []):
+ if compare_urls(urlparse(url1.get('url', '')), urlparse(url2.get('url', ''))):
+ unique_url = False
+ break
+ if unique_url:
+ urls1.append(url2)
+
+ infobox1['urls'] = urls1
+
+ if 'img_src' in infobox2:
+ img1 = infobox1.get('img_src', None)
+ img2 = infobox2.get('img_src')
+ if img1 is None:
+ infobox1['img_src'] = img2
+ elif weight2 > weight1:
+ infobox1['img_src'] = img2
+
+ if 'attributes' in infobox2:
+ attributes1 = infobox1.get('attributes', None)
+ if attributes1 is None:
+ attributes1 = []
+ infobox1['attributes'] = attributes1
+
+ attributeSet = set()
+ for attribute in infobox1.get('attributes', []):
+ if attribute.get('label', None) not in attributeSet:
+ attributeSet.add(attribute.get('label', None))
+
+ for attribute in infobox2.get('attributes', []):
+ if attribute.get('label', None) not in attributeSet:
+ attributes1.append(attribute)
+
+ if 'content' in infobox2:
+ content1 = infobox1.get('content', None)
+ content2 = infobox2.get('content', '')
+ if content1 is not None:
+ if result_content_len(content2) > result_content_len(content1):
+ infobox1['content'] = content2
+ else:
+ infobox1['content'] = content2
+
+
+def result_score(result):
+ weight = 1.0
+
+ for result_engine in result['engines']:
+ if hasattr(engines[result_engine], 'weight'):
+ weight *= float(engines[result_engine].weight)
+
+ occurences = len(result['positions'])
+
+ return sum((occurences * weight) / position for position in result['positions'])
+
+
+class ResultContainer(object):
+ """docstring for ResultContainer"""
+
+ def __init__(self):
+ super(ResultContainer, self).__init__()
+ self.results = defaultdict(list)
+ self._merged_results = []
+ self.infoboxes = []
+ self.suggestions = set()
+ self.answers = set()
+ self.corrections = set()
+ self._number_of_results = []
+ self._ordered = False
+ self.paging = False
+
+ def extend(self, engine_name, results):
+ for result in list(results):
+ result['engine'] = engine_name
+ if 'suggestion' in result:
+ self.suggestions.add(result['suggestion'])
+ results.remove(result)
+ elif 'answer' in result:
+ self.answers.add(result['answer'])
+ results.remove(result)
+ elif 'correction' in result:
+ self.corrections.add(result['correction'])
+ results.remove(result)
+ elif 'infobox' in result:
+ self._merge_infobox(result)
+ results.remove(result)
+ elif 'number_of_results' in result:
+ self._number_of_results.append(result['number_of_results'])
+ results.remove(result)
+
+ if engine_name in engines:
+ with RLock():
+ engines[engine_name].stats['search_count'] += 1
+ engines[engine_name].stats['result_count'] += len(results)
+
+ if not results:
+ return
+
+ self.results[engine_name].extend(results)
+
+ if not self.paging and engine_name in engines and engines[engine_name].paging:
+ self.paging = True
+
+ for i, result in enumerate(results):
+ try:
+ result['url'] = result['url'].decode('utf-8')
+ except:
+ pass
+ position = i + 1
+ self._merge_result(result, position)
+
+ def _merge_infobox(self, infobox):
+ add_infobox = True
+ infobox_id = infobox.get('id', None)
+ if infobox_id is not None:
+ for existingIndex in self.infoboxes:
+ if compare_urls(urlparse(existingIndex.get('id', '')), urlparse(infobox_id)):
+ merge_two_infoboxes(existingIndex, infobox)
+ add_infobox = False
+
+ if add_infobox:
+ self.infoboxes.append(infobox)
+
+ def _merge_result(self, result, position):
+ result['parsed_url'] = urlparse(result['url'])
+
+ # if the result has no scheme, use http as default
+ if not result['parsed_url'].scheme:
+ result['parsed_url'] = result['parsed_url']._replace(scheme="http")
+ result['url'] = result['parsed_url'].geturl()
+
+ result['engines'] = [result['engine']]
+
+ # strip multiple spaces and cariage returns from content
+ if result.get('content'):
+ result['content'] = WHITESPACE_REGEX.sub(' ', result['content'])
+
+ # check for duplicates
+ duplicated = False
+ for merged_result in self._merged_results:
+ if compare_urls(result['parsed_url'], merged_result['parsed_url'])\
+ and result.get('template') == merged_result.get('template'):
+ duplicated = merged_result
+ break
+
+ # merge duplicates together
+ if duplicated:
+ # using content with more text
+ if result_content_len(result.get('content', '')) >\
+ result_content_len(duplicated.get('content', '')):
+ duplicated['content'] = result['content']
+
+ # add the new position
+ duplicated['positions'].append(position)
+
+ # add engine to list of result-engines
+ duplicated['engines'].append(result['engine'])
+
+ # using https if possible
+ if duplicated['parsed_url'].scheme != 'https' and result['parsed_url'].scheme == 'https':
+ duplicated['url'] = result['parsed_url'].geturl()
+ duplicated['parsed_url'] = result['parsed_url']
+
+ # if there is no duplicate found, append result
+ else:
+ result['positions'] = [position]
+ with RLock():
+ self._merged_results.append(result)
+
+ def order_results(self):
+ for result in self._merged_results:
+ score = result_score(result)
+ result['score'] = score
+ with RLock():
+ for result_engine in result['engines']:
+ engines[result_engine].stats['score_count'] += score
+
+ results = sorted(self._merged_results, key=itemgetter('score'), reverse=True)
+
+ # pass 2 : group results by category and template
+ gresults = []
+ categoryPositions = {}
+
+ for i, res in enumerate(results):
+ # FIXME : handle more than one category per engine
+ res['category'] = engines[res['engine']].categories[0]
+
+ # FIXME : handle more than one category per engine
+ category = engines[res['engine']].categories[0]\
+ + ':' + res.get('template', '')\
+ + ':' + ('img_src' if 'img_src' in res or 'thumbnail' in res else '')
+
+ current = None if category not in categoryPositions\
+ else categoryPositions[category]
+
+ # group with previous results using the same category
+ # if the group can accept more result and is not too far
+ # from the current position
+ if current is not None and (current['count'] > 0)\
+ and (len(gresults) - current['index'] < 20):
+ # group with the previous results using
+ # the same category with this one
+ index = current['index']
+ gresults.insert(index, res)
+
+ # update every index after the current one
+ # (including the current one)
+ for k in categoryPositions:
+ v = categoryPositions[k]['index']
+ if v >= index:
+ categoryPositions[k]['index'] = v + 1
+
+ # update this category
+ current['count'] -= 1
+
+ else:
+ # same category
+ gresults.append(res)
+
+ # update categoryIndex
+ categoryPositions[category] = {'index': len(gresults), 'count': 8}
+
+ # update _merged_results
+ self._ordered = True
+ self._merged_results = gresults
+
+ def get_ordered_results(self):
+ if not self._ordered:
+ self.order_results()
+ return self._merged_results
+
+ def results_length(self):
+ return len(self._merged_results)
+
+ def results_number(self):
+ resultnum_sum = sum(self._number_of_results)
+ if not resultnum_sum or not self._number_of_results:
+ return 0
+ return resultnum_sum / len(self._number_of_results)
diff --git a/searx/search.py b/searx/search.py
new file mode 100644
index 0000000..790e7d0
--- /dev/null
+++ b/searx/search.py
@@ -0,0 +1,432 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
+'''
+
+import gc
+import sys
+import threading
+from time import time
+from uuid import uuid4
+import requests.exceptions
+import searx.poolrequests as requests_lib
+from searx.engines import (
+ categories, engines
+)
+from searx.answerers import ask
+from searx.utils import gen_useragent
+from searx.query import RawTextQuery, SearchQuery, VALID_LANGUAGE_CODE
+from searx.results import ResultContainer
+from searx import logger
+from searx.plugins import plugins
+from searx.exceptions import SearxParameterException
+
+try:
+ from thread import start_new_thread
+except:
+ from _thread import start_new_thread
+
+if sys.version_info[0] == 3:
+ unicode = str
+
+logger = logger.getChild('search')
+
+number_of_searches = 0
+
+
+def send_http_request(engine, request_params, start_time, timeout_limit):
+ # for page_load_time stats
+ time_before_request = time()
+
+ # create dictionary which contain all
+ # informations about the request
+ request_args = dict(
+ headers=request_params['headers'],
+ cookies=request_params['cookies'],
+ timeout=timeout_limit,
+ verify=request_params['verify']
+ )
+
+ # specific type of request (GET or POST)
+ if request_params['method'] == 'GET':
+ req = requests_lib.get
+ else:
+ req = requests_lib.post
+ request_args['data'] = request_params['data']
+
+ # send the request
+ response = req(request_params['url'], **request_args)
+
+ # is there a timeout (no parsing in this case)
+ timeout_overhead = 0.2 # seconds
+ time_after_request = time()
+ search_duration = time_after_request - start_time
+ if search_duration > timeout_limit + timeout_overhead:
+ raise requests.exceptions.Timeout(response=response)
+
+ with threading.RLock():
+ # no error : reset the suspend variables
+ engine.continuous_errors = 0
+ engine.suspend_end_time = 0
+ # update stats with current page-load-time
+ # only the HTTP request
+ engine.stats['page_load_time'] += time_after_request - time_before_request
+ engine.stats['page_load_count'] += 1
+
+ # everything is ok : return the response
+ return response
+
+
+def search_one_request(engine, query, request_params, start_time, timeout_limit):
+ # update request parameters dependent on
+ # search-engine (contained in engines folder)
+ engine.request(query, request_params)
+
+ # ignoring empty urls
+ if request_params['url'] is None:
+ return []
+
+ if not request_params['url']:
+ return []
+
+ # send request
+ response = send_http_request(engine, request_params, start_time, timeout_limit)
+
+ # parse the response
+ response.search_params = request_params
+ return engine.response(response)
+
+
+def search_one_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit):
+ engine = engines[engine_name]
+
+ try:
+ # send requests and parse the results
+ search_results = search_one_request(engine, query, request_params, start_time, timeout_limit)
+
+ # add results
+ result_container.extend(engine_name, search_results)
+
+ # update engine time when there is no exception
+ with threading.RLock():
+ engine.stats['engine_time'] += time() - start_time
+ engine.stats['engine_time_count'] += 1
+
+ return True
+
+ except Exception as e:
+ engine.stats['errors'] += 1
+
+ search_duration = time() - start_time
+ requests_exception = False
+
+ if (issubclass(e.__class__, requests.exceptions.Timeout)):
+ # requests timeout (connect or read)
+ logger.error("engine {0} : HTTP requests timeout"
+ "(search duration : {1} s, timeout: {2} s) : {3}"
+ .format(engine_name, search_duration, timeout_limit, e.__class__.__name__))
+ requests_exception = True
+ elif (issubclass(e.__class__, requests.exceptions.RequestException)):
+ # other requests exception
+ logger.exception("engine {0} : requests exception"
+ "(search duration : {1} s, timeout: {2} s) : {3}"
+ .format(engine_name, search_duration, timeout_limit, e))
+ requests_exception = True
+ else:
+ # others errors
+ logger.exception('engine {0} : exception : {1}'.format(engine_name, e))
+
+ # update continuous_errors / suspend_end_time
+ if requests_exception:
+ with threading.RLock():
+ engine.continuous_errors += 1
+ engine.suspend_end_time = time() + min(60, engine.continuous_errors)
+
+ #
+ return False
+
+
+def search_multiple_requests(requests, result_container, start_time, timeout_limit):
+ search_id = uuid4().__str__()
+
+ for engine_name, query, request_params in requests:
+ th = threading.Thread(
+ target=search_one_request_safe,
+ args=(engine_name, query, request_params, result_container, start_time, timeout_limit),
+ name=search_id,
+ )
+ th._engine_name = engine_name
+ th.start()
+
+ for th in threading.enumerate():
+ if th.name == search_id:
+ remaining_time = max(0.0, timeout_limit - (time() - start_time))
+ th.join(remaining_time)
+ if th.isAlive():
+ logger.warning('engine timeout: {0}'.format(th._engine_name))
+
+
+# get default reqest parameter
+def default_request_params():
+ return {
+ 'method': 'GET',
+ 'headers': {},
+ 'data': {},
+ 'url': '',
+ 'cookies': {},
+ 'verify': True
+ }
+
+
+def get_search_query_from_webapp(preferences, form):
+ # no text for the query ?
+ if not form.get('q'):
+ raise SearxParameterException('q', '')
+
+ # set blocked engines
+ disabled_engines = preferences.engines.get_disabled()
+
+ # parse query, if tags are set, which change
+ # the serch engine or search-language
+ raw_text_query = RawTextQuery(form['q'], disabled_engines)
+ raw_text_query.parse_query()
+
+ # set query
+ query = raw_text_query.getSearchQuery()
+
+ # get and check page number
+ pageno_param = form.get('pageno', '1')
+ if not pageno_param.isdigit() or int(pageno_param) < 1:
+ raise SearxParameterException('pageno', pageno_param)
+ query_pageno = int(pageno_param)
+
+ # get language
+ # set specific language if set on request, query or preferences
+ # TODO support search with multible languages
+ if len(raw_text_query.languages):
+ query_lang = raw_text_query.languages[-1]
+ elif 'language' in form:
+ query_lang = form.get('language')
+ else:
+ query_lang = preferences.get_value('language')
+
+ # check language
+ if not VALID_LANGUAGE_CODE.match(query_lang):
+ raise SearxParameterException('language', query_lang)
+
+ # get safesearch
+ if 'safesearch' in form:
+ query_safesearch = form.get('safesearch')
+ # first check safesearch
+ if not query_safesearch.isdigit():
+ raise SearxParameterException('safesearch', query_safesearch)
+ query_safesearch = int(query_safesearch)
+ else:
+ query_safesearch = preferences.get_value('safesearch')
+
+ # safesearch : second check
+ if query_safesearch < 0 or query_safesearch > 2:
+ raise SearxParameterException('safesearch', query_safesearch)
+
+ # get time_range
+ query_time_range = form.get('time_range')
+
+ # check time_range
+ if query_time_range not in ('None', None, '', 'day', 'week', 'month', 'year'):
+ raise SearxParameterException('time_range', query_time_range)
+
+ # query_engines
+ query_engines = raw_text_query.engines
+
+ # query_categories
+ query_categories = []
+
+ # if engines are calculated from query,
+ # set categories by using that informations
+ if query_engines and raw_text_query.specific:
+ query_categories = list(set(engine['category']
+ for engine in query_engines))
+
+ # otherwise, using defined categories to
+ # calculate which engines should be used
+ else:
+ # set categories/engines
+ load_default_categories = True
+ for pd_name, pd in form.items():
+ if pd_name == 'categories':
+ query_categories.extend(categ for categ in map(unicode.strip, pd.split(',')) if categ in categories)
+ elif pd_name == 'engines':
+ pd_engines = [{'category': engines[engine].categories[0],
+ 'name': engine}
+ for engine in map(unicode.strip, pd.split(',')) if engine in engines]
+ if pd_engines:
+ query_engines.extend(pd_engines)
+ load_default_categories = False
+ elif pd_name.startswith('category_'):
+ category = pd_name[9:]
+
+ # if category is not found in list, skip
+ if category not in categories:
+ continue
+
+ if pd != 'off':
+ # add category to list
+ query_categories.append(category)
+ elif category in query_categories:
+ # remove category from list if property is set to 'off'
+ query_categories.remove(category)
+
+ if not load_default_categories:
+ if not query_categories:
+ query_categories = list(set(engine['category']
+ for engine in query_engines))
+ else:
+ # if no category is specified for this search,
+ # using user-defined default-configuration which
+ # (is stored in cookie)
+ if not query_categories:
+ cookie_categories = preferences.get_value('categories')
+ for ccateg in cookie_categories:
+ if ccateg in categories:
+ query_categories.append(ccateg)
+
+ # if still no category is specified, using general
+ # as default-category
+ if not query_categories:
+ query_categories = ['general']
+
+ # using all engines for that search, which are
+ # declared under the specific categories
+ for categ in query_categories:
+ query_engines.extend({'category': categ,
+ 'name': engine.name}
+ for engine in categories[categ]
+ if (engine.name, categ) not in disabled_engines)
+
+ return SearchQuery(query, query_engines, query_categories,
+ query_lang, query_safesearch, query_pageno, query_time_range)
+
+
+class Search(object):
+
+ """Search information container"""
+
+ def __init__(self, search_query):
+ # init vars
+ super(Search, self).__init__()
+ self.search_query = search_query
+ self.result_container = ResultContainer()
+
+ # do search-request
+ def search(self):
+ global number_of_searches
+
+ # start time
+ start_time = time()
+
+ # answeres ?
+ answerers_results = ask(self.search_query)
+
+ if answerers_results:
+ for results in answerers_results:
+ self.result_container.extend('answer', results)
+ return self.result_container
+
+ # init vars
+ requests = []
+
+ # increase number of searches
+ number_of_searches += 1
+
+ # set default useragent
+ # user_agent = request.headers.get('User-Agent', '')
+ user_agent = gen_useragent()
+
+ search_query = self.search_query
+
+ # max of all selected engine timeout
+ timeout_limit = 0
+
+ # start search-reqest for all selected engines
+ for selected_engine in search_query.engines:
+ if selected_engine['name'] not in engines:
+ continue
+
+ engine = engines[selected_engine['name']]
+
+ # skip suspended engines
+ if engine.suspend_end_time >= time():
+ logger.debug('Engine currently suspended: %s', selected_engine['name'])
+ continue
+
+ # if paging is not supported, skip
+ if search_query.pageno > 1 and not engine.paging:
+ continue
+
+ # if time_range is not supported, skip
+ if search_query.time_range and not engine.time_range_support:
+ continue
+
+ # set default request parameters
+ request_params = default_request_params()
+ request_params['headers']['User-Agent'] = user_agent
+ request_params['category'] = selected_engine['category']
+ request_params['pageno'] = search_query.pageno
+
+ if hasattr(engine, 'language') and engine.language:
+ request_params['language'] = engine.language
+ else:
+ request_params['language'] = search_query.lang
+
+ # 0 = None, 1 = Moderate, 2 = Strict
+ request_params['safesearch'] = search_query.safesearch
+ request_params['time_range'] = search_query.time_range
+
+ # append request to list
+ requests.append((selected_engine['name'], search_query.query, request_params))
+
+ # update timeout_limit
+ timeout_limit = max(timeout_limit, engine.timeout)
+
+ if requests:
+ # send all search-request
+ search_multiple_requests(requests, self.result_container, start_time, timeout_limit)
+ start_new_thread(gc.collect, tuple())
+
+ # return results, suggestions, answers and infoboxes
+ return self.result_container
+
+
+class SearchWithPlugins(Search):
+
+ """Similar to the Search class but call the plugins."""
+
+ def __init__(self, search_query, ordered_plugin_list, request):
+ super(SearchWithPlugins, self).__init__(search_query)
+ self.ordered_plugin_list = ordered_plugin_list
+ self.request = request
+
+ def search(self):
+ if plugins.call(self.ordered_plugin_list, 'pre_search', self.request, self):
+ super(SearchWithPlugins, self).search()
+
+ plugins.call(self.ordered_plugin_list, 'post_search', self.request, self)
+
+ results = self.result_container.get_ordered_results()
+
+ for result in results:
+ plugins.call(self.ordered_plugin_list, 'on_result', self.request, self, result)
+
+ return self.result_container
diff --git a/searx/settings.yml b/searx/settings.yml
new file mode 100644
index 0000000..17b0bd5
--- /dev/null
+++ b/searx/settings.yml
@@ -0,0 +1,686 @@
+general:
+ debug : False # Debug mode, only for development
+ instance_name : "searx" # displayed name
+
+search:
+ safe_search : 0 # Filter results. 0: None, 1: Moderate, 2: Strict
+ autocomplete : "" # Existing autocomplete backends: "dbpedia", "duckduckgo", "google", "startpage", "wikipedia" - leave blank to turn it off by default
+ language : "all"
+
+server:
+ port : 8888
+ bind_address : "127.0.0.1" # address to listen on
+ secret_key : "ultrasecretkey" # change this!
+ base_url : False # Set custom base_url. Possible values: False or "https://your.custom.host/location/"
+ image_proxy : False # Proxying image results through searx
+ http_protocol_version : "1.0" # 1.0 and 1.1 are supported
+
+ui:
+ static_path : "" # Custom static path - leave it blank if you didn't change
+ templates_path : "" # Custom templates path - leave it blank if you didn't change
+ default_theme : oscar # ui theme
+ default_locale : "" # Default interface locale - leave blank to detect from browser information or use codes from the 'locales' config section
+
+# searx supports result proxification using an external service: https://github.com/asciimoo/morty
+# uncomment below section if you have running morty proxy
+#result_proxy:
+# url : http://127.0.0.1:3000/
+# key : your_morty_proxy_key
+
+outgoing: # communication with search engines
+ request_timeout : 2.0 # seconds
+ useragent_suffix : "" # suffix of searx_useragent, could contain informations like an email address to the administrator
+ pool_connections : 100 # Number of different hosts
+ pool_maxsize : 10 # Number of simultaneous requests by host
+# uncomment below section if you want to use a proxy
+# see http://docs.python-requests.org/en/latest/user/advanced/#proxies
+# SOCKS proxies are also supported: see http://docs.python-requests.org/en/master/user/advanced/#socks
+# proxies :
+# http : http://127.0.0.1:8080
+# https: http://127.0.0.1:8080
+# uncomment below section only if you have more than one network interface
+# which can be the source of outgoing search requests
+# source_ips:
+# - 1.1.1.1
+# - 1.1.1.2
+
+engines:
+ - name : arch linux wiki
+ engine : archlinux
+ shortcut : al
+
+ - name : archive is
+ engine : xpath
+ search_url : https://archive.is/{query}
+ url_xpath : (//div[@class="TEXT-BLOCK"]/a)/@href
+ title_xpath : (//div[@class="TEXT-BLOCK"]/a)
+ content_xpath : //div[@class="TEXT-BLOCK"]/ul/li
+ categories : general
+ timeout : 7.0
+ disabled : True
+ shortcut : ai
+
+ - name : base
+ engine : base
+ shortcut : bs
+
+ - name : wikipedia
+ engine : wikipedia
+ shortcut : wp
+ base_url : 'https://{language}.wikipedia.org/'
+
+ - name : bing
+ engine : bing
+ shortcut : bi
+
+ - name : bing images
+ engine : bing_images
+ shortcut : bii
+
+ - name : bing news
+ engine : bing_news
+ shortcut : bin
+
+ - name : bitbucket
+ engine : xpath
+ paging : True
+ search_url : https://bitbucket.org/repo/all/{pageno}?name={query}
+ url_xpath : //article[@class="repo-summary"]//a[@class="repo-link"]/@href
+ title_xpath : //article[@class="repo-summary"]//a[@class="repo-link"]
+ content_xpath : //article[@class="repo-summary"]/p
+ categories : it
+ timeout : 4.0
+ disabled : True
+ shortcut : bb
+
+ - name : ccc-tv
+ engine : xpath
+ paging : False
+ search_url : https://media.ccc.de/search/?q={query}
+ url_xpath : //div[@class="caption"]/h3/a/@href
+ title_xpath : //div[@class="caption"]/h3/a/text()
+ content_xpath : //div[@class="caption"]/h4/@title
+ categories : videos
+ disabled : True
+ shortcut : c3tv
+
+ - name : crossref
+ engine : json_engine
+ paging : True
+ search_url : http://search.crossref.org/dois?q={query}&page={pageno}
+ url_query : doi
+ title_query : title
+ content_query : fullCitation
+ categories : science
+ shortcut : cr
+
+ - name : currency
+ engine : currency_convert
+ categories : general
+ shortcut : cc
+
+ - name : deezer
+ engine : deezer
+ shortcut : dz
+
+ - name : deviantart
+ engine : deviantart
+ shortcut : da
+ timeout: 3.0
+
+ - name : ddg definitions
+ engine : duckduckgo_definitions
+ shortcut : ddd
+ weight : 2
+ disabled : True
+
+ - name : digbt
+ engine : digbt
+ shortcut : dbt
+ timeout : 6.0
+ disabled : True
+
+ - name : digg
+ engine : digg
+ shortcut : dg
+
+ - name : erowid
+ engine : xpath
+ paging : True
+ first_page_num : 0
+ page_size : 30
+ search_url : https://www.erowid.org/search.php?q={query}&s={pageno}
+ url_xpath : //dl[@class="results-list"]/dt[@class="result-title"]/a/@href
+ title_xpath : //dl[@class="results-list"]/dt[@class="result-title"]/a/text()
+ content_xpath : //dl[@class="results-list"]/dd[@class="result-details"]
+ categories : general
+ shortcut : ew
+ disabled : True
+
+ - name : wikidata
+ engine : wikidata
+ shortcut : wd
+ weight : 2
+
+ - name : duckduckgo
+ engine : duckduckgo
+ shortcut : ddg
+ disabled : True
+
+ - name : duckduckgo images
+ engine : duckduckgo_images
+ shortcut : ddi
+ timeout: 3.0
+ disabled : True
+
+ - name : etymonline
+ engine : xpath
+ paging : True
+ search_url : http://etymonline.com/?search={query}&p={pageno}
+ url_xpath : //dt/a[1]/@href
+ title_xpath : //dt
+ content_xpath : //dd
+ suggestion_xpath : //a[@class="crossreference"]
+ first_page_num : 0
+ shortcut : et
+ disabled : True
+
+# api-key required: http://www.faroo.com/hp/api/api.html#key
+# - name : faroo
+# engine : faroo
+# shortcut : fa
+# api_key : 'apikey' # required!
+
+ - name : 500px
+ engine : www500px
+ shortcut : px
+
+ - name : 1x
+ engine : www1x
+ shortcut : 1x
+ disabled : True
+
+ - name : fdroid
+ engine : fdroid
+ shortcut : fd
+ disabled : True
+
+ - name : flickr
+ categories : images
+ shortcut : fl
+# You can use the engine using the official stable API, but you need an API key
+# See : https://www.flickr.com/services/apps/create/
+# engine : flickr
+# api_key: 'apikey' # required!
+# Or you can use the html non-stable engine, activated by default
+ engine : flickr_noapi
+
+ - name : free software directory
+ engine : mediawiki
+ shortcut : fsd
+ categories : it
+ base_url : https://directory.fsf.org/
+ number_of_results : 5
+# what part of a page matches the query string: title, text, nearmatch
+# title - query matches title, text - query matches the text of page, nearmatch - nearmatch in title
+ search_type : title
+ timeout : 5.0
+ disabled : True
+
+ - name : frinkiac
+ engine : frinkiac
+ shortcut : frk
+ disabled : True
+
+ - name : gigablast
+ engine : gigablast
+ shortcut : gb
+ timeout : 3.0
+ disabled: True
+
+ - name : gitlab
+ engine : xpath
+ paging : True
+ search_url : https://gitlab.com/search?page={pageno}&search={query}
+ url_xpath : //li[@class="project-row"]//a[@class="project"]/@href
+ title_xpath : //li[@class="project-row"]//span[contains(@class, "project-full-name")]
+ content_xpath : //li[@class="project-row"]//div[@class="description"]/p
+ categories : it
+ shortcut : gl
+ timeout : 5.0
+ disabled : True
+
+ - name : github
+ engine : github
+ shortcut : gh
+
+ - name : google
+ engine : google
+ shortcut : go
+
+ - name : google images
+ engine : google_images
+ shortcut : goi
+
+ - name : google news
+ engine : google_news
+ shortcut : gon
+
+ - name : google scholar
+ engine : xpath
+ paging : True
+ search_url : https://scholar.google.com/scholar?start={pageno}&q={query}&hl=en&as_sdt=0,5&as_vis=1
+ results_xpath : //div[@class="gs_r"]/div[@class="gs_ri"]
+ url_xpath : .//h3/a/@href
+ title_xpath : .//h3/a
+ content_xpath : .//div[@class="gs_rs"]
+ suggestion_xpath : //div[@id="gs_qsuggest"]/ul/li
+ page_size : 10
+ first_page_num : 0
+ categories : science
+ shortcut : gos
+
+ - name : google play apps
+ engine : xpath
+ search_url : https://play.google.com/store/search?q={query}&c=apps
+ url_xpath : //a[@class="title"]/@href
+ title_xpath : //a[@class="title"]
+ content_xpath : //a[@class="subtitle"]
+ categories : files
+ shortcut : gpa
+ disabled : True
+
+ - name : google play movies
+ engine : xpath
+ search_url : https://play.google.com/store/search?q={query}&c=movies
+ url_xpath : //a[@class="title"]/@href
+ title_xpath : //a[@class="title"]/@title
+ content_xpath : //a[contains(@class, "subtitle")]
+ categories : videos
+ shortcut : gpm
+ disabled : True
+
+ - name : google play music
+ engine : xpath
+ search_url : https://play.google.com/store/search?q={query}&c=music
+ url_xpath : //a[@class="title"]/@href
+ title_xpath : //a[@class="title"]
+ content_xpath : //a[@class="subtitle"]
+ categories : music
+ shortcut : gps
+ disabled : True
+
+ - name : geektimes
+ engine : xpath
+ paging : True
+ search_url : https://geektimes.ru/search/page{pageno}/?q={query}
+ url_xpath : //div[@class="search_results"]//a[@class="post__title_link"]/@href
+ title_xpath : //div[@class="search_results"]//a[@class="post__title_link"]
+ content_xpath : //div[@class="search_results"]//div[contains(@class, "content")]
+ categories : it
+ timeout : 4.0
+ disabled : True
+ shortcut : gt
+
+ - name : habrahabr
+ engine : xpath
+ paging : True
+ search_url : https://habrahabr.ru/search/page{pageno}/?q={query}
+ url_xpath : //div[@class="search_results"]//a[contains(@class, "post__title_link")]/@href
+ title_xpath : //div[@class="search_results"]//a[contains(@class, "post__title_link")]
+ content_xpath : //div[@class="search_results"]//div[contains(@class, "content")]
+ categories : it
+ timeout : 4.0
+ disabled : True
+ shortcut : habr
+
+ - name : hoogle
+ engine : json_engine
+ paging : True
+ search_url : https://www.haskell.org/hoogle/?mode=json&hoogle={query}&start={pageno}
+ results_query : results
+ url_query : location
+ title_query : self
+ content_query : docs
+ page_size : 20
+ categories : it
+ shortcut : ho
+
+ - name : ina
+ engine : ina
+ shortcut : in
+ timeout : 6.0
+ disabled : True
+
+ - name: kickass
+ engine : kickass
+ shortcut : kc
+ timeout : 4.0
+ disabled : True
+
+ - name : library genesis
+ engine : xpath
+ search_url : http://libgen.io/search.php?req={query}
+ url_xpath : //a[contains(@href,"bookfi.net")]/@href
+ title_xpath : //a[contains(@href,"book/")]/text()[1]
+ content_xpath : //td/a[1][contains(@href,"=author")]/text()
+ categories : general
+ timeout : 7.0
+ disabled : True
+ shortcut : lg
+
+ - name : lobste.rs
+ engine : xpath
+ search_url : https://lobste.rs/search?utf8=%E2%9C%93&q={query}&what=stories&order=relevance
+ results_xpath : //li[contains(@class, "story")]
+ url_xpath : .//span[@class="link"]/a/@href
+ title_xpath : .//span[@class="link"]/a
+ content_xpath : .//a[@class="domain"]
+ categories : it
+ shortcut : lo
+
+ - name : microsoft academic
+ engine : json_engine
+ paging : True
+ search_url : https://academic.microsoft.com/api/search/GetEntityResults?query=%40{query}%40&filters=&offset={pageno}&limit=8&correlationId=undefined
+ results_query : results
+ url_query : u
+ title_query : dn
+ content_query : d
+ page_size : 8
+ first_page_num : 0
+ categories : science
+ shortcut : ma
+
+ - name : mixcloud
+ engine : mixcloud
+ shortcut : mc
+
+ - name : nyaa
+ engine : nyaa
+ shortcut : nt
+ disabled : True
+
+ - name : openstreetmap
+ engine : openstreetmap
+ shortcut : osm
+
+ - name : openrepos
+ engine : xpath
+ paging : True
+ search_url : https://openrepos.net/search/node/{query}?page={pageno}
+ url_xpath : //li[@class="search-result"]//h3[@class="title"]/a/@href
+ title_xpath : //li[@class="search-result"]//h3[@class="title"]/a
+ content_xpath : //li[@class="search-result"]//div[@class="search-snippet-info"]//p[@class="search-snippet"]
+ categories : files
+ timeout : 4.0
+ disabled : True
+ shortcut : or
+
+ - name : pdbe
+ engine : pdbe
+ shortcut : pdb
+# Hide obsolete PDB entries.
+# Default is not to hide obsolete structures
+# hide_obsolete : False
+
+ - name : photon
+ engine : photon
+ shortcut : ph
+
+ - name : piratebay
+ engine : piratebay
+ shortcut : tpb
+ url: https://pirateproxy.red/
+ timeout : 3.0
+
+ - name : qwant
+ engine : qwant
+ shortcut : qw
+ categories : general
+ disabled : True
+
+ - name : qwant images
+ engine : qwant
+ shortcut : qwi
+ categories : images
+
+ - name : qwant news
+ engine : qwant
+ shortcut : qwn
+ categories : news
+
+ - name : qwant social
+ engine : qwant
+ shortcut : qws
+ categories : social media
+
+ - name : reddit
+ engine : reddit
+ shortcut : re
+ page_size : 25
+ timeout : 10.0
+ disabled : True
+
+ - name : scanr structures
+ shortcut: scs
+ engine : scanr_structures
+ disabled : True
+
+ - name : soundcloud
+ engine : soundcloud
+ shortcut : sc
+
+ - name : stackoverflow
+ engine : stackoverflow
+ shortcut : st
+
+ - name : searchcode doc
+ engine : searchcode_doc
+ shortcut : scd
+
+ - name : searchcode code
+ engine : searchcode_code
+ shortcut : scc
+ disabled : True
+
+ - name : framalibre
+ engine : framalibre
+ shortcut : frl
+ disabled : True
+
+# - name : searx
+# engine : searx_engine
+# shortcut : se
+# instance_urls :
+# - http://127.0.0.1:8888/
+# - ...
+# disabled : True
+
+ - name : semantic scholar
+ engine : xpath
+ paging : True
+ search_url : https://www.semanticscholar.org/search?q={query}&sort=relevance&page={pageno}&ae=false
+ results_xpath : //article
+ url_xpath : .//div[@class="search-result-title"]/a/@href
+ title_xpath : .//div[@class="search-result-title"]/a
+ content_xpath : .//div[@class="search-result-abstract"]
+ shortcut : se
+ categories : science
+
+ - name : spotify
+ engine : spotify
+ shortcut : stf
+
+ - name : subtitleseeker
+ engine : subtitleseeker
+ shortcut : ss
+# The language is an option. You can put any language written in english
+# Examples : English, French, German, Hungarian, Chinese...
+# language : English
+
+ - name : startpage
+ engine : startpage
+ shortcut : sp
+ timeout : 6.0
+ disabled : True
+
+ - name : ixquick
+ engine : startpage
+ base_url : 'https://www.ixquick.eu/'
+ search_url : 'https://www.ixquick.eu/do/search'
+ shortcut : iq
+ timeout : 6.0
+ disabled : True
+
+ - name : swisscows
+ engine : swisscows
+ shortcut : sw
+ disabled : True
+
+ - name : tokyotoshokan
+ engine : tokyotoshokan
+ shortcut : tt
+ timeout : 6.0
+ disabled : True
+
+ - name : twitter
+ engine : twitter
+ shortcut : tw
+
+# maybe in a fun category
+# - name : uncyclopedia
+# engine : mediawiki
+# shortcut : unc
+# base_url : https://uncyclopedia.wikia.com/
+# number_of_results : 5
+
+# tmp suspended - too slow, too many errors
+# - name : urbandictionary
+# engine : xpath
+# search_url : http://www.urbandictionary.com/define.php?term={query}
+# url_xpath : //*[@class="word"]/@href
+# title_xpath : //*[@class="def-header"]
+# content_xpath : //*[@class="meaning"]
+# shortcut : ud
+
+ - name : yahoo
+ engine : yahoo
+ shortcut : yh
+
+ - name : yandex
+ engine : yandex
+ shortcut : yn
+ disabled : True
+
+ - name : yahoo news
+ engine : yahoo_news
+ shortcut : yhn
+
+ - name : youtube
+ shortcut : yt
+ # You can use the engine using the official stable API, but you need an API key
+ # See : https://console.developers.google.com/project
+ # engine : youtube_api
+ # api_key: 'apikey' # required!
+ # Or you can use the html non-stable engine, activated by default
+ engine : youtube_noapi
+
+ - name : dailymotion
+ engine : dailymotion
+ shortcut : dm
+
+ - name : vimeo
+ engine : vimeo
+ shortcut : vm
+
+ - name : wolframalpha
+ shortcut : wa
+ # You can use the engine using the official stable API, but you need an API key
+ # See : http://products.wolframalpha.com/api/
+ # engine : wolframalpha_api
+ # api_key: '' # required!
+ engine : wolframalpha_noapi
+ timeout: 6.0
+ categories : science
+
+ - name : seedpeer
+ engine : seedpeer
+ shortcut: speu
+ categories: files, music, videos
+ disabled: True
+
+ - name : dictzone
+ engine : dictzone
+ shortcut : dc
+
+ - name : mymemory translated
+ engine : translated
+ shortcut : tl
+ timeout : 5.0
+ disabled : True
+ # You can use without an API key, but you are limited to 1000 words/day
+ # See : http://mymemory.translated.net/doc/usagelimits.php
+ # api_key : ''
+
+ - name : voat
+ engine: xpath
+ shortcut: vo
+ categories: social media
+ search_url : https://voat.co/search?q={query}
+ url_xpath : //p[contains(@class, "title")]/a/@href
+ title_xpath : //p[contains(@class, "title")]/a
+ content_xpath : //span[@class="domain"]
+ timeout : 10.0
+ disabled : True
+
+ - name : 1337x
+ engine : 1337x
+ shortcut : 1337x
+ disabled : True
+
+#The blekko technology and team have joined IBM Watson! -> https://blekko.com/
+# - name : blekko images
+# engine : blekko_images
+# locale : en-US
+# shortcut : bli
+
+# - name : yacy
+# engine : yacy
+# shortcut : ya
+# base_url : 'http://localhost:8090'
+# number_of_results : 5
+# timeout : 3.0
+
+# Doku engine lets you access to any Doku wiki instance:
+# A public one or a privete/corporate one.
+# - name : ubuntuwiki
+# engine : doku
+# shortcut : uw
+# base_url : 'http://doc.ubuntu-fr.org'
+
+locales:
+ en : English
+ bg : Български (Bulgarian)
+ cs : Čeština (Czech)
+ de : Deutsch (German)
+ de_DE : Deutsch (German_Germany)
+ el_GR : Ελληνικά (Greek_Greece)
+ eo : Esperanto (Esperanto)
+ es : Español (Spanish)
+ fi : Suomi (Finnish)
+ fr : Français (French)
+ he : עברית (Hebrew)
+ hu : Magyar (Hungarian)
+ it : Italiano (Italian)
+ ja : 日本語 (Japanese)
+ nl : Nederlands (Dutch)
+ pt : Português (Portuguese)
+ pt_BR : Português (Portuguese_Brazil)
+ ro : Română (Romanian)
+ ru : Русский (Russian)
+ sk : Slovenčina (Slovak)
+ sv : Svenska (Swedish)
+ tr : Türkçe (Turkish)
+ uk : українська мова (Ukrainian)
+ zh : 中文 (Chinese)
diff --git a/searx/settings_robot.yml b/searx/settings_robot.yml
new file mode 100644
index 0000000..070a0ed
--- /dev/null
+++ b/searx/settings_robot.yml
@@ -0,0 +1,41 @@
+general:
+ debug : False
+ instance_name : "searx_test"
+
+search:
+ safe_search : 0
+ autocomplete : ""
+ language: "all"
+
+server:
+ port : 11111
+ bind_address : 127.0.0.1
+ secret_key : "ultrasecretkey" # change this!
+ base_url : False
+ image_proxy : False
+ http_protocol_version : "1.0"
+
+ui:
+ static_path : ""
+ templates_path : ""
+ default_theme : oscar
+ default_locale : ""
+
+outgoing:
+ request_timeout : 1.0 # seconds
+ useragent_suffix : ""
+
+engines:
+ - name : general dummy
+ engine : dummy
+ categories : general
+ shortcut : gd
+
+ - name : dummy dummy
+ engine : dummy
+ categories : dummy
+ shortcut : dd
+
+locales:
+ en : English
+ hu : Magyar
diff --git a/searx/static/plugins/css/infinite_scroll.css b/searx/static/plugins/css/infinite_scroll.css
new file mode 100644
index 0000000..7e0ee20
--- /dev/null
+++ b/searx/static/plugins/css/infinite_scroll.css
@@ -0,0 +1,16 @@
+@keyframes rotate-forever {
+ 0% { transform: rotate(0deg) }
+ 100% { transform: rotate(360deg) }
+}
+.loading-spinner {
+ animation-duration: 0.75s;
+ animation-iteration-count: infinite;
+ animation-name: rotate-forever;
+ animation-timing-function: linear;
+ height: 30px;
+ width: 30px;
+ border: 8px solid #666;
+ border-right-color: transparent;
+ border-radius: 50% !important;
+ margin: 0 auto;
+}
diff --git a/searx/static/plugins/css/vim_hotkeys.css b/searx/static/plugins/css/vim_hotkeys.css
new file mode 100644
index 0000000..2ccfdc1
--- /dev/null
+++ b/searx/static/plugins/css/vim_hotkeys.css
@@ -0,0 +1,26 @@
+.vim-hotkeys-help {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 9999999;
+ overflow-y: auto;
+ max-height: 80%;
+ box-shadow: 0 0 1em;
+}
+
+.dflex {
+ display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
+ display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
+ display: -ms-flexbox; /* TWEENER - IE 10 */
+ display: -webkit-flex; /* NEW - Chrome */
+ display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
+}
+
+.iflex {
+ -webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */
+ -moz-box-flex: 1; /* OLD - Firefox 19- */
+ -webkit-flex: 1; /* Chrome */
+ -ms-flex: 1; /* IE 10 */
+ flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
+}
diff --git a/searx/static/plugins/js/infinite_scroll.js b/searx/static/plugins/js/infinite_scroll.js
new file mode 100644
index 0000000..9cd582d
--- /dev/null
+++ b/searx/static/plugins/js/infinite_scroll.js
@@ -0,0 +1,18 @@
+$(document).ready(function() {
+ var win = $(window);
+ win.scroll(function() {
+ if ($(document).height() - win.height() == win.scrollTop()) {
+ var formData = $('#pagination form:last').serialize();
+ if (formData) {
+ $('#pagination').html('<div class="loading-spinner"></div>');
+ $.post('./', formData, function (data) {
+ var body = $(data);
+ $('#pagination').remove();
+ $('#main_results').append('<hr/>');
+ $('#main_results').append(body.find('.result'));
+ $('#main_results').append(body.find('#pagination'));
+ });
+ }
+ }
+ });
+});
diff --git a/searx/static/plugins/js/open_results_on_new_tab.js b/searx/static/plugins/js/open_results_on_new_tab.js
new file mode 100644
index 0000000..99ef382
--- /dev/null
+++ b/searx/static/plugins/js/open_results_on_new_tab.js
@@ -0,0 +1,3 @@
+$(document).ready(function() {
+ $('.result_header > a').attr('target', '_blank');
+});
diff --git a/searx/static/plugins/js/search_on_category_select.js b/searx/static/plugins/js/search_on_category_select.js
new file mode 100644
index 0000000..a76fd12
--- /dev/null
+++ b/searx/static/plugins/js/search_on_category_select.js
@@ -0,0 +1,24 @@
+$(document).ready(function() {
+ if($('#q').length) {
+ $('#categories label').click(function(e) {
+ $('#categories input[type="checkbox"]').each(function(i, checkbox) {
+ $(checkbox).prop('checked', false);
+ });
+ $(document.getElementById($(this).attr("for"))).prop('checked', true);
+ if($('#q').val()) {
+ $('#search_form').submit();
+ }
+ return false;
+ });
+ $('#time-range > option').click(function(e) {
+ if($('#q').val()) {
+ $('#search_form').submit();
+ }
+ });
+ $('#language').change(function(e) {
+ if($('#q').val()) {
+ $('#search_form').submit();
+ }
+ });
+ }
+});
diff --git a/searx/static/plugins/js/vim_hotkeys.js b/searx/static/plugins/js/vim_hotkeys.js
new file mode 100644
index 0000000..61500d8
--- /dev/null
+++ b/searx/static/plugins/js/vim_hotkeys.js
@@ -0,0 +1,336 @@
+$(document).ready(function() {
+ highlightResult('top')();
+
+ $('.result').on('click', function() {
+ highlightResult($(this))();
+ });
+
+ var vimKeys = {
+ 27: {
+ key: 'Escape',
+ fun: removeFocus,
+ des: 'remove focus from the focused input',
+ cat: 'Control'
+ },
+ 73: {
+ key: 'i',
+ fun: searchInputFocus,
+ des: 'focus on the search input',
+ cat: 'Control'
+ },
+ 66: {
+ key: 'b',
+ fun: scrollPage(-window.innerHeight),
+ des: 'scroll one page up',
+ cat: 'Navigation'
+ },
+ 70: {
+ key: 'f',
+ fun: scrollPage(window.innerHeight),
+ des: 'scroll one page down',
+ cat: 'Navigation'
+ },
+ 85: {
+ key: 'u',
+ fun: scrollPage(-window.innerHeight / 2),
+ des: 'scroll half a page up',
+ cat: 'Navigation'
+ },
+ 68: {
+ key: 'd',
+ fun: scrollPage(window.innerHeight / 2),
+ des: 'scroll half a page down',
+ cat: 'Navigation'
+ },
+ 71: {
+ key: 'g',
+ fun: scrollPageTo(-document.body.scrollHeight, 'top'),
+ des: 'scroll to the top of the page',
+ cat: 'Navigation'
+ },
+ 86: {
+ key: 'v',
+ fun: scrollPageTo(document.body.scrollHeight, 'bottom'),
+ des: 'scroll to the bottom of the page',
+ cat: 'Navigation'
+ },
+ 75: {
+ key: 'k',
+ fun: highlightResult('up'),
+ des: 'select previous search result',
+ cat: 'Results'
+ },
+ 74: {
+ key: 'j',
+ fun: highlightResult('down'),
+ des: 'select next search result',
+ cat: 'Results'
+ },
+ 80: {
+ key: 'p',
+ fun: pageButtonClick(0),
+ des: 'go to previous page',
+ cat: 'Results'
+ },
+ 78: {
+ key: 'n',
+ fun: pageButtonClick(1),
+ des: 'go to next page',
+ cat: 'Results'
+ },
+ 79: {
+ key: 'o',
+ fun: openResult(false),
+ des: 'open search result',
+ cat: 'Results'
+ },
+ 84: {
+ key: 't',
+ fun: openResult(true),
+ des: 'open the result in a new tab',
+ cat: 'Results'
+ },
+ 82: {
+ key: 'r',
+ fun: reloadPage,
+ des: 'reload page from the server',
+ cat: 'Control'
+ },
+ 72: {
+ key: 'h',
+ fun: toggleHelp,
+ des: 'toggle help window',
+ cat: 'Other'
+ }
+ };
+
+ $(document).keyup(function(e) {
+ // check for modifiers so we don't break browser's hotkeys
+ if (vimKeys.hasOwnProperty(e.keyCode)
+ && !e.ctrlKey
+ && !e.altKey
+ && !e.shiftKey
+ && !e.metaKey)
+ {
+ if (e.keyCode === 27) {
+ if (e.target.tagName.toLowerCase() === 'input') {
+ vimKeys[e.keyCode].fun();
+ }
+ } else {
+ if (e.target === document.body) {
+ vimKeys[e.keyCode].fun();
+ }
+ }
+ }
+ });
+
+ function highlightResult(which) {
+ return function() {
+ var current = $('.result[data-vim-selected]');
+ if (current.length === 0) {
+ current = $('.result:first');
+ if (current.length === 0) {
+ return;
+ }
+ }
+
+ var next;
+
+ if (typeof which !== 'string') {
+ next = which;
+ } else {
+ switch (which) {
+ case 'visible':
+ var top = $(window).scrollTop();
+ var bot = top + $(window).height();
+ var results = $('.result');
+
+ for (var i = 0; i < results.length; i++) {
+ next = $(results[i]);
+ var etop = next.offset().top;
+ var ebot = etop + next.height();
+
+ if ((ebot <= bot) && (etop > top)) {
+ break;
+ }
+ }
+ break;
+ case 'down':
+ next = current.next('.result');
+ if (next.length === 0) {
+ next = $('.result:first');
+ }
+ break;
+ case 'up':
+ next = current.prev('.result');
+ if (next.length === 0) {
+ next = $('.result:last');
+ }
+ break;
+ case 'bottom':
+ next = $('.result:last');
+ break;
+ case 'top':
+ default:
+ next = $('.result:first');
+ }
+ }
+
+ if (next) {
+ current.removeAttr('data-vim-selected').removeClass('well well-sm');
+ next.attr('data-vim-selected', 'true').addClass('well well-sm');
+ scrollPageToSelected();
+ }
+ }
+ }
+
+ function reloadPage() {
+ document.location.reload(false);
+ }
+
+ function removeFocus() {
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+ }
+
+ function pageButtonClick(num) {
+ return function() {
+ var buttons = $('div#pagination button[type="submit"]');
+ if (buttons.length !== 2) {
+ console.log('page navigation with this theme is not supported');
+ return;
+ }
+ if (num >= 0 && num < buttons.length) {
+ buttons[num].click();
+ } else {
+ console.log('pageButtonClick(): invalid argument');
+ }
+ }
+ }
+
+ function scrollPageToSelected() {
+ var sel = $('.result[data-vim-selected]');
+ if (sel.length !== 1) {
+ return;
+ }
+
+ var wnd = $(window);
+
+ var wtop = wnd.scrollTop();
+ var etop = sel.offset().top;
+
+ var offset = 30;
+
+ if (wtop > etop) {
+ wnd.scrollTop(etop - offset);
+ } else {
+ var ebot = etop + sel.height();
+ var wbot = wtop + wnd.height();
+
+ if (wbot < ebot) {
+ wnd.scrollTop(ebot - wnd.height() + offset);
+ }
+ }
+ }
+
+ function scrollPage(amount) {
+ return function() {
+ window.scrollBy(0, amount);
+ highlightResult('visible')();
+ }
+ }
+
+ function scrollPageTo(position, nav) {
+ return function() {
+ window.scrollTo(0, position);
+ highlightResult(nav)();
+ }
+ }
+
+ function searchInputFocus() {
+ $('input#q').focus();
+ }
+
+ function openResult(newTab) {
+ return function() {
+ var link = $('.result[data-vim-selected] .result_header a');
+ if (link.length) {
+ var url = link.attr('href');
+ if (newTab) {
+ window.open(url);
+ } else {
+ window.location.href = url;
+ }
+ }
+ };
+ }
+
+ function toggleHelp() {
+ var helpPanel = $('#vim-hotkeys-help');
+ if (helpPanel.length) {
+ helpPanel.toggleClass('hidden');
+ return;
+ }
+
+ var categories = {};
+
+ for (var k in vimKeys) {
+ var key = vimKeys[k];
+ categories[key.cat] = categories[key.cat] || [];
+ categories[key.cat].push(key);
+ }
+
+ var sorted = Object.keys(categories).sort(function(a, b) {
+ return categories[b].length - categories[a].length;
+ });
+
+ if (sorted.length === 0) {
+ return;
+ }
+
+ var html = '<div id="vim-hotkeys-help" class="well vim-hotkeys-help">';
+ html += '<div class="container-fluid">';
+
+ html += '<div class="row">';
+ html += '<div class="col-sm-12">';
+ html += '<h3>How to navigate searx with Vim-like hotkeys</h3>';
+ html += '</div>'; // col-sm-12
+ html += '</div>'; // row
+
+ for (var i = 0; i < sorted.length; i++) {
+ var cat = categories[sorted[i]];
+
+ var lastCategory = i === (sorted.length - 1);
+ var first = i % 2 === 0;
+
+ if (first) {
+ html += '<div class="row dflex">';
+ }
+ html += '<div class="col-sm-' + (first && lastCategory ? 12 : 6) + ' dflex">';
+
+ html += '<div class="panel panel-default iflex">';
+ html += '<div class="panel-heading">' + cat[0].cat + '</div>';
+ html += '<div class="panel-body">';
+ html += '<ul class="list-unstyled">';
+
+ for (var cj in cat) {
+ html += '<li><kbd>' + cat[cj].key + '</kbd> ' + cat[cj].des + '</li>';
+ }
+
+ html += '</ul>';
+ html += '</div>'; // panel-body
+ html += '</div>'; // panel
+ html += '</div>'; // col-sm-*
+
+ if (!first || lastCategory) {
+ html += '</div>'; // row
+ }
+ }
+
+ html += '</div>'; // container-fluid
+ html += '</div>'; // vim-hotkeys-help
+
+ $('body').append(html);
+ }
+});
diff --git a/searx/static/themes/courgette/img/favicon.png b/searx/static/themes/courgette/img/favicon.png
new file mode 100644
index 0000000..3818d3d
--- /dev/null
+++ b/searx/static/themes/courgette/img/favicon.png
Binary files differ
diff --git a/searx/static/themes/courgette/img/preference-icon.png b/searx/static/themes/courgette/img/preference-icon.png
new file mode 100644
index 0000000..57e991c
--- /dev/null
+++ b/searx/static/themes/courgette/img/preference-icon.png
Binary files differ
diff --git a/searx/static/themes/courgette/img/search-icon.png b/searx/static/themes/courgette/img/search-icon.png
new file mode 100644
index 0000000..9bc7a22
--- /dev/null
+++ b/searx/static/themes/courgette/img/search-icon.png
Binary files differ
diff --git a/searx/static/themes/courgette/img/searx-mobile.png b/searx/static/themes/courgette/img/searx-mobile.png
new file mode 100644
index 0000000..31dd7d1
--- /dev/null
+++ b/searx/static/themes/courgette/img/searx-mobile.png
Binary files differ
diff --git a/searx/static/themes/courgette/img/searx.png b/searx/static/themes/courgette/img/searx.png
new file mode 100644
index 0000000..68c2e4f
--- /dev/null
+++ b/searx/static/themes/courgette/img/searx.png
Binary files differ
diff --git a/searx/static/themes/courgette/img/searx_logo.svg b/searx/static/themes/courgette/img/searx_logo.svg
new file mode 100644
index 0000000..67a2d45
--- /dev/null
+++ b/searx/static/themes/courgette/img/searx_logo.svg
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="searx_logo.svg"
+ inkscape:export-filename="/home/a/magnif.png"
+ inkscape:export-xdpi="203.1774"
+ inkscape:export-ydpi="203.1774">
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3857">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3859" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3861" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3790">
+ <stop
+ style="stop-color:#a9a9a9;stop-opacity:1;"
+ offset="0"
+ id="stop3792" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="1"
+ id="stop3794" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3790"
+ id="radialGradient3798"
+ cx="294.45947"
+ cy="208.37973"
+ fx="294.45947"
+ fy="208.37973"
+ r="107.58125"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3857"
+ id="linearGradient3865"
+ x1="120.68947"
+ y1="239.61774"
+ x2="120.68947"
+ y2="602.17517"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3790"
+ id="linearGradient3912"
+ x1="186.74416"
+ y1="354.42426"
+ x2="255.84358"
+ y2="254.35953"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.2227304,0,0,0.89945099,-289.31433,113.40259)" />
+ <filter
+ inkscape:collect="always"
+ id="filter4024"
+ x="-0.12996517"
+ width="1.2599303"
+ y="-0.14709377"
+ height="1.2941875">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="6.4759344"
+ id="feGaussianBlur4026" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3983"
+ x="-1.0608404"
+ width="3.1216809"
+ y="-0.31017202"
+ height="1.620344">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="9.392858"
+ id="feGaussianBlur3985" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="30.708726"
+ inkscape:cy="948.08556"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1364"
+ inkscape:window-height="663"
+ inkscape:window-x="0"
+ inkscape:window-y="30"
+ inkscape:window-maximized="0"
+ showguides="true"
+ inkscape:guide-bbox="true" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 70.523181,34.870671 c -7.11959,15.242893 -10.17798,31.779192 -8.22563,48.814566 5.01677,43.774133 41.675309,79.324503 91.536109,95.162893 -6.62576,-22.40752 -5.34093,-44.9362 2.6395,-65.84431 C 108.73618,98.821131 74.828141,70.195435 70.523181,34.870671 z"
+ id="path3814-0-7"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 303.77876,36.21406 c 7.11959,15.242893 10.17798,31.779192 8.22563,48.814566 -5.01677,43.774134 -41.67531,79.324504 -91.53611,95.162894 6.62576,-22.40752 5.34093,-44.9362 -2.6395,-65.84431 47.73698,-14.18269 81.64502,-42.808386 85.94998,-78.13315 z"
+ id="path3814-0"
+ inkscape:connector-curvature="0" />
+ <path
+ transform="matrix(0.6556593,-0.75505688,0.75505688,0.6556593,0,0)"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m -5.0905523,259.06055 18.4167573,0 c 6.220455,0 11.228257,16.68196 11.228257,37.40349 l 0,172.83701 c 0,20.72153 -5.007802,37.40349 -11.228257,37.40349 l -18.4167573,0 c -6.2204547,0 -11.2282577,-16.68196 -11.2282577,-37.40349 l 0,-172.83701 c 0,-20.72153 5.007803,-37.40349 11.2282577,-37.40349 z"
+ id="rect3804" />
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#radialGradient3798);fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path2987"
+ sodipodi:cx="294.45947"
+ sodipodi:cy="208.37973"
+ sodipodi:rx="107.58125"
+ sodipodi:ry="107.58125"
+ d="m 402.04073,208.37973 a 107.58125,107.58125 0 1 1 -215.16251,0 107.58125,107.58125 0 1 1 215.16251,0 z"
+ transform="translate(-107.07617,-60.609153)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#linearGradient3865);fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path3757"
+ sodipodi:cx="131.82491"
+ sodipodi:cy="299.29346"
+ sodipodi:rx="101.52033"
+ sodipodi:ry="101.52033"
+ d="m 233.34524,299.29346 a 101.52033,101.52033 0 1 1 -203.040667,0 101.52033,101.52033 0 1 1 203.040667,0 z"
+ transform="matrix(0.76865672,0,0,0.76865672,85.80266,-82.535889)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path3800"
+ sodipodi:cx="183.34268"
+ sodipodi:cy="156.35687"
+ sodipodi:rx="27.274118"
+ sodipodi:ry="27.274118"
+ d="m 210.6168,156.35687 a 27.274118,27.274118 0 1 1 -54.54824,0 27.274118,27.274118 0 1 1 54.54824,0 z"
+ transform="translate(5,-7.1428572)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path3802"
+ sodipodi:cx="197.9899"
+ sodipodi:cy="203.32896"
+ sodipodi:rx="5.5558391"
+ sodipodi:ry="5.5558391"
+ d="m 203.54574,203.32896 a 5.5558391,5.5558391 0 1 1 -11.11168,0 5.5558391,5.5558391 0 1 1 11.11168,0 z"
+ transform="translate(1.4847712,-63.564549)" />
+ <rect
+ style="fill:#ffffff;fill-opacity:0.82211531000000004;fill-rule:nonzero;stroke:none;filter:url(#filter4024)"
+ id="rect3916"
+ width="2.2392972"
+ height="159.43797"
+ x="19.525793"
+ y="337.8396"
+ rx="2.8666623"
+ ry="9.0007057"
+ transform="matrix(0.74466525,-0.84318084,0.84318084,0.74466525,-35.543204,-26.349917)" />
+ </g>
+</svg>
diff --git a/searx/static/themes/courgette/js/searx.js b/searx/static/themes/courgette/js/searx.js
new file mode 100644
index 0000000..92a25e3
--- /dev/null
+++ b/searx/static/themes/courgette/js/searx.js
@@ -0,0 +1,45 @@
+if(searx.autocompleter) {
+ window.addEvent('domready', function() {
+ new Autocompleter.Request.JSON('q', './autocompleter', {
+ postVar:'q',
+ postData:{
+ 'format': 'json'
+ },
+ ajaxOptions:{
+ timeout: 5 // Correct option?
+ },
+ 'minLength': 4,
+ // 'selectMode': 'type-ahead',
+ cache: true,
+ delay: 300
+ });
+ });
+}
+
+(function (w, d) {
+ 'use strict';
+ function addListener(el, type, fn) {
+ if (el.addEventListener) {
+ el.addEventListener(type, fn, false);
+ } else {
+ el.attachEvent('on' + type, fn);
+ }
+ }
+
+ function placeCursorAtEnd() {
+ if (this.setSelectionRange) {
+ var len = this.value.length * 2;
+ this.setSelectionRange(len, len);
+ }
+ }
+
+ addListener(w, 'load', function () {
+ var qinput = d.getElementById('q');
+ if (qinput !== null && qinput.value === "") {
+ addListener(qinput, 'focus', placeCursorAtEnd);
+ qinput.focus();
+ }
+ });
+
+})(window, document);
+
diff --git a/searx/static/themes/courgette/less/style-rtl.less b/searx/static/themes/courgette/less/style-rtl.less
new file mode 100644
index 0000000..3e357d3
--- /dev/null
+++ b/searx/static/themes/courgette/less/style-rtl.less
@@ -0,0 +1,42 @@
+.q {
+ padding: 0.5em 1em 0.5em 3em;
+}
+
+#search_submit {
+ left: 0;
+ right:auto;
+}
+
+.result .favicon {
+ float: right;
+ margin-left: 0.5em;
+ margin-right: 0;
+}
+
+#sidebar {
+ right: auto;
+ left: 0;
+}
+
+#results {
+ padding: 0px 32px 0px 272px;
+}
+
+.search.center {
+ padding-right: 0;
+ padding-left: 17em;
+}
+
+.right {
+ right: auto;
+ left: 0;
+}
+
+#pagination form + form {
+ float: left;
+ margin-top: -2em;
+}
+
+.engine-table {
+ text-align:right;
+} \ No newline at end of file
diff --git a/searx/static/themes/courgette/less/style.less b/searx/static/themes/courgette/less/style.less
new file mode 100644
index 0000000..0387af5
--- /dev/null
+++ b/searx/static/themes/courgette/less/style.less
@@ -0,0 +1,691 @@
+
+@color-main: #3498DB;
+@color-focus: #0665A2;
+@color-other-links: #666;
+@color-fonts: #333;
+@center-width: 70em;
+
+
+
+
+* {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+
+input[type="search"] {
+ -webkit-appearance: textfield;
+}
+
+h2 {
+ color: @color-other-links;
+ text-transform: uppercase;
+}
+
+body {
+ font-family: sans-serif;
+ line-height: 1.5;
+ margin: 0;
+ background: #EEE;
+}
+
+html {
+ position: relative;
+ min-height: 100%;
+}
+
+a {
+ color: @color-other-links;
+}
+
+.title h1 {
+ font-size:7em;
+ color:@color-main;
+ margin:0 auto;
+ line-height:100px;
+ margin-top:-20px;
+ padding-bottom:20px;
+}
+
+.center {
+ max-width: @center-width;
+ text-align: center;
+ background: rgba(255,255,255,0.6);
+ padding: 2em;
+ margin: 7% auto 0;
+ position: relative;
+}
+
+.center.search {
+ position: static;
+ width: auto;
+ background: none;
+ margin: auto;
+ padding-top: 1.8em;
+}
+
+@media screen and (min-width: 1001px) {
+ .center:after {
+ content: "";
+ z-index: -1;
+ background: url(../img/bg-body-index.jpg) no-repeat;
+ background-size: cover;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ position: fixed;
+ }
+ .center.search:after {
+ content: none;
+ }
+}
+
+.autocompleter-choices {
+ position: absolute;
+ margin: 0;
+ padding: 0;
+ background: #FFF;
+}
+
+.autocompleter-choices li {
+ padding: 0.5em 1em;
+}
+
+.autocompleter-choices li:hover {
+ background: @color-main;
+ color: #FFF;
+ cursor: pointer;
+}
+
+#categories {
+ text-align: center;
+}
+
+.top_margin {
+ position: absolute;
+ bottom: -3.5em;
+ width: 100%;
+ left: 0;
+}
+
+.top_margin a {
+ display: inline-block;
+ margin-right: 1em;
+ color: #FFF;
+ text-decoration: none;
+}
+
+.top_margin a:hover,
+.top_margin a:focus {
+ text-decoration: underline;
+}
+
+@media screen and (max-width: 1000px) {
+ .center { background: none; }
+ .top_margin a {
+ color: @color-fonts;
+ }
+}
+
+.checkbox_container { margin-top: 1.5em; }
+.checkbox_container label {
+ padding: 0.5em 1em;
+ color: @color-fonts;
+ cursor: pointer;
+ font-size: 0.9em;
+}
+
+.checkbox_container label:hover {
+ background: @color-main;
+ color: #FFF;
+}
+
+.checkbox_container input[type="checkbox"] {
+ position: absolute;
+ top: -9999px;
+}
+
+.checkbox_container input[type="checkbox"]:checked + label {
+ background: @color-main;
+ color: #FFF;
+}
+
+#categories_container > div {
+ display: inline-block;
+}
+
+#categories .hidden {
+ display: none;
+ position: absolute;
+ bottom: 1em;
+ left: 0;
+ text-align: center;
+ width: 100%;
+ font-size: 0.9em;
+ font-style: italic;
+ color: @color-fonts;
+}
+
+#categories:hover .hidden {
+ display: block;
+}
+
+@media screen and (max-width: 900px) {
+ #categories_container { letter-spacing: -5px; }
+ #categories_container > div {
+ letter-spacing: normal;
+ margin-top: 1em;
+ }
+ .checkbox_container {
+ margin: 0;
+ }
+ .checkbox_container label {
+ display: block;
+ background: #CCC;
+ padding: 1em;
+ border: 1px solid #FFF;
+ }
+ .top_margin { position: static; }
+ #categories .hidden {
+ position: static;
+ display: block;
+ }
+}
+
+@media screen and (max-width: 900px) and (min-width: 501px) {
+ #categories_container > div {
+ width: 31%;
+ margin-left: 2.333%;
+ }
+ #categories_container > div:nth-child(3n+1) { margin-left: 0; }
+}
+
+@media screen and (max-width: 500px) {
+ #categories_container > div {
+ width: 48%;
+ margin-left: 2%;
+ font-size: 0.9em;
+ }
+ #categories_container > div:nth-child(2n+1) { margin-left: 0; }
+ .title h1 {
+ background: url(../img/searx-mobile.png) no-repeat;
+ width: 200px;
+ height: 39px;
+ }
+}
+
+#search_wrapper {
+ position: relative;
+}
+
+.q {
+ padding: 0.5em 3em 0.5em 1em;
+ width: 100%;
+ font-size: 1.5em;
+ border: 0;
+ color: #666;
+}
+
+#search_submit {
+ position: absolute;
+ top: 0;
+ right: 0;
+ border: 0;
+ background:url("../img/search-icon.png") no-repeat scroll center center / 65% auto @color-main;
+ text-indent: -9999px;
+ width: 5em;
+ height: 100%;
+ cursor: pointer;
+}
+
+#search_submit:hover,
+#search_submit:focus {
+ background-color: @color-focus;
+}
+
+#sidebar {
+ background: @color-main;
+ position: fixed;
+ top: 0;
+ right: 0;
+ width: 15em;
+ height: 100%;
+ padding: 1.5em;
+ text-align: right;
+}
+
+.right {
+ position: fixed;
+ bottom: 1.5em;
+ width: 15em;
+ right: 0;
+ z-index: 1;
+ padding: 0 1.5em;
+ text-align: right;
+}
+
+.right a {
+ color: #FFF;
+ display: block;
+ text-decoration: none;
+}
+
+.right a:hover,
+.right a:focus {
+ text-decoration: underline;
+}
+
+#preferences {
+ background: url("../img/preference-icon.png") no-repeat right center / 12% auto;
+ padding-right: 1.8em;
+}
+
+#preferences:hover,
+#preferences:focus {
+
+}
+
+#search_url input {
+ border: 0;
+ padding: 0.5em;
+}
+
+#sidebar > div {
+ margin-bottom: 1em;
+ color: #FFF;
+}
+
+#sidebar form {
+ display: inline-block;
+}
+
+#sidebar input[type="submit"] {
+ background: #CCC;
+ border: 0;
+ padding: 0.5em 1em;
+ cursor: pointer;
+ margin-top: 0.5em;
+}
+
+#sidebar input[type="submit"]:hover,
+#sidebar input[type="submit"]:focus {
+ color: #FFF;
+ background-color: @color-focus;
+}
+
+#results {
+ padding-right: 17em;
+ padding-left: 2em;
+ padding: 0 17em 0 2em;
+}
+
+.result p {
+ font-size: 0.9em;
+}
+
+.result .content {
+ margin: 0;
+ color: #666;
+}
+
+.result .url {
+ margin-top: 0;
+ color: #FF6530;
+}
+
+.result .favicon {
+ float: left;
+ position: relative;
+ top: 0.5em;
+ margin-right: 0.5em;
+}
+
+.definition_result {
+ background: #CCC;
+ padding: 1em;
+}
+
+.definition_result .result_title,
+.definition_result p {
+ margin: 0;
+}
+
+.result_title {
+ margin-bottom: 0;
+ font-weight: normal;
+}
+
+.highlight {
+ font-weight: bold;
+}
+
+.result_title a {
+ color: @color-main;
+ text-decoration: none;
+}
+
+.result_title a:hover,
+.result_title a:focus {
+ text-decoration: underline;
+}
+
+.cache_link {
+ color: #666;
+ font-size: 0.9em;
+ font-style: italic;
+}
+
+.search.center {
+ padding-right: 17em;
+}
+
+#answers {
+ border: 2px solid @color-main;
+ padding: 20px;
+ color:#666;
+ text-align: center;
+ max-width:@center-width;
+ margin:0 auto 20px;
+}
+
+#suggestions { margin-bottom: 1em; }
+
+#suggestions span { color: #666; }
+
+#suggestions form {
+ display: inline-block;
+ vertical-align: top;
+ margin-bottom: 0.5em;
+}
+
+#suggestions input[type="submit"] {
+ color: @color-fonts;
+ padding: 0.5em 1em;
+ border: 0;
+ background: #CCC;
+ cursor:pointer;
+}
+
+#suggestions input[type="submit"]:hover,
+#suggestions input[type="submit"]:focus {
+ background: @color-main;
+ color: #FFF;
+}
+
+#pagination {
+ margin: 1.5em 0 2em;
+}
+
+#pagination form + form {
+ float: right;
+ margin-top: -2em;
+}
+
+input[type="submit"] {
+ display: inline-block;
+ background: @color-main;
+ color: #FFF;
+ border: 0;
+ padding: 0.6em 1em;
+ cursor: pointer;
+}
+
+input[type="submit"]:hover,
+input[type="submit"]:focus {
+ background: @color-focus;
+}
+
+.row {
+ max-width: 60em;
+ margin: auto;
+}
+
+.row a {
+ color: @color-main;
+}
+
+.row form {
+ letter-spacing: -5px;
+}
+
+.row form > * { letter-spacing: normal; }
+
+.row p { margin: 0; }
+
+.row fieldset {
+ display: inline-block;
+ width: 48%;
+ vertical-align: top;
+}
+
+.row fieldset:last-of-type {
+ display: block;
+ width: auto;
+ background: none;
+ padding: 0;
+}
+
+.row fieldset:nth-child(odd) {
+ margin-right: 2%;
+}
+
+.row fieldset:nth-child(2) {
+ min-height: 10.5em;
+}
+
+@media screen and (max-width: 900px) {
+ .row {
+ margin: 0 1em;
+ }
+
+ .row fieldset { width: 49%; }
+ .row fieldset,
+ .row fieldset:nth-child(odd) {
+ margin-right: 0;
+ }
+
+ .row fieldset:first-child {
+ width: 100%;
+ margin-right: 0;
+ }
+
+ .row fieldset:nth-child(even) {
+ margin-right: 2%;
+ }
+}
+
+@media screen and (max-width: 800px) {
+ .row fieldset { width: 100%; }
+
+ select { width: 100%; }
+
+ table { font-size: 0.8em; }
+ .right {display: none;}
+ #sidebar { display: none; }
+ #results { padding: 0 2em; }
+ .search.center {
+ padding-right: 2em;
+ }
+}
+
+@media screen and (max-width: 400px) {
+ .row #categories_container > div {
+ width: 100%;
+ margin-left: 0;
+ }
+}
+
+fieldset {
+ border: 0;
+ margin: 1em 0;
+ background: #CCC;
+ padding: 1.5em;
+}
+
+table {
+ width: 100%;
+ text-align: left;
+ border: 1px solid #CCC;
+ border-collapse: collapse;
+}
+
+table th {
+ background: #999;
+ color: #FFF;
+}
+
+table tr:nth-child(odd) {
+ background: #CCC;
+}
+
+table th,
+table td {
+ padding: 0.5em 1em;
+ border: 1px solid #FFF;
+}
+
+.engine_checkbox label {
+ padding: 0.5em;
+ background: @color-main;
+ color: #FFF;
+ cursor: pointer;
+}
+
+.engine_checkbox .deny {
+ background: @color-main;
+}
+
+.engine_checkbox .allow {
+ display: none;
+ background: #666;
+}
+
+.engine_checkbox input {
+ display: none;
+}
+
+.engine_checkbox input:checked + .allow {
+ display: inline;
+}
+
+.engine_checkbox input:checked + .allow + .deny{
+ display: none;
+}
+
+.row input[type="submit"] {
+ font-size: 1em;
+ margin: 1em 0 2em;
+}
+
+.row .right {
+ position: static;
+ display: inline-block;
+
+}
+
+.row .right a {
+ color: @color-fonts;
+ width: auto;
+ text-align: left;
+ padding: 0;
+}
+
+.small_font {
+ font-size: 0.8em;
+}
+
+table th {
+ padding: 1em;
+}
+
+legend {
+ background: #EEE;
+ padding: 0 1em;
+ position: relative;
+}
+
+select {
+ border: 1px solid #DDD;
+ padding: 0.5em 0.8em;
+ font-size: 1em;
+}
+
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #f8f8f8; }
+.highlight .c { color: #408080; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #008000; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
+.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #888888 } /* Generic.Output */
+.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0044DD } /* Generic.Traceback */
+.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #008000 } /* Keyword.Pseudo */
+.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #B00040 } /* Keyword.Type */
+.highlight .m { color: #666666 } /* Literal.Number */
+.highlight .s { color: #BA2121 } /* Literal.String */
+.highlight .na { color: #7D9029 } /* Name.Attribute */
+.highlight .nb { color: #008000 } /* Name.Builtin */
+.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+.highlight .no { color: #880000 } /* Name.Constant */
+.highlight .nd { color: #AA22FF } /* Name.Decorator */
+.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #0000FF } /* Name.Function */
+.highlight .nl { color: #A0A000 } /* Name.Label */
+.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #19177C } /* Name.Variable */
+.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #666666 } /* Literal.Number.Float */
+.highlight .mh { color: #666666 } /* Literal.Number.Hex */
+.highlight .mi { color: #666666 } /* Literal.Number.Integer */
+.highlight .mo { color: #666666 } /* Literal.Number.Oct */
+.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
+.highlight .sc { color: #BA2121 } /* Literal.String.Char */
+.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
+.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
+.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.highlight .sx { color: #008000 } /* Literal.String.Other */
+.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
+.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
+.highlight .ss { color: #19177C } /* Literal.String.Symbol */
+.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #19177C } /* Name.Variable.Class */
+.highlight .vg { color: #19177C } /* Name.Variable.Global */
+.highlight .vi { color: #19177C } /* Name.Variable.Instance */
+.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
+
+.highlight pre { overflow: auto; }
+
+.highlight .lineno {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: default;
+}
+
+.highlight .lineno::selection { background: transparent; } /* WebKit/Blink Browsers */
+.highlight .lineno::-moz-selection { background: transparent; } /* Gecko Browsers */
diff --git a/searx/static/themes/legacy/img/favicon.png b/searx/static/themes/legacy/img/favicon.png
new file mode 100644
index 0000000..3818d3d
--- /dev/null
+++ b/searx/static/themes/legacy/img/favicon.png
Binary files differ
diff --git a/searx/static/themes/legacy/img/preference-icon.png b/searx/static/themes/legacy/img/preference-icon.png
new file mode 100644
index 0000000..8bdee64
--- /dev/null
+++ b/searx/static/themes/legacy/img/preference-icon.png
Binary files differ
diff --git a/searx/static/themes/legacy/img/search-icon.png b/searx/static/themes/legacy/img/search-icon.png
new file mode 100644
index 0000000..d70310b
--- /dev/null
+++ b/searx/static/themes/legacy/img/search-icon.png
Binary files differ
diff --git a/searx/static/themes/legacy/img/searx.png b/searx/static/themes/legacy/img/searx.png
new file mode 100644
index 0000000..a98f12a
--- /dev/null
+++ b/searx/static/themes/legacy/img/searx.png
Binary files differ
diff --git a/searx/static/themes/legacy/img/searx_logo.svg b/searx/static/themes/legacy/img/searx_logo.svg
new file mode 100644
index 0000000..67a2d45
--- /dev/null
+++ b/searx/static/themes/legacy/img/searx_logo.svg
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="searx_logo.svg"
+ inkscape:export-filename="/home/a/magnif.png"
+ inkscape:export-xdpi="203.1774"
+ inkscape:export-ydpi="203.1774">
+ <defs
+ id="defs4">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3857">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3859" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3861" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3790">
+ <stop
+ style="stop-color:#a9a9a9;stop-opacity:1;"
+ offset="0"
+ id="stop3792" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="1"
+ id="stop3794" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3790"
+ id="radialGradient3798"
+ cx="294.45947"
+ cy="208.37973"
+ fx="294.45947"
+ fy="208.37973"
+ r="107.58125"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3857"
+ id="linearGradient3865"
+ x1="120.68947"
+ y1="239.61774"
+ x2="120.68947"
+ y2="602.17517"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3790"
+ id="linearGradient3912"
+ x1="186.74416"
+ y1="354.42426"
+ x2="255.84358"
+ y2="254.35953"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.2227304,0,0,0.89945099,-289.31433,113.40259)" />
+ <filter
+ inkscape:collect="always"
+ id="filter4024"
+ x="-0.12996517"
+ width="1.2599303"
+ y="-0.14709377"
+ height="1.2941875">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="6.4759344"
+ id="feGaussianBlur4026" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3983"
+ x="-1.0608404"
+ width="3.1216809"
+ y="-0.31017202"
+ height="1.620344">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="9.392858"
+ id="feGaussianBlur3985" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.979899"
+ inkscape:cx="30.708726"
+ inkscape:cy="948.08556"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1364"
+ inkscape:window-height="663"
+ inkscape:window-x="0"
+ inkscape:window-y="30"
+ inkscape:window-maximized="0"
+ showguides="true"
+ inkscape:guide-bbox="true" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 70.523181,34.870671 c -7.11959,15.242893 -10.17798,31.779192 -8.22563,48.814566 5.01677,43.774133 41.675309,79.324503 91.536109,95.162893 -6.62576,-22.40752 -5.34093,-44.9362 2.6395,-65.84431 C 108.73618,98.821131 74.828141,70.195435 70.523181,34.870671 z"
+ id="path3814-0-7"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m 303.77876,36.21406 c 7.11959,15.242893 10.17798,31.779192 8.22563,48.814566 -5.01677,43.774134 -41.67531,79.324504 -91.53611,95.162894 6.62576,-22.40752 5.34093,-44.9362 -2.6395,-65.84431 47.73698,-14.18269 81.64502,-42.808386 85.94998,-78.13315 z"
+ id="path3814-0"
+ inkscape:connector-curvature="0" />
+ <path
+ transform="matrix(0.6556593,-0.75505688,0.75505688,0.6556593,0,0)"
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ d="m -5.0905523,259.06055 18.4167573,0 c 6.220455,0 11.228257,16.68196 11.228257,37.40349 l 0,172.83701 c 0,20.72153 -5.007802,37.40349 -11.228257,37.40349 l -18.4167573,0 c -6.2204547,0 -11.2282577,-16.68196 -11.2282577,-37.40349 l 0,-172.83701 c 0,-20.72153 5.007803,-37.40349 11.2282577,-37.40349 z"
+ id="rect3804" />
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#radialGradient3798);fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path2987"
+ sodipodi:cx="294.45947"
+ sodipodi:cy="208.37973"
+ sodipodi:rx="107.58125"
+ sodipodi:ry="107.58125"
+ d="m 402.04073,208.37973 a 107.58125,107.58125 0 1 1 -215.16251,0 107.58125,107.58125 0 1 1 215.16251,0 z"
+ transform="translate(-107.07617,-60.609153)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:url(#linearGradient3865);fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path3757"
+ sodipodi:cx="131.82491"
+ sodipodi:cy="299.29346"
+ sodipodi:rx="101.52033"
+ sodipodi:ry="101.52033"
+ d="m 233.34524,299.29346 a 101.52033,101.52033 0 1 1 -203.040667,0 101.52033,101.52033 0 1 1 203.040667,0 z"
+ transform="matrix(0.76865672,0,0,0.76865672,85.80266,-82.535889)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#1a1a1a;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path3800"
+ sodipodi:cx="183.34268"
+ sodipodi:cy="156.35687"
+ sodipodi:rx="27.274118"
+ sodipodi:ry="27.274118"
+ d="m 210.6168,156.35687 a 27.274118,27.274118 0 1 1 -54.54824,0 27.274118,27.274118 0 1 1 54.54824,0 z"
+ transform="translate(5,-7.1428572)" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path3802"
+ sodipodi:cx="197.9899"
+ sodipodi:cy="203.32896"
+ sodipodi:rx="5.5558391"
+ sodipodi:ry="5.5558391"
+ d="m 203.54574,203.32896 a 5.5558391,5.5558391 0 1 1 -11.11168,0 5.5558391,5.5558391 0 1 1 11.11168,0 z"
+ transform="translate(1.4847712,-63.564549)" />
+ <rect
+ style="fill:#ffffff;fill-opacity:0.82211531000000004;fill-rule:nonzero;stroke:none;filter:url(#filter4024)"
+ id="rect3916"
+ width="2.2392972"
+ height="159.43797"
+ x="19.525793"
+ y="337.8396"
+ rx="2.8666623"
+ ry="9.0007057"
+ transform="matrix(0.74466525,-0.84318084,0.84318084,0.74466525,-35.543204,-26.349917)" />
+ </g>
+</svg>
diff --git a/searx/static/themes/legacy/js/searx.js b/searx/static/themes/legacy/js/searx.js
new file mode 100644
index 0000000..d6d5b74
--- /dev/null
+++ b/searx/static/themes/legacy/js/searx.js
@@ -0,0 +1,49 @@
+if(searx.autocompleter) {
+ window.addEvent('domready', function() {
+ new Autocompleter.Request.JSON('q', './autocompleter', {
+ postVar:'q',
+ postData:{
+ 'format': 'json'
+ },
+ ajaxOptions:{
+ timeout: 5 // Correct option?
+ },
+ 'minLength': 4,
+ 'selectMode': false,
+ cache: true,
+ delay: 300
+ });
+ });
+}
+
+(function (w, d) {
+ 'use strict';
+ function addListener(el, type, fn) {
+ if (el.addEventListener) {
+ el.addEventListener(type, fn, false);
+ } else {
+ el.attachEvent('on' + type, fn);
+ }
+ }
+
+ function placeCursorAtEnd() {
+ if (this.setSelectionRange) {
+ var len = this.value.length * 2;
+ this.setSelectionRange(len, len);
+ }
+ }
+
+ addListener(w, 'load', function () {
+ var qinput = d.getElementById('q');
+ if (qinput !== null && qinput.value === "") {
+ addListener(qinput, 'focus', placeCursorAtEnd);
+ qinput.focus();
+ }
+ });
+
+ if (!!('ontouchstart' in window)) {
+ document.getElementsByTagName("html")[0].className += " touch";
+ }
+
+})(window, document);
+
diff --git a/searx/static/themes/legacy/less/autocompleter.less b/searx/static/themes/legacy/less/autocompleter.less
new file mode 100644
index 0000000..db9601a
--- /dev/null
+++ b/searx/static/themes/legacy/less/autocompleter.less
@@ -0,0 +1,61 @@
+/*
+ * searx, A privacy-respecting, hackable metasearch engine
+ */
+
+ul {
+ &.autocompleter-choices {
+ position: absolute;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ border: 1px solid @color-autocompleter-choices-border;
+ border-left-color: @color-autocompleter-choices-border-left-right;
+ border-right-color: @color-autocompleter-choices-border-left-right;
+ border-bottom-color: @color-autocompleter-choices-border-bottom;
+ text-align: left;
+ font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
+ z-index: 50;
+ background-color: @color-autocompleter-choices-background;
+ color: @color-autocompleter-choices-font;
+
+ li {
+ position: relative;
+ margin: -2px 0 0 0;
+ padding: 0.2em 1.5em 0.2em 1em;
+ display: block;
+ float: none !important;
+ cursor: pointer;
+ font-weight: normal;
+ white-space: nowrap;
+ font-size: 1em;
+ line-height: 1.5em;
+
+ &.autocompleter-selected {
+ background-color: @color-autocompleter-selected-background;
+ color: @color-autocompleter-selected-font;
+
+ span.autocompleter-queried {
+ color: @color-autocompleter-selected-queried-font;
+ }
+ }
+ }
+
+ span.autocompleter-queried {
+ display: inline;
+ float: none;
+ font-weight: bold;
+ margin: 0;
+ padding: 0;
+ }
+ }
+}
+
+/*.autocompleter-loading {
+ //background-image: url(images/spinner.gif);
+ background-repeat: no-repeat;
+ background-position: right 50%;
+}*/
+
+/*textarea.autocompleter-loading {
+ background-position: right bottom;
+}*/
diff --git a/searx/static/themes/legacy/less/code.less b/searx/static/themes/legacy/less/code.less
new file mode 100644
index 0000000..a688dd9
--- /dev/null
+++ b/searx/static/themes/legacy/less/code.less
@@ -0,0 +1,83 @@
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #f8f8f8; }
+.highlight .c { color: #408080; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #008000; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
+.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #888888 } /* Generic.Output */
+.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0044DD } /* Generic.Traceback */
+.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #008000 } /* Keyword.Pseudo */
+.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #B00040 } /* Keyword.Type */
+.highlight .m { color: #666666 } /* Literal.Number */
+.highlight .s { color: #BA2121 } /* Literal.String */
+.highlight .na { color: #7D9029 } /* Name.Attribute */
+.highlight .nb { color: #008000 } /* Name.Builtin */
+.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+.highlight .no { color: #880000 } /* Name.Constant */
+.highlight .nd { color: #AA22FF } /* Name.Decorator */
+.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #0000FF } /* Name.Function */
+.highlight .nl { color: #A0A000 } /* Name.Label */
+.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #19177C } /* Name.Variable */
+.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #666666 } /* Literal.Number.Float */
+.highlight .mh { color: #666666 } /* Literal.Number.Hex */
+.highlight .mi { color: #666666 } /* Literal.Number.Integer */
+.highlight .mo { color: #666666 } /* Literal.Number.Oct */
+.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
+.highlight .sc { color: #BA2121 } /* Literal.String.Char */
+.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
+.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
+.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.highlight .sx { color: #008000 } /* Literal.String.Other */
+.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
+.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
+.highlight .ss { color: #19177C } /* Literal.String.Symbol */
+.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #19177C } /* Name.Variable.Class */
+.highlight .vg { color: #19177C } /* Name.Variable.Global */
+.highlight .vi { color: #19177C } /* Name.Variable.Instance */
+.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
+
+.highlight pre {
+ overflow: auto;
+}
+
+.highlight .lineno {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: default;
+
+ &::selection {
+ background: transparent; /* WebKit/Blink Browsers */
+ }
+ &::-moz-selection {
+ background: transparent; /* Gecko Browsers */
+ }
+}
diff --git a/searx/static/themes/legacy/less/definitions.less b/searx/static/themes/legacy/less/definitions.less
new file mode 100644
index 0000000..0ac0cc9
--- /dev/null
+++ b/searx/static/themes/legacy/less/definitions.less
@@ -0,0 +1,119 @@
+/*
+ * searx, A privacy-respecting, hackable metasearch engine
+ *
+ * To change the colors of the site, simple edit this variables
+ */
+
+/// Basic Colors
+
+@color-base: #3498DB;
+@color-base-dark: #2980B9;
+@color-base-light: #ECF0F1;
+@color-highlight: #094089;
+@color-black: #000000;
+
+/// General
+
+@color-font: #444;
+@color-font-light: #888;
+
+@color-red: #C0392B;
+
+@color-url-font: #1a11be;
+@color-url-visited-font: #8E44AD;
+@results-width: 50em;
+
+
+/// Start-Screen
+
+// hmarg
+@color-hmarg-border: @color-base;
+@color-hmarg-font: @color-base;
+@color-hmarg-font-hover: @color-base;
+
+
+/// Search-Input
+
+@color-search-border: @color-base;
+@color-search-background: #FFF;
+@color-search-font: #222;
+
+/// Autocompleter
+
+@color-autocompleter-choices-background: #FFF;
+@color-autocompleter-choices-border: @color-base;
+@color-autocompleter-choices-border-left-right: @color-base;
+@color-autocompleter-choices-border-bottom: @color-base;
+
+@color-autocompleter-choices-font: #444;
+
+/// Answers
+@color-answers-border: @color-base-dark;
+
+// Selected
+@color-autocompleter-selected-background: #444;
+@color-autocompleter-selected-font: #FFF;
+@color-autocompleter-selected-queried-font: #9FCFFF;
+
+/// Categories
+
+@color-categories-item-selected: @color-base;
+@color-categories-item-selected-font: #FFF;
+
+@color-categories-item-border-selected: @color-base-dark;
+@color-categories-item-border-unselected: #E8E7E6;
+@color-categories-item-border-unselected-hover: @color-base;
+
+
+/// Results
+
+@color-suggestions-button-background: @color-base;
+@color-suggestions-button-font: #FFF;
+
+@color-download-button-background: @color-base;
+@color-download-button-font: #FFF;
+
+@color-result-search-background: @color-base-light;
+
+@color-result-definition-border: gray;
+@color-result-torrent-border: lightgray;
+@color-result-top-border: #E8E7E6;
+
+// Link to result
+@color-result-link-font: @color-base-dark;
+@color-result-link-visited-font: @color-url-visited-font;
+
+// Url to result
+@color-result-url-font: @color-red;
+
+// Publish Date
+@color-result-publishdate-font: @color-font-light;
+
+// Images
+@color-result-image-span-background-hover: rgba(0, 0, 0, 0.6);
+@color-result-image-span-font: #FFF;
+
+// Search-URL
+@color-result-search-url-border: #888;
+@color-result-search-url-font: #444;
+
+
+/// Settings
+
+@color-settings-fieldset: @color-base;
+@color-settings-tr-hover: #DDD;
+
+// Labels
+@color-settings-label-allowed-background: #E74C3C;
+@color-settings-label-allowed-font: #FFF;
+
+@color-settings-label-deny-background: #2ECC71;
+@color-settings-label-deny-font: @color-font;
+
+@color-settings-return-background: @color-base;
+@color-settings-return-font: #FFF;
+
+/// Other
+
+@color-engines-font: @color-font-light;
+@color-percentage-div-background: #444;
diff --git a/searx/static/themes/legacy/less/mixins.less b/searx/static/themes/legacy/less/mixins.less
new file mode 100644
index 0000000..dbccce6
--- /dev/null
+++ b/searx/static/themes/legacy/less/mixins.less
@@ -0,0 +1,27 @@
+/*
+ * searx, A privacy-respecting, hackable metasearch engine
+ */
+
+// Mixins
+
+.text-size-adjust (@property: 100%) {
+ -webkit-text-size-adjust: @property;
+ -ms-text-size-adjust: @property;
+ -moz-text-size-adjust: @property;
+ text-size-adjust: @property;
+}
+
+.rounded-corners (@radius: 4px) {
+ -webkit-border-radius: @radius;
+ -moz-border-radius: @radius;
+ border-radius: @radius;
+}
+
+.user-select () {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
diff --git a/searx/static/themes/legacy/less/search.less b/searx/static/themes/legacy/less/search.less
new file mode 100644
index 0000000..d285ca7
--- /dev/null
+++ b/searx/static/themes/legacy/less/search.less
@@ -0,0 +1,68 @@
+/*
+ * searx, A privacy-respecting, hackable metasearch engine
+ */
+
+.search {
+ padding: 0;
+ margin: 0;
+ .checkbox_container label {
+ font-size: 0.9em;
+ border-bottom: 2px solid @color-categories-item-border-unselected;
+ }
+
+ .checkbox_container label:hover {
+ border-bottom: 2px solid @color-categories-item-border-unselected-hover;
+ }
+
+ .checkbox_container input[type="checkbox"]:checked + label {
+ border-bottom: 2px solid @color-categories-item-border-selected;
+ }
+}
+
+#search_wrapper {
+ position: relative;
+ width: @results-width;
+ padding: 10px;
+}
+
+.center #search_wrapper {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.q {
+ background: none repeat scroll 0 0 @color-search-background;
+ border: 1px solid @color-search-border;
+ color: @color-search-font;
+ font-size: 16px;
+ height: 28px;
+ margin: 0;
+ outline: medium none;
+ padding: 2px;
+ padding-left: 8px;
+ padding-right: 0px !important;
+ width: 100%;
+ z-index: 2;
+}
+
+#search_submit {
+ position: absolute;
+ top: 13px;
+ right: 1px;
+ padding: 0;
+ border: 0;
+ background: url('../img/search-icon.png') no-repeat;
+ background-size: 24px 24px;
+ opacity: 0.8;
+ width: 24px;
+ height: 30px;
+ font-size: 0;
+}
+
+@media screen and (max-width: @results-width) {
+ #search_wrapper {
+ width: 90%;
+ clear:both;
+ overflow: hidden
+ }
+}
diff --git a/searx/static/themes/legacy/less/style-rtl.less b/searx/static/themes/legacy/less/style-rtl.less
new file mode 100644
index 0000000..eac53c1
--- /dev/null
+++ b/searx/static/themes/legacy/less/style-rtl.less
@@ -0,0 +1,11 @@
+#search_submit {
+ left: 1px;
+ right:auto;
+}
+
+.result .favicon {
+ float: right;
+ margin-left: 0.5em;
+ margin-right: 0;
+}
+
diff --git a/searx/static/themes/legacy/less/style.less b/searx/static/themes/legacy/less/style.less
new file mode 100644
index 0000000..4374f7d
--- /dev/null
+++ b/searx/static/themes/legacy/less/style.less
@@ -0,0 +1,739 @@
+/*
+ * searx, A privacy-respecting, hackable metasearch engine
+ *
+ * To convert "style.less" to "style.css" run: $make styles
+ */
+
+@import "definitions.less";
+
+@import "mixins.less";
+
+@import "code.less";
+
+// Main LESS-Code
+
+html {
+ font-family: sans-serif;
+ font-size: 0.9em;
+ .text-size-adjust;
+ color: @color-font;
+ padding: 0;
+ margin: 0;
+}
+
+body, #container {
+ padding: 0;
+ margin: 0;
+}
+
+#container {
+ width: 100%;
+ position: absolute;
+ top: 0;
+}
+
+// Search-Field
+
+@import "search.less";
+
+// Autocompleter
+
+@import "autocompleter.less";
+
+.row {
+ max-width: 800px;
+ margin: 20px auto;
+ text-align: justify;
+
+ h1 {
+ font-size: 3em;
+ margin-top: 50px;
+ }
+
+ p {
+ padding: 0 10px;
+ max-width: 700px;
+ }
+
+ h3,ul {
+ margin: 4px 8px;
+ }
+}
+
+.hmarg {
+ margin: 0 20px;
+ border: 1px solid @color-hmarg-border;
+ padding: 4px 10px;
+}
+
+a {
+ &:link.hmarg {
+ color: @color-hmarg-font;
+ }
+
+ &:visited.hmarg {
+ color: @color-hmarg-font;
+ }
+
+ &:active.hmarg {
+ color: @color-hmarg-font-hover;
+ }
+
+ &:hover.hmarg {
+ color: @color-hmarg-font-hover;
+ }
+}
+
+.top_margin {
+ margin-top: 60px;
+}
+
+.center {
+ text-align: center;
+}
+
+h1 {
+ font-size: 5em;
+}
+
+div.title {
+ background: url('../img/searx.png') no-repeat;
+ width: 100%;
+ min-height: 80px;
+ background-position: center;
+
+ h1 {
+ visibility: hidden;
+ }
+}
+
+input[type="submit"] {
+ padding: 2px 6px;
+ margin: 2px 4px;
+ display: inline-block;
+ background: @color-download-button-background;
+ color: @color-download-button-font;
+ .rounded-corners;
+ border: 0;
+ cursor: pointer;
+}
+
+input[type="checkbox"] {
+ visibility: hidden;
+}
+
+fieldset {
+ margin: 8px;
+ border: 1px solid @color-settings-fieldset;
+}
+
+#categories {
+ margin: 0 10px;
+ .user-select;
+}
+
+.checkbox_container {
+ display: inline-block;
+ position: relative;
+ margin: 0 3px;
+ padding: 0px;
+
+ input {
+ display: none;
+ }
+}
+
+.checkbox_container label, .engine_checkbox label {
+ cursor: pointer;
+ padding: 4px 10px;
+ margin: 0;
+ display: block;
+ text-transform: capitalize;
+ .user-select;
+}
+
+.checkbox_container input[type="checkbox"]:checked + label {
+ background: @color-categories-item-selected;
+ color: @color-categories-item-selected-font;
+}
+
+.engine_checkbox {
+ padding: 4px;
+}
+
+label {
+ &.allow {
+ background: @color-settings-label-allowed-background;
+ padding: 4px 8px;
+ color: @color-settings-label-allowed-font;
+ display: none;
+ }
+
+ &.deny {
+ background: @color-settings-label-deny-background;
+ padding: 4px 8px;
+ color: @color-settings-label-deny-font;
+ display: inline;
+ }
+}
+
+.engine_checkbox input[type="checkbox"]:checked + label {
+ &:nth-child(2) + label {
+ display: none;
+ }
+
+ &.allow {
+ display: inline;
+ }
+}
+
+a {
+ text-decoration: none;
+ color: @color-url-font;
+
+ &:visited {
+ color: @color-url-visited-font;
+ }
+}
+
+.result {
+ margin: 19px 0 18px 0;
+ padding: 0;
+ clear: both;
+}
+
+.result_title {
+ margin-bottom: 0;
+
+ a {
+ color: @color-result-link-font;
+ font-weight: normal;
+ font-size: 1.1em;
+
+ &:hover {
+ text-decoration: underline;
+ }
+
+ &:visited {
+ color: @color-result-link-visited-font;
+ }
+ }
+}
+
+.cache_link {
+ font-size: 10px !important;
+}
+
+.result {
+ h3 {
+ font-size: 1em;
+ word-wrap:break-word;
+ margin: 5px 0 1px 0;
+ padding: 0
+ }
+
+ .content {
+ font-size: 0.8em;
+ margin: 0;
+ padding: 0;
+ max-width: 54em;
+ word-wrap:break-word;
+ line-height: 1.24;
+
+ img {
+ float: left;
+ margin-right: 5px;
+ max-width: 200px;
+ max-height: 100px;
+ }
+
+ br.last {
+ clear: both;
+ }
+ }
+
+ .url {
+ font-size: 0.8em;
+ margin: 0 0 3px 0;
+ padding: 0;
+ max-width: 54em;
+ word-wrap:break-word;
+ color: @color-result-url-font;
+ }
+
+ .published_date {
+ font-size: 0.8em;
+ color: @color-result-publishdate-font;
+ Margin: 5px 20px;
+ }
+
+ .thumbnail {
+ width: 400px;
+ }
+}
+
+.engines {
+ color: @color-engines-font;
+}
+
+.small_font {
+ font-size: 0.8em;
+}
+
+.small p {
+ margin: 2px 0;
+}
+
+.right {
+ float: right;
+}
+
+.invisible {
+ display: none;
+}
+
+.left {
+ float: left;
+}
+
+.highlight {
+ color: @color-highlight;
+}
+
+.content .highlight {
+ color: @color-black;
+}
+
+.image_result {
+ display: inline-block;
+ margin: 10px 10px;
+ position: relative;
+ max-height: 160px;
+
+ img {
+ border: 0;
+ max-height: 160px;
+ }
+
+ p {
+ margin: 0;
+ padding: 0;
+
+ span a {
+ display: none;
+ color: @color-result-image-span-font;
+ }
+
+ &:hover span a {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ padding: 4px;
+ background-color: @color-result-image-span-background-hover;
+ font-size: 0.7em;
+ }
+ }
+}
+
+.torrent_result {
+ border-left: 10px solid @color-result-torrent-border;
+ padding-left: 3px;
+
+ p {
+ margin: 3px;
+ font-size: 0.8em;
+ }
+
+ a {
+ color: @color-result-link-font;
+
+ &:hover {
+ text-decoration: underline;
+ }
+
+ &:visited {
+ color: @color-result-link-visited-font;
+ }
+ }
+}
+
+.definition_result {
+ border-left: 10px solid @color-result-definition-border;
+ padding-left: 3px;
+}
+
+.percentage {
+ position: relative;
+ width: 300px;
+
+ div {
+ background: @color-percentage-div-background;
+ }
+}
+
+table {
+ width: 100%;
+}
+
+td {
+ padding: 0 4px;
+}
+
+tr {
+ &:hover {
+ background: @color-settings-tr-hover;
+ }
+}
+
+#results {
+ margin: auto;
+ padding: 0;
+ width: @results-width;
+ margin-bottom: 20px;
+}
+
+#sidebar {
+ position: fixed;
+ bottom: 10px;
+ left: 10px;
+ margin: 0 2px 5px 5px;
+ padding: 0 2px 2px 2px;
+ width: 14em;
+
+ input {
+ padding: 0;
+ margin: 3px;
+ font-size: 0.8em;
+ display: inline-block;
+ background: transparent;
+ color: @color-result-search-url-font;
+ cursor: pointer;
+ }
+ input[type="submit"] {
+ text-decoration: underline;
+ }
+}
+
+#suggestions {
+
+ form {
+ display: inline;
+ }
+
+}
+
+#suggestions, #answers {
+
+ margin-top: 20px;
+ max-width: 45em;
+
+}
+
+#suggestions, #answers, #infoboxes {
+
+ input {
+ padding: 0;
+ margin: 3px;
+ font-size: 0.8em;
+ display: inline-block;
+ background: transparent;
+ color: @color-result-search-url-font;
+ cursor: pointer;
+ }
+
+ input[type="submit"] {
+ text-decoration: underline;
+ }
+
+}
+
+#suggestions-title {
+
+color: @color-font-light;
+
+
+}
+
+#answers {
+
+ border: 2px solid @color-answers-border;
+ padding: 20px;
+
+}
+
+#answers, #infoboxes {
+ form {
+ min-width: 210px;
+ }
+}
+
+
+#infoboxes {
+ position: absolute;
+ top: 100px;
+ right: 20px;
+ margin: 0px 2px 5px 5px;
+ padding: 0px 2px 2px;
+ max-width: 21em;
+ word-wrap: break-word;
+
+ .infobox {
+ margin: 10px 0 10px;
+ border: 1px solid #ddd;
+ padding: 5px;
+ font-size: 0.8em;
+ /* box-shadow: 0px 0px 5px #CCC; */
+
+ img {
+ max-width: 90%;
+ max-heigt: 12em;
+ display: block;
+ margin: 5px;
+ padding: 5px;
+ }
+
+ h2 {
+ margin: 0;
+ }
+
+ table {
+ table-layout: fixed;
+
+ td {
+ vertical-align: top;
+ }
+
+ }
+
+ input {
+ font-size: 1em;
+ }
+
+ br {
+ clear: both;
+ }
+
+ }
+}
+
+#search_url {
+ margin-top: 8px;
+
+ input {
+ border: 1px solid @color-result-search-url-border;
+ padding: 4px;
+ color: @color-result-search-url-font;
+ width: 14em;
+ display: block;
+ margin: 4px;
+ font-size: 0.8em;
+ }
+}
+
+#preferences {
+ top: 10px;
+ padding: 0;
+ border: 0;
+ background: url('../img/preference-icon.png') no-repeat;
+ background-size: 28px 28px;
+ opacity: 0.8;
+ width: 28px;
+ height: 30px;
+ display: block;
+
+ * {
+ display: none;
+ }
+}
+
+#pagination {
+ clear: both;
+
+ br {
+ clear: both;
+ }
+}
+
+#apis {
+ margin-top: 8px;
+ clear: both;
+}
+
+#categories_container {
+ position: relative;
+}
+
+@media screen and (max-width: @results-width) {
+
+ #results {
+ margin: auto;
+ padding: 0;
+ width: 90%;
+ }
+
+ .github {
+ display: none;
+ }
+
+ .checkbox_container {
+ display: block;
+ width: 90%;
+ //float: left;
+
+ label {
+ border-bottom: 0;
+ }
+ }
+
+ .preferences_container {
+ display: none;
+ postion: fixed !important;
+ top: 100px;
+ right: 0px;
+ }
+
+}
+
+@media screen and (max-width: 75em) {
+
+ div.title {
+
+ h1 {
+ font-size: 1em;
+ }
+ }
+
+ html.touch #categories {
+ width: 95%;
+ height: 30px;
+ text-align: left;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ -webkit-overflow-scrolling: touch;
+
+ #categories_container {
+ width: 1000px;
+ width: -moz-max-content;
+ width: -webkit-max-content;
+ width: max-content;
+
+ .checkbox_container {
+ display: inline-block;
+ width: auto;
+ }
+ }
+ }
+
+ #categories {
+ font-size: 90%;
+ clear: both;
+
+ .checkbox_container {
+ margin-top: 2px;
+ margin: auto;
+ }
+ }
+
+ #suggestions, #answers {
+ margin-top: 5px;
+ }
+
+ #infoboxes {
+ position: inherit;
+ max-width: inherit;
+
+ .infobox {
+ clear:both;
+
+ img {
+ float: left;
+ max-width: 10em;
+ }
+ }
+ }
+
+ #categories {
+ font-size: 90%;
+ clear: both;
+
+ .checkbox_container {
+ margin-top: 2px;
+ margin: auto;
+ }
+ }
+
+ #sidebar {
+ position: static;
+ max-width: @results-width;
+ margin: 0 0 2px 0;
+ padding: 0;
+ float: none;
+ border: none;
+ width: auto;
+ input {
+ border: 0;
+ }
+ }
+
+ #apis {
+ display: none;
+ }
+
+ #search_url {
+ display: none;
+ }
+
+ .result {
+ border-top: 1px solid @color-result-top-border;
+ margin: 8px 0 8px 0;
+
+ .thumbnail {
+ max-width: 98%;
+ }
+ }
+
+ .image_result {
+ max-width: 98%;
+ img {
+ max-width: 98%;
+ }
+ }
+}
+
+.favicon {
+ float: left;
+ margin-right: 4px;
+ margin-top: 2px;
+}
+
+.preferences_back {
+ background: none repeat scroll 0 0 @color-settings-return-background;
+ border: 0 none;
+ .rounded-corners;
+ cursor: pointer;
+ display: inline-block;
+ margin: 2px 4px;
+ padding: 4px 6px;
+
+ a {
+ color: @color-settings-return-font;
+ }
+}
+
+.hidden {
+ opacity: 0;
+ overflow: hidden;
+ font-size: 0.8em;
+ position: absolute;
+ bottom: -20px;
+ width: 100%;
+ text-position: center;
+ background: white;
+ transition: opacity 1s ease;
+}
+
+#categories_container:hover .hidden {
+ transition: opacity 1s ease;
+ opacity: 0.8;
+}
diff --git a/searx/static/themes/oscar/.gitignore b/searx/static/themes/oscar/.gitignore
new file mode 100644
index 0000000..c2658d7
--- /dev/null
+++ b/searx/static/themes/oscar/.gitignore
@@ -0,0 +1 @@
+node_modules/
diff --git a/searx/static/themes/oscar/README.rst b/searx/static/themes/oscar/README.rst
new file mode 100644
index 0000000..e7daa87
--- /dev/null
+++ b/searx/static/themes/oscar/README.rst
@@ -0,0 +1,17 @@
+install dependencies
+~~~~~~~~~~~~~~~~~~~~
+
+run this command in the directory ``searx/static/themes/oscar``
+
+``npm install``
+
+compile sources
+~~~~~~~~~~~~~~~
+
+run this command in the directory ``searx/static/themes/oscar``
+
+``grunt``
+
+or in the root directory:
+
+``make grunt``
diff --git a/searx/static/themes/oscar/gruntfile.js b/searx/static/themes/oscar/gruntfile.js
new file mode 100644
index 0000000..59d1b6d
--- /dev/null
+++ b/searx/static/themes/oscar/gruntfile.js
@@ -0,0 +1,90 @@
+module.exports = function(grunt) {
+
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ concat: {
+ options: {
+ separator: ';'
+ },
+ dist: {
+ src: ['js/searx_src/*.js'],
+ dest: 'js/searx.js'
+ }
+ },
+ uglify: {
+ options: {
+ banner: '/*! oscar/searx.min.js | <%= grunt.template.today("dd-mm-yyyy") %> | https://github.com/asciimoo/searx */\n'
+ },
+ dist: {
+ files: {
+ 'js/searx.min.js': ['<%= concat.dist.dest %>']
+ }
+ }
+ },
+ jshint: {
+ files: ['gruntfile.js', 'js/searx_src/*.js'],
+ options: {
+ // options here to override JSHint defaults
+ globals: {
+ jQuery: true,
+ console: true,
+ module: true,
+ document: true
+ }
+ }
+ },
+ less: {
+ development: {
+ options: {
+ paths: ["less/pointhi", "less/logicodev"]
+ //banner: '/*! less/oscar/oscar.css | <%= grunt.template.today("dd-mm-yyyy") %> | https://github.com/asciimoo/searx */\n'
+ },
+ files: {"css/pointhi.css": "less/pointhi/oscar.less",
+ "css/logicodev.css": "less/logicodev/oscar.less"}
+ },
+ production: {
+ options: {
+ paths: ["less/pointhi", "less/logicodev"],
+ //banner: '/*! less/oscar/oscar.css | <%= grunt.template.today("dd-mm-yyyy") %> | https://github.com/asciimoo/searx */\n',
+ cleancss: true
+ },
+ files: {"css/pointhi.min.css": "less/pointhi/oscar.less",
+ "css/logicodev.min.css": "less/logicodev/oscar.less"}
+ },
+ bootstrap: {
+ options: {
+ paths: ["less/bootstrap"],
+ cleancss: true
+ },
+ files: {"css/bootstrap.min.css": "less/bootstrap/bootstrap.less"}
+ },
+ },
+ watch: {
+ scripts: {
+ files: ['<%= jshint.files %>'],
+ tasks: ['jshint', 'concat', 'uglify']
+ },
+ oscar_styles: {
+ files: ['less/pointhi/**/*.less'],
+ tasks: ['less:development', 'less:production']
+ },
+ bootstrap_styles: {
+ files: ['less/bootstrap/**/*.less'],
+ tasks: ['less:bootstrap']
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-watch');
+ grunt.loadNpmTasks('grunt-contrib-concat');
+ grunt.loadNpmTasks('grunt-contrib-less');
+
+ grunt.registerTask('test', ['jshint']);
+
+ grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'less']);
+
+ grunt.registerTask('styles', ['less']);
+
+};
diff --git a/searx/static/themes/oscar/img/favicon.png b/searx/static/themes/oscar/img/favicon.png
new file mode 100644
index 0000000..15b4575
--- /dev/null
+++ b/searx/static/themes/oscar/img/favicon.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/icons/README.md b/searx/static/themes/oscar/img/icons/README.md
new file mode 100644
index 0000000..0e3ad1c
--- /dev/null
+++ b/searx/static/themes/oscar/img/icons/README.md
@@ -0,0 +1,2 @@
+Source: http://www.iconspedia.com/pack/flat-gradient-social-icons-4384/
+License: Free for non commercial use.
diff --git a/searx/static/themes/oscar/img/loader.gif b/searx/static/themes/oscar/img/loader.gif
new file mode 100644
index 0000000..419cdee
--- /dev/null
+++ b/searx/static/themes/oscar/img/loader.gif
Binary files differ
diff --git a/searx/static/themes/oscar/img/logo_searx_a.png b/searx/static/themes/oscar/img/logo_searx_a.png
new file mode 100644
index 0000000..9427900
--- /dev/null
+++ b/searx/static/themes/oscar/img/logo_searx_a.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/logo_searx_a_n.png b/searx/static/themes/oscar/img/logo_searx_a_n.png
new file mode 100644
index 0000000..5b24aea
--- /dev/null
+++ b/searx/static/themes/oscar/img/logo_searx_a_n.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/layers-2x.png b/searx/static/themes/oscar/img/map/layers-2x.png
new file mode 100644
index 0000000..0b30da6
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/layers-2x.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/layers.png b/searx/static/themes/oscar/img/map/layers.png
new file mode 100644
index 0000000..4297fd9
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/layers.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/marker-icon-2x-green.png b/searx/static/themes/oscar/img/map/marker-icon-2x-green.png
new file mode 100644
index 0000000..7446bb0
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/marker-icon-2x-green.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/marker-icon-2x-orange.png b/searx/static/themes/oscar/img/map/marker-icon-2x-orange.png
new file mode 100644
index 0000000..ecd6773
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/marker-icon-2x-orange.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/marker-icon-2x-red.png b/searx/static/themes/oscar/img/map/marker-icon-2x-red.png
new file mode 100644
index 0000000..1d2e197
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/marker-icon-2x-red.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/marker-icon-2x.png b/searx/static/themes/oscar/img/map/marker-icon-2x.png
new file mode 100644
index 0000000..0015b64
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/marker-icon-2x.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/marker-icon-green.png b/searx/static/themes/oscar/img/map/marker-icon-green.png
new file mode 100644
index 0000000..f48ef41
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/marker-icon-green.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/marker-icon-orange.png b/searx/static/themes/oscar/img/map/marker-icon-orange.png
new file mode 100644
index 0000000..d0d2220
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/marker-icon-orange.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/marker-icon-red.png b/searx/static/themes/oscar/img/map/marker-icon-red.png
new file mode 100644
index 0000000..7a92b9e
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/marker-icon-red.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/marker-icon.png b/searx/static/themes/oscar/img/map/marker-icon.png
new file mode 100644
index 0000000..e2e9f75
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/marker-icon.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/map/marker-shadow.png b/searx/static/themes/oscar/img/map/marker-shadow.png
new file mode 100644
index 0000000..d1e773c
--- /dev/null
+++ b/searx/static/themes/oscar/img/map/marker-shadow.png
Binary files differ
diff --git a/searx/static/themes/oscar/img/searx_logo.png b/searx/static/themes/oscar/img/searx_logo.png
new file mode 100644
index 0000000..307b42f
--- /dev/null
+++ b/searx/static/themes/oscar/img/searx_logo.png
Binary files differ
diff --git a/searx/static/themes/oscar/js/searx_src/00_requirejs_config.js b/searx/static/themes/oscar/js/searx_src/00_requirejs_config.js
new file mode 100644
index 0000000..1aa4349
--- /dev/null
+++ b/searx/static/themes/oscar/js/searx_src/00_requirejs_config.js
@@ -0,0 +1,23 @@
+/**
+ * searx is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * searx is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with searx. If not, see < http://www.gnu.org/licenses/ >.
+ *
+ * (C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
+ */
+
+requirejs.config({
+ baseUrl: './static/themes/oscar/js',
+ paths: {
+ app: '../app'
+ }
+});
diff --git a/searx/static/themes/oscar/js/searx_src/autocompleter.js b/searx/static/themes/oscar/js/searx_src/autocompleter.js
new file mode 100644
index 0000000..70c66d2
--- /dev/null
+++ b/searx/static/themes/oscar/js/searx_src/autocompleter.js
@@ -0,0 +1,37 @@
+/**
+ * searx is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * searx is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with searx. If not, see < http://www.gnu.org/licenses/ >.
+ *
+ * (C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
+ */
+
+if(searx.autocompleter) {
+ searx.searchResults = new Bloodhound({
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ remote: './autocompleter?q=%QUERY'
+ });
+ searx.searchResults.initialize();
+}
+
+$(document).ready(function(){
+ if(searx.autocompleter) {
+ $('#q').typeahead(null, {
+ name: 'search-results',
+ displayKey: function(result) {
+ return result;
+ },
+ source: searx.searchResults.ttAdapter()
+ });
+ }
+});
diff --git a/searx/static/themes/oscar/js/searx_src/element_modifiers.js b/searx/static/themes/oscar/js/searx_src/element_modifiers.js
new file mode 100644
index 0000000..8e42805
--- /dev/null
+++ b/searx/static/themes/oscar/js/searx_src/element_modifiers.js
@@ -0,0 +1,99 @@
+/**
+ * searx is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * searx is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with searx. If not, see < http://www.gnu.org/licenses/ >.
+ *
+ * (C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
+ */
+
+$(document).ready(function(){
+ /**
+ * focus element if class="autofocus" and id="q"
+ */
+ $('#q.autofocus').focus();
+
+ /**
+ * select full content on click if class="select-all-on-click"
+ */
+ $(".select-all-on-click").click(function () {
+ $(this).select();
+ });
+
+ /**
+ * change text during btn-collapse click if possible
+ */
+ $('.btn-collapse').click(function() {
+ var btnTextCollapsed = $(this).data('btn-text-collapsed');
+ var btnTextNotCollapsed = $(this).data('btn-text-not-collapsed');
+
+ if(btnTextCollapsed !== '' && btnTextNotCollapsed !== '') {
+ if($(this).hasClass('collapsed')) {
+ new_html = $(this).html().replace(btnTextCollapsed, btnTextNotCollapsed);
+ } else {
+ new_html = $(this).html().replace(btnTextNotCollapsed, btnTextCollapsed);
+ }
+ $(this).html(new_html);
+ }
+ });
+
+ /**
+ * change text during btn-toggle click if possible
+ */
+ $('.btn-toggle .btn').click(function() {
+ var btnClass = 'btn-' + $(this).data('btn-class');
+ var btnLabelDefault = $(this).data('btn-label-default');
+ var btnLabelToggled = $(this).data('btn-label-toggled');
+ if(btnLabelToggled !== '') {
+ if($(this).hasClass('btn-default')) {
+ new_html = $(this).html().replace(btnLabelDefault, btnLabelToggled);
+ } else {
+ new_html = $(this).html().replace(btnLabelToggled, btnLabelDefault);
+ }
+ $(this).html(new_html);
+ }
+ $(this).toggleClass(btnClass);
+ $(this).toggleClass('btn-default');
+ });
+
+ /**
+ * change text during btn-toggle click if possible
+ */
+ $('.media-loader').click(function() {
+ var target = $(this).data('target');
+ var iframe_load = $(target + ' > iframe');
+ var srctest = iframe_load.attr('src');
+ if(srctest === undefined || srctest === false){
+ iframe_load.attr('src', iframe_load.data('src'));
+ }
+ });
+
+ /**
+ * Select or deselect every categories on double clic
+ */
+ $(".btn-sm").dblclick(function() {
+ var btnClass = 'btn-' + $(this).data('btn-class'); // primary
+ if($(this).hasClass('btn-default')) {
+ $(".btn-sm > input").attr('checked', 'checked');
+ $(".btn-sm > input").prop("checked", true);
+ $(".btn-sm").addClass(btnClass);
+ $(".btn-sm").addClass('active');
+ $(".btn-sm").removeClass('btn-default');
+ } else {
+ $(".btn-sm > input").attr('checked', '');
+ $(".btn-sm > input").removeAttr('checked');
+ $(".btn-sm > input").checked = false;
+ $(".btn-sm").removeClass(btnClass);
+ $(".btn-sm").removeClass('active');
+ $(".btn-sm").addClass('btn-default');
+ }
+ });
+});
diff --git a/searx/static/themes/oscar/js/searx_src/leaflet_map.js b/searx/static/themes/oscar/js/searx_src/leaflet_map.js
new file mode 100644
index 0000000..4be46ac
--- /dev/null
+++ b/searx/static/themes/oscar/js/searx_src/leaflet_map.js
@@ -0,0 +1,167 @@
+/**
+ * searx is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * searx is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with searx. If not, see < http://www.gnu.org/licenses/ >.
+ *
+ * (C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
+ */
+
+$(document).ready(function(){
+ $(".searx_overpass_request").on( "click", function( event ) {
+ var overpass_url = "https://overpass-api.de/api/interpreter?data=";
+ var query_start = overpass_url + "[out:json][timeout:25];(";
+ var query_end = ");out meta;";
+
+ var osm_id = $(this).data('osm-id');
+ var osm_type = $(this).data('osm-type');
+ var result_table = $(this).data('result-table');
+ var result_table_loadicon = "#" + $(this).data('result-table-loadicon');
+
+ // tags which can be ignored
+ var osm_ignore_tags = [ "addr:city", "addr:country", "addr:housenumber", "addr:postcode", "addr:street" ];
+
+ if(osm_id && osm_type && result_table) {
+ result_table = "#" + result_table;
+ var query = null;
+ switch(osm_type) {
+ case 'node':
+ query = query_start + "node(" + osm_id + ");" + query_end;
+ break;
+ case 'way':
+ query = query_start + "way(" + osm_id + ");" + query_end;
+ break;
+ case 'relation':
+ query = query_start + "relation(" + osm_id + ");" + query_end;
+ break;
+ default:
+ break;
+ }
+ if(query) {
+ //alert(query);
+ var ajaxRequest = $.ajax( query )
+ .done(function( html) {
+ if(html && html.elements && html.elements[0]) {
+ var element = html.elements[0];
+ var newHtml = $(result_table).html();
+ for (var row in element.tags) {
+ if(element.tags.name === null || osm_ignore_tags.indexOf(row) == -1) {
+ newHtml += "<tr><td>" + row + "</td><td>";
+ switch(row) {
+ case "phone":
+ case "fax":
+ newHtml += "<a href=\"tel:" + element.tags[row].replace(/ /g,'') + "\">" + element.tags[row] + "</a>";
+ break;
+ case "email":
+ newHtml += "<a href=\"mailto:" + element.tags[row] + "\">" + element.tags[row] + "</a>";
+ break;
+ case "website":
+ case "url":
+ newHtml += "<a href=\"" + element.tags[row] + "\">" + element.tags[row] + "</a>";
+ break;
+ case "wikidata":
+ newHtml += "<a href=\"https://www.wikidata.org/wiki/" + element.tags[row] + "\">" + element.tags[row] + "</a>";
+ break;
+ case "wikipedia":
+ if(element.tags[row].indexOf(":") != -1) {
+ newHtml += "<a href=\"https://" + element.tags[row].substring(0,element.tags[row].indexOf(":")) + ".wikipedia.org/wiki/" + element.tags[row].substring(element.tags[row].indexOf(":")+1) + "\">" + element.tags[row] + "</a>";
+ break;
+ }
+ /* jshint ignore:start */
+ default:
+ /* jshint ignore:end */
+ newHtml += element.tags[row];
+ break;
+ }
+ newHtml += "</td></tr>";
+ }
+ }
+ $(result_table).html(newHtml);
+ $(result_table).removeClass('hidden');
+ $(result_table_loadicon).addClass('hidden');
+ }
+ })
+ .fail(function() {
+ $(result_table_loadicon).html($(result_table_loadicon).html() + "<p class=\"text-muted\">could not load data!</p>");
+ });
+ }
+ }
+
+ // this event occour only once per element
+ $( this ).off( event );
+ });
+
+ $(".searx_init_map").on( "click", function( event ) {
+ var leaflet_target = $(this).data('leaflet-target');
+ var map_lon = $(this).data('map-lon');
+ var map_lat = $(this).data('map-lat');
+ var map_zoom = $(this).data('map-zoom');
+ var map_boundingbox = $(this).data('map-boundingbox');
+ var map_geojson = $(this).data('map-geojson');
+
+ require(['leaflet-0.7.3.min'], function(leaflet) {
+ if(map_boundingbox) {
+ southWest = L.latLng(map_boundingbox[0], map_boundingbox[2]);
+ northEast = L.latLng(map_boundingbox[1], map_boundingbox[3]);
+ map_bounds = L.latLngBounds(southWest, northEast);
+ }
+
+ // TODO hack
+ // change default imagePath
+ L.Icon.Default.imagePath = "./static/themes/oscar/img/map";
+
+ // init map
+ var map = L.map(leaflet_target);
+
+ // create the tile layer with correct attribution
+ var osmMapnikUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+ var osmMapnikAttrib='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
+ var osmMapnik = new L.TileLayer(osmMapnikUrl, {minZoom: 1, maxZoom: 19, attribution: osmMapnikAttrib});
+
+ var osmWikimediaUrl='https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png';
+ var osmWikimediaAttrib = 'Wikimedia maps beta | Maps data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
+ var osmWikimedia = new L.TileLayer(osmWikimediaUrl, {minZoom: 1, maxZoom: 19, attribution: osmWikimediaAttrib});
+
+ // init map view
+ if(map_bounds) {
+ // TODO hack: https://github.com/Leaflet/Leaflet/issues/2021
+ setTimeout(function () {
+ map.fitBounds(map_bounds, {
+ maxZoom:17
+ });
+ }, 0);
+ } else if (map_lon && map_lat) {
+ if(map_zoom)
+ map.setView(new L.LatLng(map_lat, map_lon),map_zoom);
+ else
+ map.setView(new L.LatLng(map_lat, map_lon),8);
+ }
+
+ map.addLayer(osmMapnik);
+
+ var baseLayers = {
+ "OSM Mapnik": osmMapnik/*,
+ "OSM Wikimedia": osmWikimedia*/
+ };
+
+ L.control.layers(baseLayers).addTo(map);
+
+
+ if(map_geojson)
+ L.geoJson(map_geojson).addTo(map);
+ /*else if(map_bounds)
+ L.rectangle(map_bounds, {color: "#ff7800", weight: 3, fill:false}).addTo(map);*/
+ });
+
+ // this event occour only once per element
+ $( this ).off( event );
+ });
+});
diff --git a/searx/static/themes/oscar/less/logicodev/advanced.less b/searx/static/themes/oscar/less/logicodev/advanced.less
new file mode 100644
index 0000000..4c3827b
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/advanced.less
@@ -0,0 +1,49 @@
+#advanced-search-container {
+ display: none;
+ text-align: left;
+ margin-bottom: 1rem;
+ clear: both;
+
+ label, .input-group-addon {
+ font-size: 1.2rem;
+ font-weight:normal;
+ background-color: white;
+ border: @mild-gray 1px solid;
+ border-right: none;
+ color: @dark-gray;
+ padding-bottom: 0.4rem;
+ padding-right: 0.7rem;
+ padding-left: 0.7rem;
+ }
+
+ label:last-child, .input-group-addon:last-child {
+ border-right: @mild-gray 1px solid;
+ }
+
+ input[type="radio"] {
+ display: none;
+ }
+
+ input[type="radio"]:checked + label{
+ color: @black;
+ font-weight: bold;
+ border-bottom: @light-green 5px solid;
+ }
+}
+
+#check-advanced {
+ display: none;
+}
+
+#check-advanced:checked ~ #advanced-search-container {
+ display: block;
+}
+
+.advanced {
+ padding: 0;
+ margin-top: 0.3rem;
+ text-align: right;
+ label, select {
+ cursor: pointer;
+ }
+}
diff --git a/searx/static/themes/oscar/less/logicodev/checkbox.less b/searx/static/themes/oscar/less/logicodev/checkbox.less
new file mode 100644
index 0000000..6428b36
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/checkbox.less
@@ -0,0 +1,9 @@
+// Hide element if checkbox is checked
+input[type=checkbox]:checked + .label_hide_if_checked, input[type=checkbox]:checked + .label_hide_if_not_checked + .label_hide_if_checked {
+ display:none;
+}
+
+// Hide element if checkbox is not checked
+input[type=checkbox]:not(:checked) + .label_hide_if_not_checked, input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not_checked {
+ display:none;
+}
diff --git a/searx/static/themes/oscar/less/logicodev/code.less b/searx/static/themes/oscar/less/logicodev/code.less
new file mode 100644
index 0000000..96486f5
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/code.less
@@ -0,0 +1,103 @@
+pre, code{
+ font-family: 'Ubuntu Mono', 'Courier New', 'Lucida Console', monospace !important;
+}
+
+.lineno{
+ margin-right: 5px;
+}
+
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #f8f8f8; }
+.highlight .c { color: #556366; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid @orange } /* Error */
+.highlight .k { color: #BE74D5; font-weight: bold } /* Keyword */
+.highlight .o { color: #D19A66 } /* Operator */
+.highlight .cm { color: #556366; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
+.highlight .c1 { color: #556366; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #556366; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #888888 } /* Generic.Output */
+.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0044DD } /* Generic.Traceback */
+.highlight .kc { color: #BE74D5; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #BE74D5; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #BE74D5; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #BE74D5 } /* Keyword.Pseudo */
+.highlight .kr { color: #BE74D5; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #D46C72 } /* Keyword.Type */
+.highlight .m { color: #D19A66 } /* Literal.Number */
+.highlight .s { color: #86C372 } /* Literal.String */
+.highlight .na { color: #7D9029 } /* Name.Attribute */
+.highlight .nb { color: #BE74D5 } /* Name.Builtin */
+.highlight .nc { color: #61AFEF; font-weight: bold } /* Name.Class */
+.highlight .no { color: #D19A66 } /* Name.Constant */
+.highlight .nd { color: #AA22FF } /* Name.Decorator */
+.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #61AFEF } /* Name.Function */
+.highlight .nl { color: #A0A000 } /* Name.Label */
+.highlight .nn { color: #61AFEF; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #BE74D5; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #DFC06F } /* Name.Variable */
+.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #D7DAE0 } /* Text.Whitespace */
+.highlight .mf { color: #D19A66 } /* Literal.Number.Float */
+.highlight .mh { color: #D19A66 } /* Literal.Number.Hex */
+.highlight .mi { color: #D19A66 } /* Literal.Number.Integer */
+.highlight .mo { color: #D19A66 } /* Literal.Number.Oct */
+.highlight .sb { color: #86C372 } /* Literal.String.Backtick */
+.highlight .sc { color: #86C372 } /* Literal.String.Char */
+.highlight .sd { color: #86C372; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #86C372 } /* Literal.String.Double */
+.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #86C372 } /* Literal.String.Heredoc */
+.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.highlight .sx { color: #BE74D5 } /* Literal.String.Other */
+.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
+.highlight .s1 { color: #86C372 } /* Literal.String.Single */
+.highlight .ss { color: #DFC06F } /* Literal.String.Symbol */
+.highlight .bp { color: #BE74D5 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #DFC06F } /* Name.Variable.Class */
+.highlight .vg { color: #DFC06F } /* Name.Variable.Global */
+.highlight .vi { color: #DFC06F } /* Name.Variable.Instance */
+.highlight .il { color: #D19A66 } /* Literal.Number.Integer.Long */
+
+.highlight .lineno {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: default;
+ color: #556366;
+
+ &::selection {
+ background: transparent; /* WebKit/Blink Browsers */
+ }
+ &::-moz-selection {
+ background: transparent; /* Gecko Browsers */
+ }
+}
+
+.highlight pre {
+ background-color: #282C34;
+ color: #D7DAE0;
+ border: none;
+ margin-bottom: 25px;
+ font-size: 15px;
+ padding: 20px 10px;
+}
+
+.highlight {
+ font-weight: 700;
+}
+
+
diff --git a/searx/static/themes/oscar/less/logicodev/cursor.less b/searx/static/themes/oscar/less/logicodev/cursor.less
new file mode 100644
index 0000000..cbc1ea6
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/cursor.less
@@ -0,0 +1,8 @@
+// display cursor
+.cursor-text {
+ cursor: text !important;
+}
+
+.cursor-pointer {
+ cursor: pointer !important;
+}
diff --git a/searx/static/themes/oscar/less/logicodev/footer.less b/searx/static/themes/oscar/less/logicodev/footer.less
new file mode 100644
index 0000000..d23a0cc
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/footer.less
@@ -0,0 +1,30 @@
+// Sticky footer styles
+*{
+ border-radius: 0 !important;
+}
+html {
+ position: relative;
+ min-height: 100%;
+ color: @black;
+}
+
+body {
+ /* Margin bottom by footer height */
+ font-family: 'Roboto', Helvetica, Arial, sans-serif;
+ margin-bottom: 80px;
+ background-color: white;
+
+ a{
+ color: @blue;
+ }
+}
+
+.footer {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ /* Set the fixed height of the footer here */
+ height: 60px;
+ text-align: center;
+ color: #999;
+}
diff --git a/searx/static/themes/oscar/less/logicodev/infobox.less b/searx/static/themes/oscar/less/logicodev/infobox.less
new file mode 100644
index 0000000..0d488d7
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/infobox.less
@@ -0,0 +1,37 @@
+.infobox {
+
+ .panel-heading{
+ background-color: @dim-gray;
+
+ .panel-title{
+ font-weight: 700;
+ }
+ }
+
+
+ p{
+ font-family: "DejaVu Serif", Georgia, Cambria, "Times New Roman", Times, serif !important;
+ font-style: italic;
+ }
+
+ .btn{
+ background-color: @green;
+ border: none;
+
+ a{
+ color: white;
+ margin: 5px;
+ }
+ }
+
+ .infobox_part {
+ margin-bottom: 20px;
+ word-wrap: break-word;
+ table-layout: fixed;
+
+ }
+
+ .infobox_part:last-child {
+ margin-bottom: 0;
+ }
+}
diff --git a/searx/static/themes/oscar/less/logicodev/navbar.less b/searx/static/themes/oscar/less/logicodev/navbar.less
new file mode 100644
index 0000000..5da7115
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/navbar.less
@@ -0,0 +1,31 @@
+.searx-navbar {
+ background: @black;
+ height: 2.3rem;
+ font-size: 1.3rem;
+ line-height: 1.3rem;
+ padding: 0.5rem;
+ font-weight: bold;
+ margin-bottom: 0.8rem;
+
+ a, a:hover {
+ margin-right: 2.0rem;
+ color: white;
+ text-decoration: none;
+ }
+
+ .instance a {
+ color: @light-green;
+ margin-left: 2.0rem;
+ }
+}
+
+#main-logo {
+ margin-top: 20vh;
+ margin-bottom: 25px;
+
+ & > img {
+ max-width: 350px;
+ width: 80%;
+ }
+}
+
diff --git a/searx/static/themes/oscar/less/logicodev/onoff.less b/searx/static/themes/oscar/less/logicodev/onoff.less
new file mode 100644
index 0000000..f471892
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/onoff.less
@@ -0,0 +1,57 @@
+.onoff-checkbox {
+ width:15%;
+}
+.onoffswitch {
+ position: relative;
+ width: 110px;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ -ms-user-select: none;
+}
+.onoffswitch-checkbox {
+ display: none;
+}
+.onoffswitch-label {
+ display: block;
+ overflow: hidden;
+ cursor: pointer;
+ border: 2px solid #FFFFFF !important;
+ border-radius: 50px !important;
+}
+.onoffswitch-inner {
+ display: block;
+ transition: margin 0.3s ease-in 0s;
+}
+
+.onoffswitch-inner:before, .onoffswitch-inner:after {
+ display: block;
+ float: left;
+ width: 50%;
+ height: 30px;
+ padding: 0;
+ line-height: 40px;
+ font-size: 20px;
+ box-sizing: border-box;
+ content: "";
+ background-color: #EEEEEE;
+}
+
+.onoffswitch-switch {
+ display: block;
+ width: 37px;
+ background-color: @light-green;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0px;
+ border: 2px solid #FFFFFF !important;
+ border-radius: 50px !important;
+ transition: all 0.3s ease-in 0s;
+}
+.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
+ margin-right: 0;
+}
+.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
+ right: 71px;
+ background-color: #A1A1A1;
+}
diff --git a/searx/static/themes/oscar/less/logicodev/oscar.less b/searx/static/themes/oscar/less/logicodev/oscar.less
new file mode 100644
index 0000000..55181cb
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/oscar.less
@@ -0,0 +1,21 @@
+@import "variables.less";
+
+@import "navbar.less";
+
+@import "footer.less";
+
+@import "checkbox.less";
+
+@import "onoff.less";
+
+@import "results.less";
+
+@import "infobox.less";
+
+@import "search.less";
+
+@import "advanced.less";
+
+@import "cursor.less";
+
+@import "code.less";
diff --git a/searx/static/themes/oscar/less/logicodev/results.less b/searx/static/themes/oscar/less/logicodev/results.less
new file mode 100644
index 0000000..3b36a17
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/results.less
@@ -0,0 +1,168 @@
+.result_header {
+ margin-top: 0px;
+ margin-bottom: 2px;
+ font-size: 16px;
+
+ .favicon {
+ margin-bottom:-3px;
+ }
+
+ a {
+ color: @black;
+ text-decoration: none;
+
+ &:hover{
+ color: @blue;
+ }
+
+ &:visited{
+ color: @violet;
+ }
+
+ .highlight {
+ background-color: @dim-gray;
+ // Chrome hack: bold is different size than normal
+ // https://stackoverflow.com/questions/20713988/weird-text-alignment-issue-in-css-when-bolded-lucida-sans
+ }
+ }
+}
+
+.result-content {
+ margin-top: 2px;
+ margin-bottom: 0;
+ word-wrap: break-word;
+ color: @dark-gray;
+ font-size: 13px;
+
+
+ .highlight {
+ font-weight:bold;
+ }
+
+}
+
+.external-link {
+ color: @dark-green;
+ font-size: 12px;
+
+ a {
+ margin-right: 3px;
+ }
+}
+
+// default formating of results
+.result-default, .result-code, .result-torrent, .result-videos, .result-map {
+ clear: both;
+ padding: 2px 4px;
+ &:hover{
+ background-color: @dim-gray;
+ }
+}
+
+
+// image formating of results
+.result-images {
+ float: left !important;
+ width: 24%;
+ margin: .5%;
+ a {
+ display: block;
+ width: 100%;
+ background-size: cover;
+ }
+}
+
+.img-thumbnail {
+ margin: 5px;
+ max-height: 128px;
+ min-height: 128px;
+}
+
+// video formating of results
+.result-videos {
+ clear: both;
+
+ hr{
+ margin: 5px 0 15px 0;
+ }
+
+ .collapse{
+ width: 100%;
+ }
+
+ .in{
+ margin-bottom: 8px;
+ }
+}
+
+// torrent formating of results
+.result-torrent {
+ clear: both;
+
+ b{
+ margin-right: 5px;
+ margin-left: 5px;
+ }
+
+ .seeders{
+ color: @green;
+ }
+
+ .leechers{
+ color: @red;
+ }
+}
+
+// map formating of results
+.result-map {
+ clear: both;
+}
+
+// code formating of results
+.result-code {
+ clear: both;
+
+ .code-fork, .code-fork a{
+ color: @dark-gray;
+ }
+
+}
+
+// suggestion
+.suggestion_item {
+ margin: 2px 5px;
+}
+
+// download result
+.result_download {
+ margin-right: 5px;
+}
+
+// page forward, backward
+#pagination {
+ margin-top: 30px;
+ padding-bottom: 60px;
+}
+
+.label-default {
+ color: @gray;
+ background: transparent;
+}
+
+.result .text-muted small {
+ word-wrap: break-word;
+}
+
+.modal-wrapper {
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+}
+
+.modal-wrapper {
+ background-clip: padding-box;
+ background-color: #fff;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ outline: 0 none;
+ position: relative;
+}
diff --git a/searx/static/themes/oscar/less/logicodev/search.less b/searx/static/themes/oscar/less/logicodev/search.less
new file mode 100644
index 0000000..fa1e0e8
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/search.less
@@ -0,0 +1,79 @@
+.search_categories, #categories {
+ text-transform: capitalize;
+ margin-bottom: 0.5rem;
+ display: flex;
+ flex-wrap: wrap;
+ flex-flow: row wrap;
+ align-content: stretch;
+
+ label, .input-group-addon {
+ flex-grow: 1;
+ flex-basis: auto;
+ font-size: 1.2rem;
+ font-weight: normal;
+ background-color: white;
+ border: @mild-gray 1px solid;
+ border-right: none;
+ color: @dark-gray;
+ padding-bottom: 0.4rem;
+ padding-top: 0.4rem;
+ text-align: center;
+ }
+ label:last-child, .input-group-addon:last-child {
+ border-right: @mild-gray 1px solid;
+ }
+
+ input[type="checkbox"]:checked + label {
+ color: @black;
+ font-weight: bold;
+ border-bottom: @light-green 5px solid;
+ }
+}
+
+#main-logo{
+ margin-top: 10vh;
+ margin-bottom: 25px;
+}
+
+#main-logo > img {
+ max-width: 350px;
+ width: 80%;
+}
+
+#q{
+ box-shadow: none;
+ border-right: none;
+ border-color: @gray;
+}
+
+ #search_form .input-group-btn .btn{
+ border-color: @gray;
+ }
+
+ #search_form .input-group-btn .btn:hover{
+ background-color: @green;
+ color: white;
+ }
+
+.custom-select {
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ font-size: 1.2rem;
+ font-weight:normal;
+ background-color: white;
+ border: @mild-gray 1px solid;
+ color: @dark-gray;
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAQAAACR313BAAAABGdBTUEAALGPC/xhBQAAACBjSFJN
+AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZ
+cwAABFkAAARZAVnbJUkAAAAHdElNRQfgBxgLDwB20OFsAAAAbElEQVQY073OsQ3CMAAEwJMYwJGn
+sAehpoXJItltBkmcdZBYgIIiQoLglnz3ui+eP+bk5uneteTMZJa6OJuIqvYzSJoqwqBq8gdmTTW8
+6/dghxAUq4xsVYT9laBYXCw93Aajh7GPEF23t4fkBYevGFTANkPRAAAAJXRFWHRkYXRlOmNyZWF0
+ZQAyMDE2LTA3LTI0VDExOjU1OjU4KzAyOjAwRFqFOQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNi0w
+Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
+7jwaAAAAAElFTkSuQmCC) 96% no-repeat;
+}
+
+.search-margin {
+ margin-bottom: 0.6em;
+} \ No newline at end of file
diff --git a/searx/static/themes/oscar/less/logicodev/variables.less b/searx/static/themes/oscar/less/logicodev/variables.less
new file mode 100644
index 0000000..5966ee6
--- /dev/null
+++ b/searx/static/themes/oscar/less/logicodev/variables.less
@@ -0,0 +1,13 @@
+@black: #29314D;
+@gray: #A4A4A4;
+@dim-gray: #F6F9FA;
+@dark-gray: #666;
+@middle-gray: #F5F5F5;
+@mild-gray: #DDD;
+@blue: #0088CC;
+@red: #F35E77;
+@violet: #684898;
+@dark-green: #069025;
+@green: #2ecc71;
+@light-green: #01D7D4;
+@orange: #FFA92F;
diff --git a/searx/static/themes/oscar/less/pointhi/advanced.less b/searx/static/themes/oscar/less/pointhi/advanced.less
new file mode 100644
index 0000000..23bfdb0
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/advanced.less
@@ -0,0 +1,49 @@
+#advanced-search-container {
+ display: none;
+ text-align: center;
+ margin-bottom: 1rem;
+ clear: both;
+
+ label, .input-group-addon {
+ font-size: 1.3rem;
+ font-weight:normal;
+ background-color: white;
+ border: #DDD 1px solid;
+ border-right: none;
+ color: #333;
+ padding-bottom: 0.8rem;
+ padding-left: 1.2rem;
+ padding-right: 1.2rem;
+ }
+
+ label:last-child, .input-group-addon:last-child {
+ border-right: #DDD 1px solid;
+ }
+
+ input[type="radio"] {
+ display: none;
+ }
+
+ input[type="radio"]:checked + label {
+ color: black;
+ font-weight: bold;
+ background-color: #EEE;
+ }
+}
+
+#check-advanced {
+ display: none;
+}
+
+#check-advanced:checked ~ #advanced-search-container {
+ display: block;
+}
+
+.advanced {
+ padding: 0;
+ margin-top: 0.3rem;
+ text-align: right;
+ label, select {
+ cursor: pointer;
+ }
+}
diff --git a/searx/static/themes/oscar/less/pointhi/checkbox.less b/searx/static/themes/oscar/less/pointhi/checkbox.less
new file mode 100644
index 0000000..6428b36
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/checkbox.less
@@ -0,0 +1,9 @@
+// Hide element if checkbox is checked
+input[type=checkbox]:checked + .label_hide_if_checked, input[type=checkbox]:checked + .label_hide_if_not_checked + .label_hide_if_checked {
+ display:none;
+}
+
+// Hide element if checkbox is not checked
+input[type=checkbox]:not(:checked) + .label_hide_if_not_checked, input[type=checkbox]:not(:checked) + .label_hide_if_checked + .label_hide_if_not_checked {
+ display:none;
+}
diff --git a/searx/static/themes/oscar/less/pointhi/code.less b/searx/static/themes/oscar/less/pointhi/code.less
new file mode 100644
index 0000000..90a2cd6
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/code.less
@@ -0,0 +1,79 @@
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #f8f8f8; }
+.highlight .c { color: #408080; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #008000; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
+.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #888888 } /* Generic.Output */
+.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0044DD } /* Generic.Traceback */
+.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #008000 } /* Keyword.Pseudo */
+.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #B00040 } /* Keyword.Type */
+.highlight .m { color: #666666 } /* Literal.Number */
+.highlight .s { color: #BA2121 } /* Literal.String */
+.highlight .na { color: #7D9029 } /* Name.Attribute */
+.highlight .nb { color: #008000 } /* Name.Builtin */
+.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+.highlight .no { color: #880000 } /* Name.Constant */
+.highlight .nd { color: #AA22FF } /* Name.Decorator */
+.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #0000FF } /* Name.Function */
+.highlight .nl { color: #A0A000 } /* Name.Label */
+.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #19177C } /* Name.Variable */
+.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #666666 } /* Literal.Number.Float */
+.highlight .mh { color: #666666 } /* Literal.Number.Hex */
+.highlight .mi { color: #666666 } /* Literal.Number.Integer */
+.highlight .mo { color: #666666 } /* Literal.Number.Oct */
+.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
+.highlight .sc { color: #BA2121 } /* Literal.String.Char */
+.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
+.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
+.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.highlight .sx { color: #008000 } /* Literal.String.Other */
+.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
+.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
+.highlight .ss { color: #19177C } /* Literal.String.Symbol */
+.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #19177C } /* Name.Variable.Class */
+.highlight .vg { color: #19177C } /* Name.Variable.Global */
+.highlight .vi { color: #19177C } /* Name.Variable.Instance */
+.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
+
+.highlight .lineno {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: default;
+
+ &::selection {
+ background: transparent; /* WebKit/Blink Browsers */
+ }
+ &::-moz-selection {
+ background: transparent; /* Gecko Browsers */
+ }
+}
diff --git a/searx/static/themes/oscar/less/pointhi/cursor.less b/searx/static/themes/oscar/less/pointhi/cursor.less
new file mode 100644
index 0000000..cbc1ea6
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/cursor.less
@@ -0,0 +1,8 @@
+// display cursor
+.cursor-text {
+ cursor: text !important;
+}
+
+.cursor-pointer {
+ cursor: pointer !important;
+}
diff --git a/searx/static/themes/oscar/less/pointhi/footer.less b/searx/static/themes/oscar/less/pointhi/footer.less
new file mode 100644
index 0000000..0b25e73
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/footer.less
@@ -0,0 +1,19 @@
+// Sticky footer styles
+
+html {
+ position: relative;
+ min-height: 100%;
+}
+
+body {
+ /* Margin bottom by footer height */
+ margin-bottom: 80px;
+}
+
+.footer {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ /* Set the fixed height of the footer here */
+ height: 60px;
+}
diff --git a/searx/static/themes/oscar/less/pointhi/infobox.less b/searx/static/themes/oscar/less/pointhi/infobox.less
new file mode 100644
index 0000000..41375f2
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/infobox.less
@@ -0,0 +1,11 @@
+.infobox {
+ .infobox_part {
+ margin-bottom: 20px;
+ word-wrap: break-word;
+ table-layout: fixed;
+ }
+
+ .infobox_part:last-child {
+ margin-bottom: 0;
+ }
+}
diff --git a/searx/static/themes/oscar/less/pointhi/navbar.less b/searx/static/themes/oscar/less/pointhi/navbar.less
new file mode 100644
index 0000000..a057f82
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/navbar.less
@@ -0,0 +1,20 @@
+.searx-navbar {
+ background: #eee;
+ color: #aaa;
+ height: 2.3rem;
+ font-size: 1.3rem;
+ line-height: 1.3rem;
+ padding: 0.5rem;
+ font-weight: bold;
+ margin-bottom: 1.3rem;
+
+ a, a:hover {
+ margin-right: 2.0rem;
+ text-decoration: none;
+ }
+
+ .instance a {
+ color: #444;
+ margin-left: 2.0rem;
+ }
+}
diff --git a/searx/static/themes/oscar/less/pointhi/onoff.less b/searx/static/themes/oscar/less/pointhi/onoff.less
new file mode 100644
index 0000000..72b289a
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/onoff.less
@@ -0,0 +1,57 @@
+.onoff-checkbox {
+ width:15%;
+}
+.onoffswitch {
+ position: relative;
+ width: 110px;
+ -webkit-user-select:none;
+ -moz-user-select:none;
+ -ms-user-select: none;
+}
+.onoffswitch-checkbox {
+ display: none;
+}
+.onoffswitch-label {
+ display: block;
+ overflow: hidden;
+ cursor: pointer;
+ border: 2px solid #FFFFFF !important;
+ border-radius: 50px !important;
+}
+.onoffswitch-inner {
+ display: block;
+ transition: margin 0.3s ease-in 0s;
+}
+
+.onoffswitch-inner:before, .onoffswitch-inner:after {
+ display: block;
+ float: left;
+ width: 50%;
+ height: 30px;
+ padding: 0;
+ line-height: 40px;
+ font-size: 20px;
+ box-sizing: border-box;
+ content: "";
+ background-color: #EEEEEE;
+}
+
+.onoffswitch-switch {
+ display: block;
+ width: 37px;
+ background-color: #00CC00;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ right: 0px;
+ border: 2px solid #FFFFFF !important;
+ border-radius: 50px !important;
+ transition: all 0.3s ease-in 0s;
+}
+.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
+ margin-right: 0;
+}
+.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
+ right: 71px;
+ background-color: #A1A1A1;
+}
diff --git a/searx/static/themes/oscar/less/pointhi/oscar.less b/searx/static/themes/oscar/less/pointhi/oscar.less
new file mode 100644
index 0000000..4e2fee1
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/oscar.less
@@ -0,0 +1,19 @@
+@import "footer.less";
+
+@import "checkbox.less";
+
+@import "onoff.less";
+
+@import "results.less";
+
+@import "infobox.less";
+
+@import "search.less";
+
+@import "advanced.less";
+
+@import "cursor.less";
+
+@import "code.less";
+
+@import "navbar.less";
diff --git a/searx/static/themes/oscar/less/pointhi/results.less b/searx/static/themes/oscar/less/pointhi/results.less
new file mode 100644
index 0000000..beea353
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/results.less
@@ -0,0 +1,101 @@
+
+.result_header {
+ margin-bottom:5px;
+ margin-top:20px;
+
+ .favicon {
+ margin-bottom:-3px;
+ }
+
+ a {
+ vertical-align: bottom;
+
+ .highlight {
+ font-weight:bold;
+ }
+ }
+}
+
+.result-content {
+ margin-top: 5px;
+ word-wrap: break-word;
+
+ .highlight {
+ font-weight:bold;
+ }
+}
+
+// default formating of results
+.result-default {
+ clear: both;
+}
+
+// image formating of results
+.result-images {
+ float: left !important;
+}
+
+.img-thumbnail {
+ margin: 5px;
+ max-height: 128px;
+ min-height: 128px;
+}
+
+// video formating of results
+.result-videos {
+ clear: both;
+}
+
+// torrent formating of results
+.result-torrents {
+ clear: both;
+}
+
+// map formating of results
+.result-map {
+ clear: both;
+}
+
+// code formating of results
+.result-code {
+ clear: both;
+}
+
+// suggestion
+.suggestion_item {
+ margin: 2px 5px;
+}
+
+// download result
+.result_download {
+ margin-right: 5px;
+}
+
+// page forward, backward
+#pagination {
+ margin-top: 30px;
+ padding-bottom: 50px;
+}
+
+.label-default {
+ color: #AAA;
+ background: #FFF;
+}
+
+.result .text-muted small {
+ word-wrap: break-word;
+}
+
+.modal-wrapper {
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+}
+
+.modal-wrapper {
+ background-clip: padding-box;
+ background-color: #fff;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ outline: 0 none;
+ position: relative;
+}
diff --git a/searx/static/themes/oscar/less/pointhi/search.less b/searx/static/themes/oscar/less/pointhi/search.less
new file mode 100644
index 0000000..cea6799
--- /dev/null
+++ b/searx/static/themes/oscar/less/pointhi/search.less
@@ -0,0 +1,32 @@
+.search_categories, #categories {
+ text-transform: capitalize;
+ margin-bottom: 1.5rem;
+ margin-top: 1.5rem;
+ display: flex;
+ flex-wrap: wrap;
+ align-content: stretch;
+
+ label, .input-group-addon {
+ flex-grow: 1;
+ flex-basis: auto;
+ font-size: 1.3rem;
+ font-weight: normal;
+ background-color: white;
+ border: #DDD 1px solid;
+ border-right: none;
+ color: #333;
+ padding-bottom: 0.8rem;
+ padding-top: 0.8rem;
+ text-align: center;
+ }
+
+ label:last-child, .input-group-addon:last-child {
+ border-right: #DDD 1px solid;
+ }
+
+ input[type="checkbox"]:checked + label{
+ color: black;
+ font-weight: bold;
+ background-color: #EEE;
+ }
+}
diff --git a/searx/static/themes/oscar/package.json b/searx/static/themes/oscar/package.json
new file mode 100644
index 0000000..7eae9df
--- /dev/null
+++ b/searx/static/themes/oscar/package.json
@@ -0,0 +1,16 @@
+{
+ "devDependencies": {
+ "grunt": "~0.4.5",
+ "grunt-contrib-uglify": "~0.6.0",
+ "grunt-contrib-watch" : "~0.6.1",
+ "grunt-contrib-concat" : "~0.5.0",
+ "grunt-contrib-jshint" : "~0.10.0",
+ "grunt-contrib-less" : "~0.11.0"
+ },
+
+ "scripts": {
+ "build": "npm install && grunt",
+ "start": "grunt watch",
+ "test": "grunt"
+ }
+}
diff --git a/searx/static/themes/pix-art/img/favicon.png b/searx/static/themes/pix-art/img/favicon.png
new file mode 100644
index 0000000..3818d3d
--- /dev/null
+++ b/searx/static/themes/pix-art/img/favicon.png
Binary files differ
diff --git a/searx/static/themes/pix-art/img/preference-icon-pixel.png b/searx/static/themes/pix-art/img/preference-icon-pixel.png
new file mode 100644
index 0000000..424e01e
--- /dev/null
+++ b/searx/static/themes/pix-art/img/preference-icon-pixel.png
Binary files differ
diff --git a/searx/static/themes/pix-art/img/search-icon-pixel.png b/searx/static/themes/pix-art/img/search-icon-pixel.png
new file mode 100644
index 0000000..8235882
--- /dev/null
+++ b/searx/static/themes/pix-art/img/search-icon-pixel.png
Binary files differ
diff --git a/searx/static/themes/pix-art/img/searx-pixel-small.png b/searx/static/themes/pix-art/img/searx-pixel-small.png
new file mode 100644
index 0000000..75b476c
--- /dev/null
+++ b/searx/static/themes/pix-art/img/searx-pixel-small.png
Binary files differ
diff --git a/searx/static/themes/pix-art/img/searx-pixel.png b/searx/static/themes/pix-art/img/searx-pixel.png
new file mode 100644
index 0000000..6aee581
--- /dev/null
+++ b/searx/static/themes/pix-art/img/searx-pixel.png
Binary files differ
diff --git a/searx/static/themes/pix-art/js/searx.js b/searx/static/themes/pix-art/js/searx.js
new file mode 100644
index 0000000..5eb0af9
--- /dev/null
+++ b/searx/static/themes/pix-art/js/searx.js
@@ -0,0 +1,141 @@
+if(searx.autocompleter) {
+ window.addEvent('domready', function() {
+ new Autocompleter.Request.JSON('q', '/autocompleter', {
+ postVar:'q',
+ postData:{
+ 'format': 'json'
+ },
+ ajaxOptions:{
+ timeout: 5 // Correct option?
+ },
+ 'minLength': 4,
+ 'selectMode': false,
+ cache: true,
+ delay: 300
+ });
+ });
+}
+
+(function (w, d) {
+ 'use strict';
+ function addListener(el, type, fn) {
+ if (el.addEventListener) {
+ el.addEventListener(type, fn, false);
+ } else {
+ el.attachEvent('on' + type, fn);
+ }
+ }
+
+ function placeCursorAtEnd() {
+ if (this.setSelectionRange) {
+ var len = this.value.length * 2;
+ this.setSelectionRange(len, len);
+ }
+ }
+
+ addListener(w, 'load', function () {
+ var qinput = d.getElementById('q');
+ if (qinput !== null && qinput.value === "") {
+ addListener(qinput, 'focus', placeCursorAtEnd);
+ qinput.focus();
+ }
+ });
+
+ if (!!('ontouchstart' in window)) {
+ document.getElementsByTagName("html")[0].className += " touch";
+ }
+
+})(window, document);
+
+var xmlHttp
+
+function GetXmlHttpObject(){
+
+ var xmlHttp = null;
+
+ try {
+ // Firefox, Opera 8.0+, Safari
+ xmlHttp = new XMLHttpRequest();
+ }
+ catch (e) {
+ // Internet Explorer
+ try {
+ xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
+ }
+ catch (e){
+ xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+ }
+ }
+ return xmlHttp;
+}
+
+var timer;
+
+// Load more results
+function load_more(query,page){
+
+ xmlHttp = GetXmlHttpObject();
+ clearTimeout(timer);
+
+ if(xmlHttp == null){
+ alert ("Your browser does not support AJAX!");
+ return;
+ }
+
+ favicons[page] = [];
+
+ xmlHttp.onreadystatechange = function(){
+
+ var loader = document.getElementById('load_more');
+
+ // If 4, response OK
+ if (xmlHttp.readyState == 4){
+
+ var res = xmlHttp.responseText;
+
+ clearTimeout(timer);
+ timer = setTimeout(function(){},6000);
+
+ var results = document.getElementById('results_list');
+
+ var newNode = document.createElement('span');
+ newNode.innerHTML = res;
+ results_list.appendChild(newNode);
+
+ var scripts = newNode.getElementsByTagName('script');
+ for (var ix = 0; ix < scripts.length; ix++) {
+ eval(scripts[ix].text);
+ }
+
+ load_images(page);
+ document.getElementById("load_more").onclick = function() { load_more(query, (page+1)); }
+ loader.removeAttribute("disabled");
+
+ } else {
+ loader.disabled = 'disabled';
+ }
+ }
+ var url = "/";
+ var params = "q="+query+"&pageno="+page+"&category_general=1&category_files=1&category_images=1&category_it=1&category_map=1&category_music=1&category_news=1&category_social+media=1&category_videos=1";
+ xmlHttp.open("POST",url,true);
+ xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ xmlHttp.setRequestHeader("Content-length", params.length);
+ xmlHttp.setRequestHeader("Connection", "close");
+ xmlHttp.send(params);
+}
+
+// Load the images on the canvas in the page
+function load_images(page){
+ var arrayLength = favicons[page].length;
+ for (var i = 1; i < arrayLength+1; i++) {
+ var img = new Image();
+ img.setAttribute("i",i)
+ img.onload = function () {
+ var id = 'canvas-'+page+'-'+this.getAttribute("i");
+ var can = document.getElementById(id);
+ var ctx = can.getContext("2d");
+ ctx.drawImage(this, 0, 0, 16, 16);
+ };
+ img.src = favicons[page][i];
+ }
+} \ No newline at end of file
diff --git a/searx/static/themes/pix-art/less/definitions.less b/searx/static/themes/pix-art/less/definitions.less
new file mode 100644
index 0000000..0ac0cc9
--- /dev/null
+++ b/searx/static/themes/pix-art/less/definitions.less
@@ -0,0 +1,119 @@
+/*
+ * searx, A privacy-respecting, hackable metasearch engine
+ *
+ * To change the colors of the site, simple edit this variables
+ */
+
+/// Basic Colors
+
+@color-base: #3498DB;
+@color-base-dark: #2980B9;
+@color-base-light: #ECF0F1;
+@color-highlight: #094089;
+@color-black: #000000;
+
+/// General
+
+@color-font: #444;
+@color-font-light: #888;
+
+@color-red: #C0392B;
+
+@color-url-font: #1a11be;
+@color-url-visited-font: #8E44AD;
+@results-width: 50em;
+
+
+/// Start-Screen
+
+// hmarg
+@color-hmarg-border: @color-base;
+@color-hmarg-font: @color-base;
+@color-hmarg-font-hover: @color-base;
+
+
+/// Search-Input
+
+@color-search-border: @color-base;
+@color-search-background: #FFF;
+@color-search-font: #222;
+
+/// Autocompleter
+
+@color-autocompleter-choices-background: #FFF;
+@color-autocompleter-choices-border: @color-base;
+@color-autocompleter-choices-border-left-right: @color-base;
+@color-autocompleter-choices-border-bottom: @color-base;
+
+@color-autocompleter-choices-font: #444;
+
+/// Answers
+@color-answers-border: @color-base-dark;
+
+// Selected
+@color-autocompleter-selected-background: #444;
+@color-autocompleter-selected-font: #FFF;
+@color-autocompleter-selected-queried-font: #9FCFFF;
+
+/// Categories
+
+@color-categories-item-selected: @color-base;
+@color-categories-item-selected-font: #FFF;
+
+@color-categories-item-border-selected: @color-base-dark;
+@color-categories-item-border-unselected: #E8E7E6;
+@color-categories-item-border-unselected-hover: @color-base;
+
+
+/// Results
+
+@color-suggestions-button-background: @color-base;
+@color-suggestions-button-font: #FFF;
+
+@color-download-button-background: @color-base;
+@color-download-button-font: #FFF;
+
+@color-result-search-background: @color-base-light;
+
+@color-result-definition-border: gray;
+@color-result-torrent-border: lightgray;
+@color-result-top-border: #E8E7E6;
+
+// Link to result
+@color-result-link-font: @color-base-dark;
+@color-result-link-visited-font: @color-url-visited-font;
+
+// Url to result
+@color-result-url-font: @color-red;
+
+// Publish Date
+@color-result-publishdate-font: @color-font-light;
+
+// Images
+@color-result-image-span-background-hover: rgba(0, 0, 0, 0.6);
+@color-result-image-span-font: #FFF;
+
+// Search-URL
+@color-result-search-url-border: #888;
+@color-result-search-url-font: #444;
+
+
+/// Settings
+
+@color-settings-fieldset: @color-base;
+@color-settings-tr-hover: #DDD;
+
+// Labels
+@color-settings-label-allowed-background: #E74C3C;
+@color-settings-label-allowed-font: #FFF;
+
+@color-settings-label-deny-background: #2ECC71;
+@color-settings-label-deny-font: @color-font;
+
+@color-settings-return-background: @color-base;
+@color-settings-return-font: #FFF;
+
+/// Other
+
+@color-engines-font: @color-font-light;
+@color-percentage-div-background: #444;
diff --git a/searx/static/themes/pix-art/less/mixins.less b/searx/static/themes/pix-art/less/mixins.less
new file mode 100644
index 0000000..dbccce6
--- /dev/null
+++ b/searx/static/themes/pix-art/less/mixins.less
@@ -0,0 +1,27 @@
+/*
+ * searx, A privacy-respecting, hackable metasearch engine
+ */
+
+// Mixins
+
+.text-size-adjust (@property: 100%) {
+ -webkit-text-size-adjust: @property;
+ -ms-text-size-adjust: @property;
+ -moz-text-size-adjust: @property;
+ text-size-adjust: @property;
+}
+
+.rounded-corners (@radius: 4px) {
+ -webkit-border-radius: @radius;
+ -moz-border-radius: @radius;
+ border-radius: @radius;
+}
+
+.user-select () {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
diff --git a/searx/static/themes/pix-art/less/search.less b/searx/static/themes/pix-art/less/search.less
new file mode 100644
index 0000000..f5ac33e
--- /dev/null
+++ b/searx/static/themes/pix-art/less/search.less
@@ -0,0 +1,57 @@
+/*
+ * searx, A privacy-respecting, hackable metasearch engine
+ */
+
+.search {
+ padding: 0;
+ margin: 0;
+}
+
+#search_wrapper {
+ position: relative;
+ width: @results-width;
+ padding: 10px;
+}
+
+.center #search_wrapper {
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.q {
+ background: none repeat scroll 0 0 @color-search-background;
+ border: 1px solid @color-search-border;
+ color: @color-search-font;
+ font-size: 16px;
+ font-family: "Courier New", Courier, monospace;
+ height: 28px;
+ margin: 0;
+ outline: medium none;
+ padding: 2px;
+ padding-left: 8px;
+ padding-right: 0px !important;
+ width: 100%;
+ z-index: 2;
+}
+
+#search_submit {
+ position: absolute;
+ top: 15px;
+ right: 5px;
+ padding: 0;
+ border: 0;
+ background: url('../img/search-icon-pixel.png') no-repeat;
+ background-size: 24px 24px;
+ opacity: 0.8;
+ width: 24px;
+ height: 24px;
+ font-size: 0;
+}
+
+@media screen and (max-width: @results-width) {
+ #search_wrapper {
+ width: 90%;
+ clear:both;
+ overflow: hidden
+ }
+}
diff --git a/searx/static/themes/pix-art/less/style.less b/searx/static/themes/pix-art/less/style.less
new file mode 100644
index 0000000..a2088e9
--- /dev/null
+++ b/searx/static/themes/pix-art/less/style.less
@@ -0,0 +1,451 @@
+/*
+ * searx, A privacy-respecting, hackable metasearch engine
+ *
+ * To convert "style.less" to "style.css" run: $make styles
+ */
+
+@import "definitions.less";
+
+@import "mixins.less";
+
+
+// Main LESS-Code
+
+html {
+ font-family: "Courier New", Courier, monospace;
+ font-size: 0.9em;
+ .text-size-adjust;
+ color: @color-font;
+ padding: 0;
+ margin: 0;
+}
+
+body, #container {
+ padding: 0;
+ margin: 0;
+}
+
+canvas {
+ image-rendering: optimizeSpeed;
+ image-rendering: -moz-crisp-edges;
+ image-rendering: -webkit-optimize-contrast;
+ image-rendering: optimize-contrast;
+ image-rendering: pixelated;
+ -ms-interpolation-mode: nearest-neighbor;
+ width:32px;
+ height:32px;
+}
+
+#container {
+ width: 100%;
+ position: absolute;
+ top: 0;
+}
+
+// Search-Field
+
+@import "search.less";
+
+.row {
+ max-width: 800px;
+ margin: 20px auto;
+ text-align: justify;
+
+ h1 {
+ font-size: 3em;
+ margin-top: 50px;
+ }
+
+ p {
+ padding: 0 10px;
+ max-width: 700px;
+ }
+
+ h3,ul {
+ margin: 4px 8px;
+ }
+}
+
+.hmarg {
+ margin: 0 20px;
+ border: 1px solid @color-hmarg-border;
+ padding: 4px 10px;
+}
+
+a {
+ &:link.hmarg {
+ color: @color-hmarg-font;
+ }
+
+ &:visited.hmarg {
+ color: @color-hmarg-font;
+ }
+
+ &:active.hmarg {
+ color: @color-hmarg-font-hover;
+ }
+
+ &:hover.hmarg {
+ color: @color-hmarg-font-hover;
+ }
+}
+
+.top_margin {
+ margin-top: 60px;
+}
+
+.center {
+ text-align: center;
+}
+
+h1 {
+ font-size: 5em;
+}
+
+div.title {
+ background: url('../img/searx-pixel.png') no-repeat;
+ width: 100%;
+ min-height: 80px;
+ background-position: center;
+
+ h1 {
+ visibility: hidden;
+ }
+}
+
+input[type="button"],
+input[type="submit"] {
+ font-family: "Courier New", Courier, monospace;
+ padding: 4px 12px;
+ margin: 2px 4px;
+ display: inline-block;
+ background: @color-download-button-background;
+ color: @color-download-button-font;
+ .rounded-corners;
+ border: 0;
+ cursor: pointer;
+}
+
+input[type="button"]:disabled {
+ cursor: progress;
+}
+
+input[type="checkbox"] {
+ visibility: hidden;
+}
+
+fieldset {
+ margin: 8px;
+ border: 1px solid @color-settings-fieldset;
+}
+
+#logo {
+ position: absolute;
+ top: 13px;
+ left: 10px;
+}
+
+#categories {
+ margin: 0 10px;
+ .user-select;
+}
+
+.checkbox_container {
+ display: inline-block;
+ position: relative;
+ margin: 0 3px;
+ padding: 0px;
+
+ input {
+ display: none;
+ }
+}
+
+.checkbox_container label, .engine_checkbox label {
+ cursor: pointer;
+ padding: 4px 10px;
+ margin: 0;
+ display: block;
+ text-transform: capitalize;
+ .user-select;
+}
+
+.checkbox_container input[type="checkbox"]:checked + label {
+ background: @color-categories-item-selected;
+ color: @color-categories-item-selected-font;
+}
+
+.engine_checkbox {
+ padding: 4px;
+}
+
+label {
+ &.allow {
+ background: @color-settings-label-allowed-background;
+ padding: 4px 8px;
+ color: @color-settings-label-allowed-font;
+ display: none;
+ }
+
+ &.deny {
+ background: @color-settings-label-deny-background;
+ padding: 4px 8px;
+ color: @color-settings-label-deny-font;
+ display: inline;
+ }
+}
+
+.engine_checkbox input[type="checkbox"]:checked + label {
+ &:nth-child(2) + label {
+ display: none;
+ }
+
+ &.allow {
+ display: inline;
+ }
+}
+
+a {
+ text-decoration: none;
+ color: @color-url-font;
+
+ &:visited {
+ color: @color-url-visited-font;
+ }
+}
+
+.engines {
+ color: @color-engines-font;
+}
+
+.small_font {
+ font-size: 0.8em;
+}
+
+.small p {
+ margin: 2px 0;
+}
+
+.right {
+ float: right;
+}
+
+.invisible {
+ display: none;
+}
+
+.left {
+ float: left;
+}
+
+.highlight {
+ color: @color-highlight;
+}
+
+.content .highlight {
+ color: @color-black;
+}
+
+.percentage {
+ position: relative;
+ width: 300px;
+
+ div {
+ background: @color-percentage-div-background;
+ }
+}
+
+table {
+ width: 100%;
+}
+
+td {
+ padding: 0 4px;
+}
+
+tr {
+ &:hover {
+ background: @color-settings-tr-hover;
+ }
+}
+
+#results {
+ margin: auto;
+ padding: 0;
+ width: @results-width;
+ margin-bottom: 20px;
+}
+
+#search_url {
+ margin-top: 8px;
+
+ input {
+ border: 1px solid @color-result-search-url-border;
+ padding: 4px;
+ color: @color-result-search-url-font;
+ width: 14em;
+ display: block;
+ margin: 4px;
+ font-size: 0.8em;
+ }
+}
+
+#preferences {
+ top: 10px;
+ padding: 0;
+ border: 0;
+ background: url('../img/preference-icon-pixel.png') no-repeat;
+ background-size: 28px 28px;
+ opacity: 0.8;
+ width: 28px;
+ height: 30px;
+ display: block;
+
+ * {
+ display: none;
+ }
+}
+
+#pagination {
+ clear: both;
+ text-align: center;
+ br {
+ clear: both;
+ }
+}
+
+#apis {
+ margin-top: 8px;
+ clear: both;
+}
+
+#categories_container {
+ position: relative;
+}
+
+@media screen and (max-width: @results-width) {
+
+ #results {
+ margin: auto;
+ padding: 0;
+ width: 90%;
+ }
+
+ .checkbox_container {
+ display: block;
+ width: 90%;
+ //float: left;
+
+ label {
+ border-bottom: 0;
+ }
+ }
+
+ .preferences_container {
+ display: none;
+ postion: fixed !important;
+ top: 100px;
+ right: 0px;
+ }
+
+}
+
+@media screen and (max-width: 75em) {
+
+ div.title {
+
+ h1 {
+ font-size: 1em;
+ }
+ }
+
+ html.touch #categories {
+ width: 95%;
+ height: 30px;
+ text-align: left;
+ overflow-x: scroll;
+ overflow-y: hidden;
+ -webkit-overflow-scrolling: touch;
+
+ #categories_container {
+ width: 1000px;
+ width: -moz-max-content;
+ width: -webkit-max-content;
+ width: max-content;
+
+ .checkbox_container {
+ display: inline-block;
+ width: auto;
+ }
+ }
+ }
+
+ #categories {
+ font-size: 90%;
+ clear: both;
+
+ .checkbox_container {
+ margin-top: 2px;
+ margin: auto;
+ }
+ }
+
+ #categories {
+ font-size: 90%;
+ clear: both;
+
+ .checkbox_container {
+ margin-top: 2px;
+ margin: auto;
+ }
+ }
+
+ #apis {
+ display: none;
+ }
+
+ #search_url {
+ display: none;
+ }
+
+ #logo {
+ display: none;
+ }
+}
+
+.favicon {
+ float: left;
+ margin-right: 4px;
+ margin-top: 2px;
+}
+
+.preferences_back {
+ background: none repeat scroll 0 0 @color-settings-return-background;
+ border: 0 none;
+ .rounded-corners;
+ cursor: pointer;
+ display: inline-block;
+ margin: 2px 4px;
+ padding: 4px 6px;
+
+ a {
+ color: @color-settings-return-font;
+ }
+}
+
+.hidden {
+ opacity: 0;
+ overflow: hidden;
+ font-size: 0.8em;
+ position: absolute;
+ bottom: -20px;
+ width: 100%;
+ text-position: center;
+ background: white;
+ transition: opacity 1s ease;
+}
+
+#categories_container:hover .hidden {
+ transition: opacity 1s ease;
+ opacity: 0.8;
+}
diff --git a/searx/templates/__common__/about.html b/searx/templates/__common__/about.html
new file mode 100644
index 0000000..d8afab7
--- /dev/null
+++ b/searx/templates/__common__/about.html
@@ -0,0 +1,62 @@
+<div{% if rtl %} dir="ltr"{% endif %}>
+ <h1>About <a href="{{ url_for('index') }}">searx</a></h1>
+
+ <p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>, aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a> while not storing information about its users.
+ </p>
+ <h2>Why use searx?</h2>
+ <ul>
+ <li>searx may not offer you as personalised results as Google, but it doesn't generate a profile about you</li>
+ <li>searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you</li>
+ <li>searx is free software, the code is 100% open and you can help to make it better. See more on <a href="https://github.com/asciimoo/searx">github</a></li>
+ </ul>
+ <p>If you do care about privacy, want to be a conscious user, or otherwise believe
+ in digital freedom, make searx your default search engine or run it on your own server</p>
+
+<h2>Technical details - How does it work?</h2>
+
+<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
+inspired by the <a href="https://beniz.github.io/seeks/">seeks project</a>.<br />
+It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, searx uses the search bar to perform GET requests.<br />
+Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
+</p>
+
+<h2>How can I make it my own?</h2>
+
+<p>Searx appreciates your concern regarding logs, so take the <a href="https://github.com/asciimoo/searx">code</a> and run it yourself! <br />Add your Searx to this <a href="https://github.com/asciimoo/searx/wiki/Searx-instances">list</a> to help other people reclaim their privacy and make the Internet freer!
+<br />The more decentralized the Internet is, the more freedom we have!</p>
+
+
+<h2>More about searx</h2>
+
+<ul>
+ <li><a href="https://github.com/asciimoo/searx">github</a></li>
+ <li><a href="https://www.ohloh.net/p/searx/">ohloh</a></li>
+ <li><a href="https://twitter.com/Searx_engine">twitter</a></li>
+ <li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
+ <li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
+</ul>
+
+
+<hr />
+
+<h2 id="faq">FAQ</h2>
+
+<h3>How to add to firefox?</h3>
+<p><a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a> searx as a search engine on any version of Firefox! (javascript required)</p>
+
+<h2 id="dev_faq">Developer FAQ</h2>
+
+<h3>New engines?</h3>
+<ul>
+ <li>Edit your <a href="https://raw.github.com/asciimoo/searx/master/searx/settings.yml">settings.yml</a></li>
+ <li>Create your custom engine module, check the <a href="https://github.com/asciimoo/searx/blob/master/examples/basic_engine.py">example engine</a></li>
+</ul>
+<p>Don't forget to restart searx after config edit!</p>
+
+<h3>Installation/WSGI support?</h3>
+<p>See the <a href="https://github.com/asciimoo/searx/wiki/Installation">installation and setup</a> wiki page</p>
+
+<h3>How to debug engines?</h3>
+<p><a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.</p>
+
+</div>
diff --git a/searx/templates/__common__/opensearch.xml b/searx/templates/__common__/opensearch.xml
new file mode 100644
index 0000000..15d3eb7
--- /dev/null
+++ b/searx/templates/__common__/opensearch.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
+ <ShortName>{{ instance_name }}</ShortName>
+ <Description>a privacy-respecting, hackable metasearch engine</Description>
+ <InputEncoding>UTF-8</InputEncoding>
+ <Image>{{ urljoin(host, url_for('static', filename='img/favicon.png')) }}</Image>
+ <LongName>searx metasearch</LongName>
+ {% if opensearch_method == 'get' %}
+ <Url type="text/html" method="get" template="{{ host }}search?q={searchTerms}"/>
+ {% if autocomplete %}
+ <Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
+ <Param name="format" value="x-suggestions" />
+ <Param name="q" value="{searchTerms}" />
+ </Url>
+ {% endif %}
+ {% else %}
+ <Url type="text/html" method="post" template="{{ host }}">
+ <Param name="q" value="{searchTerms}" />
+ </Url>
+ {% if autocomplete %}
+ <!-- TODO, POST REQUEST doesn't work -->
+ <Url type="application/x-suggestions+json" method="get" template="{{ host }}autocompleter">
+ <Param name="format" value="x-suggestions" />
+ <Param name="q" value="{searchTerms}" />
+ </Url>
+ {% endif %}
+ {% endif %}
+</OpenSearchDescription>
diff --git a/searx/templates/__common__/opensearch_response_rss.xml b/searx/templates/__common__/opensearch_response_rss.xml
new file mode 100644
index 0000000..32c42e7
--- /dev/null
+++ b/searx/templates/__common__/opensearch_response_rss.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rss version="2.0"
+ xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+ <channel>
+ <title>Searx search: {{ q|e }}</title>
+ <link>{{ base_url }}?q={{ q|e }}</link>
+ <description>Search results for "{{ q|e }}" - searx</description>
+ <opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults>
+ <opensearch:startIndex>1</opensearch:startIndex>
+ <opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage>
+ <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/>
+ <opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
+ {% if error_message %}
+ <item>
+ <title>Error</title>
+ <description>{{ error_message|e }}</description>
+ </item>
+ {% endif %}
+ {% for r in results %}
+ <item>
+ <title>{{ r.title }}</title>
+ <link>{{ r.url }}</link>
+ <description>{{ r.content }}</description>
+ {% if r.pubdate %}<pubDate>{{ r.pubdate }}</pubDate>{% endif %}
+ </item>
+ {% endfor %}
+ </channel>
+</rss>
diff --git a/searx/templates/courgette/404.html b/searx/templates/courgette/404.html
new file mode 100644
index 0000000..9e3b8ac
--- /dev/null
+++ b/searx/templates/courgette/404.html
@@ -0,0 +1,9 @@
+{% extends "courgette/base.html" %}
+{% block content %}
+<div class="center">
+ <h1>{{ _('Page not found') }}</h1>
+ {% autoescape false %}
+ <p>{{ _('Go to %(search_page)s.', search_page=unicode('<a href="{}">{}</a>').format(url_for('index'), _('search page'))) }}</p>
+ {% endautoescape %}
+</div>
+{% endblock %}
diff --git a/searx/templates/courgette/about.html b/searx/templates/courgette/about.html
new file mode 100644
index 0000000..08948ee
--- /dev/null
+++ b/searx/templates/courgette/about.html
@@ -0,0 +1,5 @@
+{% extends 'courgette/base.html' %}
+{% block content %}
+{% include 'courgette/github_ribbon.html' %}
+{% include '__common__/about.html' %}
+{% endblock %}
diff --git a/searx/templates/courgette/base.html b/searx/templates/courgette/base.html
new file mode 100644
index 0000000..8e27258
--- /dev/null
+++ b/searx/templates/courgette/base.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}>
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
+ <meta name="keywords" content="searx, search, search engine, metasearch, meta search" />
+ <meta name="generator" content="searx/{{ searx_version }}">
+ <meta name="referrer" content="no-referrer">
+ <meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=1" />
+ <title>{% block title %}{% endblock %}searx</title>
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" type="text/css" media="screen" />
+ {% if rtl %}
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style-rtl.css') }}" type="text/css" media="screen" />
+ {% endif %}
+ {% if cookies['courgette-color'] %}
+ <style type="text/css">
+ {% include 'courgette/color.css' %}
+ </style>
+ {% endif %}
+ <link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.png') }}?v=2" />
+ {% block styles %}
+ {% endblock %}
+ {% block meta %}{% endblock %}
+ {% block head %}
+ <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
+ {% endblock %}
+ <script type="text/javascript">
+ searx = {};
+ searx.autocompleter = {% if autocomplete %}true{% else %}false{% endif %};
+ </script>
+ </head>
+ <body>
+ <div id="container">
+ {% block content %}
+ {% endblock %}
+ {% if autocomplete %}
+ <script src="{{ url_for('static', filename='js/mootools-core-1.4.5-min.js') }}" ></script>
+ <script src="{{ url_for('static', filename='js/mootools-autocompleter-1.1.2-min.js') }}" ></script>
+ {% endif %}
+ <script src="{{ url_for('static', filename='js/searx.js') }}" ></script>
+ </div>
+ </body>
+</html>
diff --git a/searx/templates/courgette/categories.html b/searx/templates/courgette/categories.html
new file mode 100644
index 0000000..b8d6a75
--- /dev/null
+++ b/searx/templates/courgette/categories.html
@@ -0,0 +1,9 @@
+<div id="categories">
+ <div id="categories_container">
+ {% for category in categories %}
+ <div class="checkbox_container">
+ <input type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} /><label for="checkbox_{{ category|replace(' ', '_') }}">{{ _(category) }}</label>
+ </div>
+ {% endfor %}
+ </div>
+</div> \ No newline at end of file
diff --git a/searx/templates/courgette/color.css b/searx/templates/courgette/color.css
new file mode 100644
index 0000000..3e0d730
--- /dev/null
+++ b/searx/templates/courgette/color.css
@@ -0,0 +1,34 @@
+ .autocompleter-choices li:hover,
+.checkbox_container label:hover,
+.checkbox_container input[type="checkbox"]:checked + label,
+#sidebar,
+#suggestions input[type="submit"]:hover,
+#suggestions input[type="submit"]:focus,
+input[type="submit"],
+.engine_checkbox label,
+.engine_checkbox .deny,
+#search_submit{
+ background-color: {{ cookies['courgette-color'].split()[0] }};
+}
+
+.result_title a,
+.row a,
+.title h1{
+ color: {{ cookies['courgette-color'].split()[0] }};
+}
+
+#answers {
+ border-color: {{ cookies['courgette-color'].split()[0] }};
+}
+
+#search_submit:hover,
+#search_submit:focus,
+#sidebar input[type="submit"]:hover,
+#sidebar input[type="submit"]:focus {
+ background-color: {{ cookies['courgette-color'].split()[1] }};
+}
+
+input[type="submit"]:hover,
+input[type="submit"]:focus {
+ background: {{ cookies['courgette-color'].split()[1] }};
+} \ No newline at end of file
diff --git a/searx/templates/courgette/github_ribbon.html b/searx/templates/courgette/github_ribbon.html
new file mode 100644
index 0000000..67c6e67
--- /dev/null
+++ b/searx/templates/courgette/github_ribbon.html
@@ -0,0 +1,3 @@
+<a href="https://github.com/asciimoo/searx" class="github">
+ <img style="position: absolute; top: 0; right: 0; border: 0;" src="{{ url_for('static', filename='img/github_ribbon.png') }}" alt="Fork me on GitHub" class="github"/>
+</a> \ No newline at end of file
diff --git a/searx/templates/courgette/index.html b/searx/templates/courgette/index.html
new file mode 100644
index 0000000..0d34e1c
--- /dev/null
+++ b/searx/templates/courgette/index.html
@@ -0,0 +1,17 @@
+{% extends "courgette/base.html" %}
+{% block content %}
+{% include 'courgette/github_ribbon.html' %}
+<div class="center">
+ <div class="title"><h1>searx</h1></div>
+ {% include 'courgette/search.html' %}
+ <p class="top_margin">
+ {% if rtl %}
+ <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>
+ {% endif %}
+ <a href="{{ url_for('about') }}" class="hmarg">{{ _('about') }}</a>
+ {% if not rtl %}
+ <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>
+ {% endif %}
+ </p>
+</div>
+{% endblock %} \ No newline at end of file
diff --git a/searx/templates/courgette/preferences.html b/searx/templates/courgette/preferences.html
new file mode 100644
index 0000000..56a6e02
--- /dev/null
+++ b/searx/templates/courgette/preferences.html
@@ -0,0 +1,132 @@
+{% extends "courgette/base.html" %}
+{% block head %} {% endblock %}
+{% block content %}
+<div class="row">
+ <h2>{{ _('Preferences') }}</h2>
+
+ <form method="post" action="{{ url_for('preferences') }}" id="search_form">
+ <fieldset>
+ <legend>{{ _('Default categories') }}</legend>
+ {% include 'courgette/categories.html' %}
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Search language') }}</legend>
+ <p>
+ <select name='language'>
+ <option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Default language') }}</option>
+ {% for lang_id,lang_name,country_name,english_name in language_codes | sort(attribute=1) %}
+ <option value="{{ lang_id }}" {% if lang_id == current_language %}selected="selected"{% endif %}>{{ lang_name }} {% if country_name %}({{ country_name }}) {% endif %}- {{ lang_id }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Interface language') }}</legend>
+ <p>
+ <select name='locale'>
+ {% for locale_id,locale_name in locales.items() | sort %}
+ <option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Autocomplete') }}</legend>
+ <p>
+ <select name="autocomplete">
+ <option value=""> - </option>
+ {% for backend in autocomplete_backends %}
+ <option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Image proxy') }}</legend>
+ <p>
+ <select name='image_proxy'>
+ <option value="1" {% if image_proxy %}selected="selected"{% endif %}>{{ _('Enabled') }}</option>
+ <option value="" {% if not image_proxy %}selected="selected"{% endif %}>{{ _('Disabled') }}</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Method') }}</legend>
+ <p>
+ <select name='method'>
+ <option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
+ <option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('SafeSearch') }}</legend>
+ <p>
+ <select name='safesearch'>
+ <option value="2" {% if safesearch == '2' %}selected="selected"{% endif %}>{{ _('Strict') }}</option>
+ <option value="1" {% if safesearch == '1' %}selected="selected"{% endif %}>{{ _('Moderate') }}</option>
+ <option value="0" {% if safesearch == '0' %}selected="selected"{% endif %}>{{ _('None') }}</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Themes') }}</legend>
+ <p>
+ <select name="theme">
+ {% for name in themes %}
+ <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Color') }}</legend>
+ <p>
+ <select name="courgette-color">
+ <option value="#3498DB #0665A2" {% if cookies['courgette-color'] and cookies['courgette-color'] == '#3498DB #0665A2' %}selected="selected"{% endif %}>{{ _('Blue (default)') }}</option>
+ <option value="#6F5499 #563D7C" {% if cookies['courgette-color'] and cookies['courgette-color'] == '#6F5499 #563D7C' %}selected="selected"{% endif %}>{{ _('Violet') }}</option>
+ <option value="#5CB85C #449D44" {% if cookies['courgette-color'] and cookies['courgette-color'] == '#5CB85C #449D44' %}selected="selected"{% endif %}>{{ _('Green') }}</option>
+ <option value="#5BC0DE #31B0D5" {% if cookies['courgette-color'] and cookies['courgette-color'] == '#5BC0DE #31B0D5' %}selected="selected"{% endif %}>{{ _('Cyan') }}</option>
+ <option value="#F0AD4E #EC971F" {% if cookies['courgette-color'] and cookies['courgette-color'] == '#F0AD4E #EC971F' %}selected="selected"{% endif %}>{{ _('Orange') }}</option>
+ <option value="#D9534F #C9302C" {% if cookies['courgette-color'] and cookies['courgette-color'] == '#D9534F #C9302C' %}selected="selected"{% endif %}>{{ _('Red') }}</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Currently used search engines') }}</legend>
+
+ <table class="engine-table">
+ <tr>
+ <th>{{ _('Engine name') }}</th>
+ <th>{{ _('Category') }}</th>
+ <th>{{ _('Allow') }} / {{ _('Block') }}</th>
+ </tr>
+ {% for categ in all_categories %}
+ {% for search_engine in engines_by_category[categ] %}
+
+ {% if not search_engine.private %}
+ <tr>
+ <td>{{ search_engine.name }} ({{ shortcuts[search_engine.name] }})&lrm;</td>
+ <td>{{ _(categ) }}</td>
+ <td class="engine_checkbox">
+ <input type="checkbox" id="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}" name="engine_{{ search_engine.name }}__{{ categ }}"{% if (search_engine.name, categ) in disabled_engines %} checked="checked"{% endif %} />
+ <label class="allow" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Allow') }}</label>
+ <label class="deny" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Block') }}</label>
+ </td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ </table>
+ </fieldset>
+ <p class="small_font">{{ _('These settings are stored in your cookies, this allows us not to store this data about you.') }}
+ <br />
+ {{ _("These cookies serve your sole convenience, we don't use these cookies to track you.") }}
+ </p>
+
+ <input type="submit" value="{{ _('save') }}" />
+ <div class="right preferences_back"><a href="{{ url_for('clear_cookies') }}">{{ _('Reset defaults') }}</a></div>
+ <div class="right preferences_back"><a href="{{ url_for('index') }}">{{ _('back') }}</a></div>
+ </form>
+</div>
+{% endblock %}
diff --git a/searx/templates/courgette/result_templates/code.html b/searx/templates/courgette/result_templates/code.html
new file mode 100644
index 0000000..953617e
--- /dev/null
+++ b/searx/templates/courgette/result_templates/code.html
@@ -0,0 +1,11 @@
+<div class="result {{ result.class }}">
+ <h3 class="result_title">{% if result['favicon'] %}<img width="14" height="14" class="favicon" src="static/{{theme}}/img/icon_{{result['favicon']}}.ico" alt="{{result['favicon']}}" />{% endif %}<a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %}
+ <p class="content">{% if result.img_src %}<img src="{{ image_proxify(result.img_src) }}" class="image" />{% endif %}{% if result.content %}{{ result.content|safe }}<br class="last"/>{% endif %}</p>
+ {% if result.repository %}<p class="content"><a href="{{ result.repository|safe }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.repository }}</a></p>{% endif %}
+ <div dir="ltr">
+ {{ result.codelines|code_highlighter(result.code_language)|safe }}
+ </div>
+
+ <p class="url">{{ result.pretty_url }}&lrm;</p>
+</div>
diff --git a/searx/templates/courgette/result_templates/default.html b/searx/templates/courgette/result_templates/default.html
new file mode 100644
index 0000000..5f2ead6
--- /dev/null
+++ b/searx/templates/courgette/result_templates/default.html
@@ -0,0 +1,13 @@
+<div class="result {{ result.class }}">
+
+ {% if "icon_"~result.engine~".ico" in favicons %}
+ <img width="14" height="14" class="favicon" src="{{ url_for('static', filename='img/icons/icon_'+result.engine+'.ico') }}" alt="{{result.engine}}" />
+ {% endif %}
+
+ <div>
+ <h3 class="result_title"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %}
+ <p class="content">{% if result.content %}{{ result.content|safe }}<br />{% endif %}</p>
+ <p class="url">{{ result.pretty_url }}&lrm;</p>
+ </div>
+</div>
diff --git a/searx/templates/courgette/result_templates/images.html b/searx/templates/courgette/result_templates/images.html
new file mode 100644
index 0000000..49acb3b
--- /dev/null
+++ b/searx/templates/courgette/result_templates/images.html
@@ -0,0 +1,6 @@
+<div class="image_result">
+ <p>
+ <a href="{{ result.img_src }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img src="{% if result.thumbnail_src %}{{ image_proxify(result.thumbnail_src) }}{% else %}{{ image_proxify(result.img_src) }}{% endif %}" title="{{ result.title|striptags }}" alt="{{ result.title|striptags }}"/></a>
+ <span class="url"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %} class="small_font">{{ _('original context') }}</a></span>
+ </p>
+</div>
diff --git a/searx/templates/courgette/result_templates/map.html b/searx/templates/courgette/result_templates/map.html
new file mode 100644
index 0000000..5f2ead6
--- /dev/null
+++ b/searx/templates/courgette/result_templates/map.html
@@ -0,0 +1,13 @@
+<div class="result {{ result.class }}">
+
+ {% if "icon_"~result.engine~".ico" in favicons %}
+ <img width="14" height="14" class="favicon" src="{{ url_for('static', filename='img/icons/icon_'+result.engine+'.ico') }}" alt="{{result.engine}}" />
+ {% endif %}
+
+ <div>
+ <h3 class="result_title"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %}
+ <p class="content">{% if result.content %}{{ result.content|safe }}<br />{% endif %}</p>
+ <p class="url">{{ result.pretty_url }}&lrm;</p>
+ </div>
+</div>
diff --git a/searx/templates/courgette/result_templates/torrent.html b/searx/templates/courgette/result_templates/torrent.html
new file mode 100644
index 0000000..2fd8395
--- /dev/null
+++ b/searx/templates/courgette/result_templates/torrent.html
@@ -0,0 +1,13 @@
+<div class="result torrent_result">
+ {% if "icon_"~result.engine~".ico" in favicons %}
+ <img width="14" height="14" class="favicon" src="{{ url_for('static', filename='img/icons/icon_'+result.engine+'.ico') }}" alt="{{result.engine}}" />
+ {% endif %}
+ <h3 class="result_title"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ {% if result.content %}<span class="content">{{ result.content|safe }}</span><br />{% endif %}
+ <span class="stats">{{ _('Seeder') }} : {{ result.seed }}, {{ _('Leecher') }} : {{ result.leech }}</span><br />
+ <span>
+ {% if result.magnetlink %}<a href="{{ result.magnetlink }}" class="magnetlink">{{ _('magnet link') }}</a>{% endif %}
+ {% if result.torrentfile %}<a href="{{ result.torrentfile }}" class="torrentfile" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ _('torrent file') }}</a>{% endif %}
+ </span>
+ <p class="url">{{ result.pretty_url }}&lrm;</p>
+</div>
diff --git a/searx/templates/courgette/result_templates/videos.html b/searx/templates/courgette/result_templates/videos.html
new file mode 100644
index 0000000..b3e19e0
--- /dev/null
+++ b/searx/templates/courgette/result_templates/videos.html
@@ -0,0 +1,10 @@
+<div class="result">
+ {% if "icon_"~result.engine~".ico" in favicons %}
+ <img width="14" height="14" class="favicon" src="{{ url_for('static', filename='img/icons/icon_'+result.engine+'.ico') }}" alt="{{result.engine}}" />
+ {% endif %}
+
+ <h3 class="result_title"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span><br />{% endif %}
+ <a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img width="400" src="{{ image_proxify(result.thumbnail) }}" title="{{ result.title|striptags }}" alt="{{ result.title|striptags }}"/></a>
+ <p class="url">{{ result.pretty_url }}&lrm;</p>
+</div>
diff --git a/searx/templates/courgette/results.html b/searx/templates/courgette/results.html
new file mode 100644
index 0000000..c72b7c3
--- /dev/null
+++ b/searx/templates/courgette/results.html
@@ -0,0 +1,87 @@
+{% extends "courgette/base.html" %}
+{% block title %}{{ q|e }} - {% endblock %}
+{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&amp;format=rss&amp;{% for category in selected_categories %}category_{{ category }}=1&amp;{% endfor %}pageno={{ pageno }}">{% endblock %}
+{% block content %}
+<div class="right"><a href="{{ url_for('preferences') }}" id="preferences"><span>{{ _('preferences') }}</span></a></div>
+<div class="small search center">
+ {% include 'courgette/search.html' %}
+</div>
+<div id="results">
+ <div id="sidebar">
+ <div id="search_url">
+ {{ _('Search URL') }}:
+ <input type="text" value="{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}" readonly />
+ </div>
+ <div id="apis">
+ {{ _('Download results') }}<br />
+ {% for output_type in ('csv', 'json', 'rss') %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <div class="left">
+ <input type="hidden" name="q" value="{{ q|e }}" />
+ <input type="hidden" name="format" value="{{ output_type }}" />
+ {% for category in selected_categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ <input type="hidden" name="pageno" value="{{ pageno }}" />
+ <input type="submit" value="{{ output_type }}" />
+ </div>
+ </form>
+ {% endfor %}
+ </div>
+ </div>
+
+ {% if answers %}
+ <div id="answers" class=""><span>{{ _('Answers') }}</span>
+ {% for answer in answers %}
+ <span>{{ answer }}</span>
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ {% if suggestions %}
+ <div id="suggestions"><span>{{ _('Suggestions') }}</span>
+ {% for suggestion in suggestions %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <input type="hidden" name="q" value="{{ suggestion }}">
+ <input type="submit" value="{{ suggestion }}" />
+ </form>
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ {% for result in results %}
+ {% if result['template'] %}
+ {% include get_result_template('courgette', result['template']) %}
+ {% else %}
+ {% include 'courgette/result_templates/default.html' %}
+ {% endif %}
+ {% endfor %}
+
+ {% if paging %}
+ <div id="pagination">
+ {% if pageno > 1 %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <div class="left">
+ <input type="hidden" name="q" value="{{ q|e }}" />
+ {% for category in selected_categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ <input type="hidden" name="pageno" value="{{ pageno-1 }}" />
+ <input type="submit" value="<< {{ _('previous page') }}" />
+ </div>
+ </form>
+ {% endif %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <div class="left">
+ {% for category in selected_categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ <input type="hidden" name="q" value="{{ q|e }}" />
+ <input type="hidden" name="pageno" value="{{ pageno+1 }}" />
+ <input type="submit" value="{{ _('next page') }} >>" />
+ </div>
+ </form>
+ </div>
+ {% endif %}
+</div>
+{% endblock %}
diff --git a/searx/templates/courgette/search.html b/searx/templates/courgette/search.html
new file mode 100644
index 0000000..bd4efd4
--- /dev/null
+++ b/searx/templates/courgette/search.html
@@ -0,0 +1,7 @@
+<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form">
+ <div id="search_wrapper">
+ <input type="text" placeholder="{{ _('Search for...') }}" id="q" class="q" name="q" tabindex="1" autocomplete="off" {% if q %}value="{{ q }}"{% endif %}/>
+ <input type="submit" value="search" id="search_submit" />
+ </div>
+ {% include 'courgette/categories.html' %}
+</form> \ No newline at end of file
diff --git a/searx/templates/courgette/stats.html b/searx/templates/courgette/stats.html
new file mode 100644
index 0000000..b9aafbb
--- /dev/null
+++ b/searx/templates/courgette/stats.html
@@ -0,0 +1,22 @@
+{% extends "courgette/base.html" %}
+{% block head %} {% endblock %}
+{% block content %}
+<h2>{{ _('Engine stats') }}</h2>
+
+{% for stat_name,stat_category in stats %}
+<div class="left">
+ <table>
+ <tr>
+ <th colspan="3">{{ stat_name }}</th>
+ </tr>
+ {% for engine in stat_category %}
+ <tr>
+ <td>{{ engine.name }}</td>
+ <td>{{ '%.02f'|format(engine.avg) }}</td>
+ <td class="percentage"><div style="width: {{ engine.percentage }}%">&nbsp;</div></td>
+ </tr>
+ {% endfor %}
+ </table>
+</div>
+{% endfor %}
+{% endblock %} \ No newline at end of file
diff --git a/searx/templates/legacy/404.html b/searx/templates/legacy/404.html
new file mode 100644
index 0000000..3e889dd
--- /dev/null
+++ b/searx/templates/legacy/404.html
@@ -0,0 +1,9 @@
+{% extends "legacy/base.html" %}
+{% block content %}
+<div class="center">
+ <h1>{{ _('Page not found') }}</h1>
+ {% autoescape false %}
+ <p>{{ _('Go to %(search_page)s.', search_page=unicode('<a href="{}">{}</a>').format(url_for('index'), _('search page'))) }}</p>
+ {% endautoescape %}
+</div>
+{% endblock %}
diff --git a/searx/templates/legacy/about.html b/searx/templates/legacy/about.html
new file mode 100644
index 0000000..f773e3a
--- /dev/null
+++ b/searx/templates/legacy/about.html
@@ -0,0 +1,5 @@
+{% extends 'legacy/base.html' %}
+{% block content %}
+{% include 'legacy/github_ribbon.html' %}
+{% include '__common__/about.html' %}
+{% endblock %}
diff --git a/searx/templates/legacy/base.html b/searx/templates/legacy/base.html
new file mode 100644
index 0000000..da19741
--- /dev/null
+++ b/searx/templates/legacy/base.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}>
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
+ <meta name="keywords" content="searx, search, search engine, metasearch, meta search" />
+ <meta name="generator" content="searx/{{ searx_version }}">
+ <meta name="referrer" content="no-referrer">
+ <meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=1" />
+ <title>{% block title %}{% endblock %}searx</title>
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" type="text/css" media="screen" />
+ {% if rtl %}
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style-rtl.css') }}" type="text/css" media="screen" />
+ {% endif %}
+ <link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.png') }}?v=2" />
+ {% block styles %}
+ {% endblock %}
+ {% block meta %}{% endblock %}
+ {% block head %}
+ <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
+ {% endblock %}
+ </head>
+ <body>
+ <div id="container">
+ {% block content %}
+ {% endblock %}
+ {% if autocomplete %}
+ <script src="{{ url_for('static', filename='js/mootools-core-1.4.5-min.js') }}" ></script>
+ <script src="{{ url_for('static', filename='js/mootools-autocompleter-1.1.2-min.js') }}" ></script>
+ {% endif %}
+ <script type="text/javascript">
+ searx = {};
+ searx.autocompleter = {% if autocomplete %}true{% else %}false{% endif %};
+ </script>
+ <script src="{{ url_for('static', filename='js/searx.js') }}" ></script>
+ </div>
+ </body>
+</html>
diff --git a/searx/templates/legacy/categories.html b/searx/templates/legacy/categories.html
new file mode 100644
index 0000000..1c46678
--- /dev/null
+++ b/searx/templates/legacy/categories.html
@@ -0,0 +1,10 @@
+<div id="categories">
+ <div id="categories_container">
+ {% for category in categories %}
+ <div class="checkbox_container">
+ <input type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} /><label for="checkbox_{{ category|replace(' ', '_') }}" class="tooltips">{{ _(category) }}</label>
+ </div>
+ {% endfor %}
+ {% if display_tooltip %}<div class="hidden">{{ _('Click on the magnifier to perform search') }}</div>{% endif %}
+ </div>
+</div>
diff --git a/searx/templates/legacy/github_ribbon.html b/searx/templates/legacy/github_ribbon.html
new file mode 100644
index 0000000..bdd9cf1
--- /dev/null
+++ b/searx/templates/legacy/github_ribbon.html
@@ -0,0 +1,3 @@
+<a href="https://github.com/asciimoo/searx" class="github">
+ <img style="position: absolute; top: 0; right: 0; border: 0;" src="{{ url_for('static', filename='img/github_ribbon.png') }}" alt="Fork me on GitHub" class="github"/>
+</a>
diff --git a/searx/templates/legacy/index.html b/searx/templates/legacy/index.html
new file mode 100644
index 0000000..de956d5
--- /dev/null
+++ b/searx/templates/legacy/index.html
@@ -0,0 +1,18 @@
+{% extends "legacy/base.html" %}
+{% block content %}
+<div class="center">
+ <div class="title"><h1>searx</h1></div>
+ {% include 'legacy/search.html' %}
+ <p class="top_margin">
+ {% if rtl %}
+ <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>
+ {% endif %}
+ <a href="{{ url_for('about') }}" class="hmarg">{{ _('about') }}</a>
+ {% if not rtl %}
+ <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>
+ {% endif %}
+ </p>
+</div>
+{% include 'legacy/github_ribbon.html' %}
+{% endblock %}
+
diff --git a/searx/templates/legacy/infobox.html b/searx/templates/legacy/infobox.html
new file mode 100644
index 0000000..4dd25fa
--- /dev/null
+++ b/searx/templates/legacy/infobox.html
@@ -0,0 +1,51 @@
+<div class="infobox">
+<h2><bdi>{{ infobox.infobox }}</bdi></h2>
+ {% if infobox.img_src %}<img src="{{ image_proxify(infobox.img_src) }}" title="{{ infobox.infobox|striptags }}" alt="{{ infobox.infobox|striptags }}" />{% endif %}
+ <p><bdi>{{ infobox.entity }}</bdi></p>
+ <p><bdi>{{ infobox.content | safe }}</bdi></p>
+ {% if infobox.attributes %}
+ <div class="attributes">
+ <table>
+ {% for attribute in infobox.attributes %}
+ <tr>
+ <td><bdi>{{ attribute.label }}</bdi></td>
+ {% if attribute.image %}
+ <td><img src="{{ image_proxify(attribute.image.src) }}" alt="{{ attribute.image.alt }}" /></td>
+ {% else %}
+ <td><bdi>{{ attribute.value }}</bdi></td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ {% endif %}
+
+ {% if infobox.urls %}
+ <div class="urls">
+ <ul>
+ {% for url in infobox.urls %}
+ <li class="url"><bdi><a href="{{ url.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ url.title }}</a></bdi></li>
+ {% endfor %}
+ </ul>
+ </div>
+ {% endif %}
+
+ {% if infobox.relatedTopics %}
+ <div class="relatedTopics">
+ {% for topic in infobox.relatedTopics %}
+ <div>
+ <h3><bdi>{{ topic.name }}</bdi></h3>
+ {% for suggestion in topic.suggestions %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <input type="hidden" name="q" value="{{ suggestion }}">
+ <input type="submit" value="{{ suggestion }}" />
+ </form>
+ {% endfor %}
+ </div>
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ <br />
+
+</div>
diff --git a/searx/templates/legacy/preferences.html b/searx/templates/legacy/preferences.html
new file mode 100644
index 0000000..f418dcd
--- /dev/null
+++ b/searx/templates/legacy/preferences.html
@@ -0,0 +1,129 @@
+{% extends "legacy/base.html" %}
+{% block head %} {% endblock %}
+{% block content %}
+<div class="row">
+ <h2>{{ _('Preferences') }}</h2>
+
+ <form method="post" action="{{ url_for('preferences') }}" id="search_form">
+ <fieldset>
+ <legend>{{ _('Default categories') }}</legend>
+ {% set display_tooltip = false %}
+ {% include 'legacy/categories.html' %}
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Search language') }}</legend>
+ <p>
+ <select name='language'>
+ <option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Default language') }}</option>
+ {% for lang_id,lang_name,country_name,english_name in language_codes | sort(attribute=1) %}
+ <option value="{{ lang_id }}" {% if lang_id == current_language %}selected="selected"{% endif %}>{{ lang_name }} {% if country_name %}({{ country_name }}) {% endif %}- {{ lang_id }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Interface language') }}</legend>
+ <p>
+ <select name='locale'>
+ {% for locale_id,locale_name in locales.items() | sort %}
+ <option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Autocomplete') }}</legend>
+ <p>
+ <select name="autocomplete">
+ <option value=""> - </option>
+ {% for backend in autocomplete_backends %}
+ <option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Image proxy') }}</legend>
+ <p>
+ <select name='image_proxy'>
+ <option value="1" {% if image_proxy %}selected="selected"{% endif %}>{{ _('Enabled') }}</option>
+ <option value="" {% if not image_proxy %}selected="selected"{% endif %}>{{ _('Disabled') }}</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Method') }}</legend>
+ <p>
+ <select name='method'>
+ <option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
+ <option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('SafeSearch') }}</legend>
+ <p>
+ <select name='safesearch'>
+ <option value="2" {% if safesearch == '2' %}selected="selected"{% endif %}>{{ _('Strict') }}</option>
+ <option value="1" {% if safesearch == '1' %}selected="selected"{% endif %}>{{ _('Moderate') }}</option>
+ <option value="0" {% if safesearch == '0' %}selected="selected"{% endif %}>{{ _('None') }}</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Themes') }}</legend>
+ <p>
+ <select name="theme">
+ {% for name in themes %}
+ <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Results on new tabs') }}</legend>
+ <p>
+ <select name='results_on_new_tab'>
+ <option value="1" {% if results_on_new_tab %}selected="selected"{% endif %}>{{ _('On') }}</option>
+ <option value="0" {% if not results_on_new_tab %}selected="selected"{% endif %}>{{ _('Off')}}</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Currently used search engines') }}</legend>
+
+ <table>
+ <tr>
+ <th>{{ _('Engine name') }}</th>
+ <th>{{ _('Category') }}</th>
+ <th>{{ _('Allow') }} / {{ _('Block') }}</th>
+ </tr>
+ {% for categ in all_categories %}
+ {% for search_engine in engines_by_category[categ] %}
+
+ {% if not search_engine.private %}
+ <tr>
+ <td>{{ search_engine.name }} ({{ shortcuts[search_engine.name] }})&lrm;</td>
+ <td>{{ _(categ) }}</td>
+ <td class="engine_checkbox">
+ <input type="checkbox" id="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}" name="engine_{{ search_engine.name }}__{{ categ }}"{% if (search_engine.name, categ) in disabled_engines %} checked="checked"{% endif %} />
+ <label class="allow" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Allow') }}</label>
+ <label class="deny" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Block') }}</label>
+ </td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ </table>
+ </fieldset>
+ <p class="small_font">{{ _('These settings are stored in your cookies, this allows us not to store this data about you.') }}
+ <br />
+ {{ _("These cookies serve your sole convenience, we don't use these cookies to track you.") }}
+ </p>
+
+ <input type="submit" value="{{ _('save') }}" />
+ <div class="{% if rtl %}left{% else %}right{% endif %} preferences_back"><a href="{{ url_for('clear_cookies') }}">{{ _('Reset defaults') }}</a></div>
+ <div class="{% if rtl %}left{% else %}right{% endif %} preferences_back"><a href="{{ url_for('index') }}">{{ _('back') }}</a></div>
+ </form>
+</div>
+{% endblock %}
diff --git a/searx/templates/legacy/result_templates/code.html b/searx/templates/legacy/result_templates/code.html
new file mode 100644
index 0000000..9e3ed20
--- /dev/null
+++ b/searx/templates/legacy/result_templates/code.html
@@ -0,0 +1,11 @@
+<div class="result {{ result.class }}">
+ <h3 class="result_title"> {% if result['favicon'] %}<img width="14" height="14" class="favicon" src="static/{{theme}}/img/icon_{{result['favicon']}}.ico" alt="{{result['favicon']}}" />{% endif %}<a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ <p class="url">{{ result.pretty_url }}&lrm; <a class="cache_link" href="https://web.archive.org/web/{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ _('cached') }}</a></p>
+ {% if result.publishedDate %}<p class="published_date">{{ result.publishedDate }}</p>{% endif %}
+ <p class="content">{% if result.img_src %}<img src="{{ image_proxify(result.img_src) }}" class="image" />{% endif %}{% if result.content %}{{ result.content|safe }}<br class="last"/>{% endif %}</p>
+ {% if result.repository %}<p class="result-content"><a href="{{ result.repository|safe }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.repository }}</a></p>{% endif %}
+
+ <div dir="ltr">
+ {{ result.codelines|code_highlighter(result.code_language)|safe }}
+ </div>
+</div>
diff --git a/searx/templates/legacy/result_templates/default.html b/searx/templates/legacy/result_templates/default.html
new file mode 100644
index 0000000..da09117
--- /dev/null
+++ b/searx/templates/legacy/result_templates/default.html
@@ -0,0 +1,6 @@
+<div class="result {{ result.class }}">
+ <h3 class="result_title">{% if "icon_"~result.engine~".ico" in favicons %}<img width="14" height="14" class="favicon" src="{{ url_for('static', filename='img/icons/icon_'+result.engine+'.ico') }}" alt="{{result.engine}}" />{% endif %}<a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ <p class="url">{{ result.pretty_url }}&lrm; <a class="cache_link" href="https://web.archive.org/web/{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ _('cached') }}</a>
+ {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %}</p>
+ <p class="content">{% if result.img_src %}<img src="{{ image_proxify(result.img_src) }}" class="image" />{% endif %}{% if result.content %}{{ result.content|safe }}<br class="last"/>{% endif %}</p>
+</div>
diff --git a/searx/templates/legacy/result_templates/images.html b/searx/templates/legacy/result_templates/images.html
new file mode 100644
index 0000000..00f62ab
--- /dev/null
+++ b/searx/templates/legacy/result_templates/images.html
@@ -0,0 +1,6 @@
+<div class="image_result">
+ <p>
+ <a href="{{ result.img_src }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img src="{% if result.thumbnail_src %}{{ image_proxify(result.thumbnail_src) }}{% else %}{{ image_proxify(result.img_src) }}{% endif %}" title="{{ result.title|striptags }}" alt="{{ result.title|striptags }}" /></a>
+ <span class="url"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %} class="small_font">{{ _('original context') }}</a></span>
+ </p>
+</div>
diff --git a/searx/templates/legacy/result_templates/map.html b/searx/templates/legacy/result_templates/map.html
new file mode 100644
index 0000000..0200e0f
--- /dev/null
+++ b/searx/templates/legacy/result_templates/map.html
@@ -0,0 +1,13 @@
+<div class="result {{ result.class }}">
+
+ {% if "icon_"~result.engine~".ico" in favicons %}
+ <img width="14" height="14" class="favicon" src="{{ url_for('static', filename='img/icons/icon_'+result.engine+'.ico') }}" alt="{{result.engine}}" />
+ {% endif %}
+
+ <div>
+ <h3 class="result_title"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ <p class="url">{{ result.pretty_url }}&lrm; <a class="cache_link" href="https://web.archive.org/web/{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ _('cached') }}</a>
+ {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %}</p>
+ <p class="content">{% if result.img_src %}<img src="{{ image_proxify(result.img_src) }}" class="image" />{% endif %}{% if result.content %}{{ result.content|safe }}<br class="last"/>{% endif %}</p>
+ </div>
+</div>
diff --git a/searx/templates/legacy/result_templates/torrent.html b/searx/templates/legacy/result_templates/torrent.html
new file mode 100644
index 0000000..67e058a
--- /dev/null
+++ b/searx/templates/legacy/result_templates/torrent.html
@@ -0,0 +1,13 @@
+<div class="result torrent_result">
+ {% if "icon_"~result.engine~".ico" in favicons %}
+ <img width="14" height="14" class="favicon" src="{{ url_for('static', filename='img/icons/icon_'+result.engine+'.ico') }}" alt="{{result.engine}}" />
+ {% endif %}
+ <h3 class="result_title"><a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ <p class="url">{{ result.pretty_url }}&lrm;</p>
+ {% if result.content %}<p class="content">{{ result.content|safe }}</p>{% endif %}
+ <p>
+ {% if result.magnetlink %}<a href="{{ result.magnetlink }}" class="magnetlink">{{ _('magnet link') }}</a>{% endif %}
+ {% if result.torrentfile %}<a href="{{ result.torrentfile }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %} class="torrentfile">{{ _('torrent file') }}</a>{% endif %} -
+ <span class="stats">{{ _('Seeder') }} : {{ result.seed }}, {{ _('Leecher') }} : {{ result.leech }}</span>
+ </p>
+</div>
diff --git a/searx/templates/legacy/result_templates/videos.html b/searx/templates/legacy/result_templates/videos.html
new file mode 100644
index 0000000..727f44c
--- /dev/null
+++ b/searx/templates/legacy/result_templates/videos.html
@@ -0,0 +1,6 @@
+<div class="result">
+ <h3 class="result_title">{% if "icon_"~result.engine~".ico" in favicons %}<img width="14" height="14" class="favicon" src="{{ url_for('static', filename='img/icons/icon_'+result.engine+'.ico') }}" alt="{{result.engine}}" />{% endif %}<a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
+ {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span><br />{% endif %}
+ <a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img class="thumbnail" src="{{ image_proxify(result.thumbnail) }}" title="{{ result.title|striptags }}" alt="{{ result.title|striptags }}"/></a>
+ <p class="url">{{ result.url }}&lrm;</p>
+</div>
diff --git a/searx/templates/legacy/results.html b/searx/templates/legacy/results.html
new file mode 100644
index 0000000..f0d7839
--- /dev/null
+++ b/searx/templates/legacy/results.html
@@ -0,0 +1,100 @@
+{% extends "legacy/base.html" %}
+{% block title %}{{ q|e }} - {% endblock %}
+{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&amp;format=rss&amp;{% for category in selected_categories %}category_{{ category }}=1&amp;{% endfor %}pageno={{ pageno }}">{% endblock %}
+{% block content %}
+<div class="preferences_container right"><a href="{{ url_for('preferences') }}" id="preferences"><span>preferences</span></a></div>
+<div class="small search center">
+ {% include 'legacy/search.html' %}
+</div>
+<div id="results">
+ <div id="sidebar">
+
+ <div id="search_url">
+ {{ _('Search URL') }}:
+ <input type="text" value="{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}" readonly />
+ </div>
+ <div id="apis">
+ {{ _('Download results') }}
+ {% for output_type in ('csv', 'json', 'rss') %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <div class="left">
+ <input type="hidden" name="q" value="{{ q|e }}" />
+ <input type="hidden" name="format" value="{{ output_type }}" />
+ {% for category in selected_categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ <input type="hidden" name="pageno" value="{{ pageno }}" />
+ <input type="submit" value="{{ output_type }}" />
+ </div>
+ </form>
+ {% endfor %}
+ </div>
+ </div>
+
+ {% if answers %}
+ <div id="answers"><span>{{ _('Answers') }}</span>
+ {% for answer in answers %}
+ <span>{{ answer }}</span>
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ {% if suggestions %}
+ <div id="suggestions"><span id="suggestions-title">{{ _('Suggestions') }} : </span>
+ {% set first = true %}
+ {% for suggestion in suggestions %}
+ {% if not first %} &bull; {% endif %}<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <input type="hidden" name="q" value="{{ suggestion }}">
+ <input type="submit" class="suggestion" value="{{ suggestion }}" />
+ </form>
+ {% set first = false %}
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ {% if infoboxes %}
+ <div id="infoboxes">
+ {% for infobox in infoboxes %}
+ {% include 'legacy/infobox.html' %}
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ {% for result in results %}
+ {% if result['template'] %}
+ {% include get_result_template('legacy', result['template']) %}
+ {% else %}
+ {% include 'legacy/result_templates/default.html' %}
+ {% endif %}
+ {% endfor %}
+
+ {% if paging %}
+ <div id="pagination">
+ {% if pageno > 1 %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <div class="{% if rtl %}right{% else %}left{% endif %}">
+ <input type="hidden" name="q" value="{{ q|e }}" />
+ {% for category in selected_categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ <input type="hidden" name="pageno" value="{{ pageno-1 }}" />
+ <input type="submit" value="<< {{ _('previous page') }}" />
+ </div>
+ </form>
+ {% endif %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <div class="{% if rtl %}left{% else %}right{% endif %}">
+ {% for category in selected_categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ <input type="hidden" name="q" value="{{ q|e }}" />
+ <input type="hidden" name="pageno" value="{{ pageno+1 }}" />
+ <input type="submit" value="{{ _('next page') }} >>" />
+ </div>
+ </form>
+
+ <br />
+ </div>
+ {% endif %}
+</div>
+{% endblock %}
diff --git a/searx/templates/legacy/search.html b/searx/templates/legacy/search.html
new file mode 100644
index 0000000..4d37f9b
--- /dev/null
+++ b/searx/templates/legacy/search.html
@@ -0,0 +1,8 @@
+<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form">
+ <div id="search_wrapper">
+ <input type="text" placeholder="{{ _('Search for...') }}" id="q" class="q" name="q" tabindex="1" autocomplete="off" size="100" {% if q %}value="{{ q }}"{% endif %}/>
+ <input type="submit" value="search" id="search_submit" />
+ </div>
+ {% set display_tooltip = true %}
+ {% include 'legacy/categories.html' %}
+</form>
diff --git a/searx/templates/legacy/stats.html b/searx/templates/legacy/stats.html
new file mode 100644
index 0000000..372447e
--- /dev/null
+++ b/searx/templates/legacy/stats.html
@@ -0,0 +1,22 @@
+{% extends "legacy/base.html" %}
+{% block head %} {% endblock %}
+{% block content %}
+<h2>{{ _('Engine stats') }}</h2>
+
+{% for stat_name,stat_category in stats %}
+<div class="left">
+ <table>
+ <tr colspan="3">
+ <th>{{ stat_name }}</th>
+ </tr>
+ {% for engine in stat_category %}
+ <tr>
+ <td>{{ engine.name }}</td>
+ <td>{{ '%.02f'|format(engine.avg) }}</td>
+ <td class="percentage"><div style="width: {{ engine.percentage }}%">&nbsp;</div></td>
+ </tr>
+ {% endfor %}
+ </table>
+</div>
+{% endfor %}
+{% endblock %}
diff --git a/searx/templates/oscar/404.html b/searx/templates/oscar/404.html
new file mode 100644
index 0000000..5a50880
--- /dev/null
+++ b/searx/templates/oscar/404.html
@@ -0,0 +1,9 @@
+{% extends "oscar/base.html" %}
+{% block content %}
+<div class="text-center">
+ <h1>{{ _('Page not found') }}</h1>
+ {% autoescape false %}
+ <p>{{ _('Go to %(search_page)s.', search_page=unicode('<a href="{}">{}</a>').format(url_for('index'), _('search page'))) }}</p>
+ {% endautoescape %}
+</div>
+{% endblock %}
diff --git a/searx/templates/oscar/about.html b/searx/templates/oscar/about.html
new file mode 100644
index 0000000..bc7fed8
--- /dev/null
+++ b/searx/templates/oscar/about.html
@@ -0,0 +1,5 @@
+{% extends "oscar/base.html" %}
+{% block title %}{{ _('about') }} - {% endblock %}
+{% block content %}
+{% include '__common__/about.html' %}
+{% endblock %}
diff --git a/searx/templates/oscar/advanced.html b/searx/templates/oscar/advanced.html
new file mode 100644
index 0000000..95d99ba
--- /dev/null
+++ b/searx/templates/oscar/advanced.html
@@ -0,0 +1,16 @@
+<input type="checkbox" name="advanced_search" id="check-advanced" {% if advanced_search %} checked="checked"{% endif %}>
+<label for="check-advanced">
+ <span class="glyphicon glyphicon-cog"></span>
+ {{ _('Advanced settings') }}
+</label>
+<div id="advanced-search-container">
+ {% include 'oscar/categories.html' %}
+ <div class="row">
+ <div class="col-xs-6">
+ {% include 'oscar/time-range.html' %}
+ </div>
+ <div class="col-xs-6">
+ {% include 'oscar/languages.html' %}
+ </div>
+ </div>
+</div>
diff --git a/searx/templates/oscar/base.html b/searx/templates/oscar/base.html
new file mode 100644
index 0000000..890204c
--- /dev/null
+++ b/searx/templates/oscar/base.html
@@ -0,0 +1,107 @@
+{% from 'oscar/macros.html' import icon %}
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}>
+<head>
+ <meta charset="UTF-8" />
+ <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
+ <meta name="keywords" content="searx, search, search engine, metasearch, meta search" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="generator" content="searx/{{ searx_version }}">
+ <meta name="referrer" content="no-referrer">
+ <meta name="viewport" content="width=device-width, initial-scale=1 , maximum-scale=1.0, user-scalable=1" />
+ {% block meta %}{% endblock %}
+ <title>{% block title %}{% endblock %}{{ instance_name }}</title>
+
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}" type="text/css" />
+ {% if cookies['oscar-style'] %}
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/'+cookies['oscar-style']+'.min.css') }}" type="text/css" />
+ {% else %}
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/logicodev.min.css') }}" type="text/css" />
+ {% endif %}
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/leaflet.min.css') }}" type="text/css" />
+ {% for css in styles %}
+ <link rel="stylesheet" href="{{ url_for('static', filename=css) }}" type="text/css" />
+ {% endfor %}
+
+ <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="{{ url_for('static', filename='js/html5shiv.min.js') }}"></script>
+ <script src="{{ url_for('static', filename='js/respond.min.js') }}"></script>
+ <![endif]-->
+
+ <link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.png') }}" />
+
+ {% block styles %}
+ {% endblock %}
+ {% block head %}
+ {% endblock %}
+
+ <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
+
+ <script type="text/javascript">
+ searx = {};
+ searx.method = "{{ method or 'POST' }}";
+ searx.autocompleter = {% if autocomplete %}true{% else %}false{% endif %};
+ </script>
+ <noscript>
+ <style type="text/css">
+ .tab-content > .active_if_nojs, .active_if_nojs {display: block !important; visibility: visible !important;}
+ .margin_top_if_nojs {margin-top: 20px;}
+ .hide_if_nojs {display: none !important;overflow:none !important;}
+ .disabled_if_nojs {pointer-events: none; cursor: default; text-decoration: line-through;}
+ </style>
+ </noscript>
+</head>
+<body>
+ {% include 'oscar/navbar.html' %}
+ <div class="container">
+ {% if errors %}
+ <div class="alert alert-danger fade in" role="alert">
+ <button class="close" data-dismiss="alert" type="button">
+ <span aria-hidden="true">×</span>
+ <span class="sr-only">{{ _('Close') }}</span>
+ </button>
+ <strong class="lead">{{ icon('info-sign') }} {{ _('Error!') }}</strong>
+ <ul>
+ {% for message in errors %}
+ <li>{{ message }}</li>
+ {% endfor %}
+ </ul>
+ </div>
+ {% endif %}
+
+ {% block site_alert_error %}
+ {% endblock %}
+ {% block site_alert_warning %}
+ {% endblock %}
+ {% block site_alert_info %}
+ {% endblock %}
+ {% block site_alert_success %}
+ {% endblock %}
+
+ {% block content %}
+ {% endblock %}
+
+ </div>
+ <div class="footer">
+ <div class="container">
+ {% block footer %}
+ {% endblock %}
+ <p class="text-muted"><small>{{ _('Powered by') }} <a href="https://asciimoo.github.io/searx/">searx</a> - {{ searx_version }} - {{ _('a privacy-respecting, hackable metasearch engine') }}</small></p>
+ </div>
+ </div>
+ <script src="{{ url_for('static', filename='js/jquery-1.11.1.min.js') }}"></script>
+ <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
+ {% if autocomplete %}<script src="{{ url_for('static', filename='js/typeahead.bundle.min.js') }}"></script>{% endif %}
+ <script src="{{ url_for('static', filename='js/require-2.1.15.min.js') }}"></script>
+ <script src="{{ url_for('static', filename='js/searx.min.js') }}"></script>
+ {% for script in scripts %}
+ <script src="{{ url_for('static', filename=script) }}"></script>
+ {% endfor %}
+ <noscript>
+ <style>
+ .glyphicon { display: none; }
+ </style>
+ </noscript>
+</body>
+</html>
diff --git a/searx/templates/oscar/categories.html b/searx/templates/oscar/categories.html
new file mode 100644
index 0000000..1ace10f
--- /dev/null
+++ b/searx/templates/oscar/categories.html
@@ -0,0 +1,13 @@
+<div id="categories">
+{% if rtl %}
+ {% for category in categories | reverse %}
+ <input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} />
+ <label for="checkbox_{{ category|replace(' ', '_') }}">{{ _(category) }}</label>
+ {% endfor %}
+{% else %}
+ {% for category in categories %}
+ <input class="hidden" type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} />
+ <label for="checkbox_{{ category|replace(' ', '_') }}">{{ _(category) }}</label>
+ {% endfor %}
+{% endif %}
+</div>
diff --git a/searx/templates/oscar/index.html b/searx/templates/oscar/index.html
new file mode 100644
index 0000000..b941f5f
--- /dev/null
+++ b/searx/templates/oscar/index.html
@@ -0,0 +1,22 @@
+{% extends "oscar/base.html" %}
+{% block content %}
+<div class="container-fluid">
+ <div class="row">
+ <div class="text-center col-sm-12 col-md-12">
+ {% if cookies['oscar-style'] == 'pointhi' %}
+ <h1 class="text-hide center-block"><img class="center-block img-responsive" src="{{ url_for('static', filename='img/searx_logo.png') }}" alt="searx logo"/>searx</h1>
+ {% else %}
+ <h1 class="text-hide center-block" id="main-logo">
+ <img class="center-block img-responsive" src="{{ url_for('static', filename='img/logo_searx_a.png') }}" alt="searx logo" />
+ searx
+ </h1>
+ {% endif %}
+ </div>
+ </div>
+ <div class="row">
+ <div class="text-center col-sm-12 col-md-12">
+ {% include 'oscar/search_full.html' %}
+ </div>
+ </div>
+</div>
+{% endblock %}
diff --git a/searx/templates/oscar/infobox.html b/searx/templates/oscar/infobox.html
new file mode 100644
index 0000000..c98fb0e
--- /dev/null
+++ b/searx/templates/oscar/infobox.html
@@ -0,0 +1,35 @@
+{% from 'oscar/macros.html' import result_link with context %}
+<div class="panel panel-default infobox">
+ <div class="panel-heading">
+ <h4 class="panel-title infobox_part"><bdi>{{ infobox.infobox }}</bdi></h4>
+ </div>
+ <div class="panel-body">
+ {% if infobox.img_src %}<img class="img-responsive center-block infobox_part" src="{{ image_proxify(infobox.img_src) }}" alt="{{ infobox.infobox }}" />{% endif %}
+ {% if infobox.content %}<bdi><p class="infobox_part">{{ infobox.content }}</bdi></p>{% endif %}
+
+ {% if infobox.attributes %}
+ <table class="table table-striped infobox_part">
+ {% for attribute in infobox.attributes %}
+ <tr>
+ <td><bdi>{{ attribute.label }}</bdi></td>
+ {% if attribute.image %}
+ <td><img class="img-responsive" src="{{ image_proxify(attribute.image.src) }}" alt="{{ attribute.image.alt }}" /></td>
+ {% else %}
+ <td><bdi>{{ attribute.value }}</bdi></td>
+ {% endif %}
+ </tr>
+ {% endfor %}
+ </table>
+ {% endif %}
+
+ {% if infobox.urls %}
+ <div class="infobox_part">
+ <bdi>
+ {% for url in infobox.urls %}
+ <p class="btn btn-default btn-xs">{{ result_link(url.url, url.title) }}</a></p>
+ {% endfor %}
+ </bdi>
+ </div>
+ {% endif %}
+ </div>
+</div>
diff --git a/searx/templates/oscar/languages.html b/searx/templates/oscar/languages.html
new file mode 100644
index 0000000..96c1c3a
--- /dev/null
+++ b/searx/templates/oscar/languages.html
@@ -0,0 +1,12 @@
+{% if preferences %}
+<select class="form-control" name='language'>
+{% else %}
+<select class="time_range custom-select form-control" id='language' name='language'>
+{% endif %}
+ <option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Default language') }}</option>
+ {% for lang_id,lang_name,country_name,english_name in language_codes | sort(attribute=1) %}
+ <option value="{{ lang_id }}" {% if lang_id == current_language %}selected="selected"{% endif %}>
+ {{ lang_name }} {% if country_name %}({{ country_name }}) {% endif %}- {{ lang_id }}
+ </option>
+ {% endfor %}
+</select>
diff --git a/searx/templates/oscar/macros.html b/searx/templates/oscar/macros.html
new file mode 100644
index 0000000..e71091e
--- /dev/null
+++ b/searx/templates/oscar/macros.html
@@ -0,0 +1,88 @@
+<!-- Draw glyphicon icon from bootstrap-theme -->
+{% macro icon(action) -%}
+ <span class="glyphicon glyphicon-{{ action }}"></span>
+{%- endmacro %}
+
+<!-- Draw favicon -->
+<!-- TODO: using url_for methode -->
+{% macro draw_favicon(favicon) -%}
+ <img width="32" height="32" class="favicon" src="static/themes/oscar/img/icons/{{ favicon }}.png" alt="{{ favicon }}" />
+{%- endmacro %}
+
+{%- macro result_link(url, title, classes='') -%}
+<a href="{{ url }}" {% if classes %}class="{{ classes }}" {% endif %}{% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ title }}</a>
+{%- endmacro -%}
+
+<!-- Draw result header -->
+{% macro result_header(result, favicons) -%}
+<h4 class="result_header">{% if result.engine~".png" in favicons %}{{ draw_favicon(result.engine) }} {% endif %}{{ result_link(result.url, result.title|safe) }}</h4>
+{%- endmacro %}
+
+<!-- Draw result sub header -->
+{% macro result_sub_header(result) -%}
+ {% if result.publishedDate %}<time class="text-muted" datetime="{{ result.pubdate }}" >{{ result.publishedDate }}</time>{% endif %}
+ {% if result.magnetlink %}<small> &bull; {{ result_link(result.magnetlink, icon('magnet') + _('magnet link'), "magnetlink") }}</small>{% endif %}
+ {% if result.torrentfile %}<small> &bull; {{ result_link(result.torrentfile, icon('download-alt') + _('torrent file'), "torrentfile") }}</small>{% endif %}
+{%- endmacro %}
+
+<!-- Draw result footer -->
+{% macro result_footer(result) -%}
+ <div class="clearfix"></div>
+ <div class="pull-right">
+ {% for engine in result.engines %}
+ <span class="label label-default">{{ engine }}</span>
+ {% endfor %}
+ <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
+ {% if proxify %}
+ <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
+ {% endif %}
+</div>
+<div class="external-link">{{ result.pretty_url }}</div>
+{%- endmacro %}
+
+<!-- Draw result footer -->
+{% macro result_footer_rtl(result) -%}
+ <div class="clearfix"></div>
+ {% for engine in result.engines %}
+ <span class="label label-default">{{ engine }}</span>
+ {% endfor %}
+ <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
+ {% if proxify %}
+ <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
+ {% endif %}
+ <div class="external-link">{{ result.pretty_url }}</div>
+{%- endmacro %}
+
+{% macro preferences_item_header(info, label, rtl) -%}
+ {% if rtl %}
+ <div class="row form-group">
+ <label class="col-sm-3 col-md-2 pull-right">{{ label }}</label>
+ <span class="col-sm-5 col-md-6 help-block pull-left">{{ info }}</span>
+ <div class="col-sm-4 col-md-4">
+ {% else %}
+ <div class="row form-group">
+ <label class="col-sm-3 col-md-2">{{ label }}</label>
+ <div class="col-sm-4 col-md-4">
+ {% endif %}
+{%- endmacro %}
+
+{% macro preferences_item_footer(info, label, rtl) -%}
+ {% if rtl %}
+ </div>
+ </div>
+ {% else %}
+ </div>
+ <span class="col-sm-5 col-md-6 help-block">{{ info }}</span>
+ </div>
+ {% endif %}
+{%- endmacro %}
+
+{% macro checkbox_toggle(id, blocked) -%}
+ <div class="onoffswitch">
+ <input type="checkbox" id="{{ id }}" name="{{ id }}"{% if blocked %} checked="checked"{% endif %} class="onoffswitch-checkbox">
+ <label class="onoffswitch-label" for="{{ id }}">
+ <span class="onoffswitch-inner"></span>
+ <span class="onoffswitch-switch"></span>
+ </label>
+ </div>
+{%- endmacro %}
diff --git a/searx/templates/oscar/messages/first_time.html b/searx/templates/oscar/messages/first_time.html
new file mode 100644
index 0000000..38db62b
--- /dev/null
+++ b/searx/templates/oscar/messages/first_time.html
@@ -0,0 +1,8 @@
+<div class="alert alert-info fade in" role="alert">
+ <button class="close" data-dismiss="alert" type="button">
+ <span aria-hidden="true">×</span>
+ <span class="sr-only">{{ _('Close') }}</span>
+ </button>
+ <strong class="lead">{{ icon('info-sign') }} {{ _('Heads up!') }}</strong>
+ {{ _('It look like you are using searx first time.') }}
+</div>
diff --git a/searx/templates/oscar/messages/no_cookies.html b/searx/templates/oscar/messages/no_cookies.html
new file mode 100644
index 0000000..9bebc8a
--- /dev/null
+++ b/searx/templates/oscar/messages/no_cookies.html
@@ -0,0 +1,5 @@
+{% from 'oscar/macros.html' import icon %}
+<div class="alert alert-info fade in" role="alert">
+ <strong class="lead">{{ icon('info-sign') }} {{ _('Information!') }}</strong>
+ {{ _('currently, there are no cookies defined.') }}
+</div>
diff --git a/searx/templates/oscar/messages/no_data_available.html b/searx/templates/oscar/messages/no_data_available.html
new file mode 100644
index 0000000..aee7917
--- /dev/null
+++ b/searx/templates/oscar/messages/no_data_available.html
@@ -0,0 +1,5 @@
+{% from 'oscar/macros.html' import icon %}
+<div class="alert alert-info fade in" role="alert">
+ <strong class="lead">{{ icon('info-sign') }} {{ _('Heads up!') }}</strong>
+ {{ _('There is currently no data available. ') }}
+</div>
diff --git a/searx/templates/oscar/messages/no_results.html b/searx/templates/oscar/messages/no_results.html
new file mode 100644
index 0000000..ac3705e
--- /dev/null
+++ b/searx/templates/oscar/messages/no_results.html
@@ -0,0 +1,9 @@
+{% from 'oscar/macros.html' import icon %}
+<div class="alert alert-info fade in" role="alert">
+ <button class="close" data-dismiss="alert" type="button">
+ <span aria-hidden="true">×</span>
+ <span class="sr-only">{{ _('Close') }}</span>
+ </button>
+ <strong class="lead">{{ icon('info-sign') }} {{ _('Sorry!') }}</strong>
+ {{ _('we didn\'t find any results. Please use another query or search in more categories.') }}
+</div>
diff --git a/searx/templates/oscar/messages/save_settings_successfull.html b/searx/templates/oscar/messages/save_settings_successfull.html
new file mode 100644
index 0000000..63e578c
--- /dev/null
+++ b/searx/templates/oscar/messages/save_settings_successfull.html
@@ -0,0 +1,9 @@
+{% from 'oscar/macros.html' import icon %}
+<div class="alert alert-success fade in" role="alert">
+ <button class="close" data-dismiss="alert" type="button">
+ <span aria-hidden="true">×</span>
+ <span class="sr-only">{{ _('Close') }}</span>
+ </button>
+ <strong class="lead">{{ icon('ok-sign') }} {{ _('Well done!') }}</strong>
+ {{ _('Settings saved successfully.') }}
+</div>
diff --git a/searx/templates/oscar/messages/unknow_error.html b/searx/templates/oscar/messages/unknow_error.html
new file mode 100644
index 0000000..3c4c9c1
--- /dev/null
+++ b/searx/templates/oscar/messages/unknow_error.html
@@ -0,0 +1,9 @@
+{% from 'oscar/macros.html' import icon %}
+<div class="alert alert-danger fade in" role="alert">
+ <button class="close" data-dismiss="alert" type="button">
+ <span aria-hidden="true">×</span>
+ <span class="sr-only">{{ _('Close') }}</span>
+ </button>
+ <strong class="lead">{{ icon('exclamation-sign') }} {{ _('Oh snap!') }}</strong>
+ {{ _('Something went wrong.') }}
+</div>
diff --git a/searx/templates/oscar/navbar.html b/searx/templates/oscar/navbar.html
new file mode 100644
index 0000000..12bf14f
--- /dev/null
+++ b/searx/templates/oscar/navbar.html
@@ -0,0 +1,9 @@
+<div class="searx-navbar">
+ <span class="instance {% if rtl %}pull-right{% else %}pull-left{% endif%}">
+ <a href="{{ url_for('index') }}">{{ instance_name }}</a>
+ </span>
+ <span class="{% if rtl %}pull-left{% else %}pull-right{% endif %}">
+ <a href="{{ url_for('about') }}">{{ _('about') }}</a>
+ <a href="{{ url_for('preferences') }}">{{ _('preferences') }}</a>
+ </span>
+</div>
diff --git a/searx/templates/oscar/preferences.html b/searx/templates/oscar/preferences.html
new file mode 100644
index 0000000..e5477e7
--- /dev/null
+++ b/searx/templates/oscar/preferences.html
@@ -0,0 +1,292 @@
+{% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle %}
+{% extends "oscar/base.html" %}
+{% block title %}{{ _('preferences') }} - {% endblock %}
+{% block content %}
+<div>
+
+ <h1>{{ _('Preferences') }}</h1>
+ <form method="post" action="{{ url_for('preferences') }}" id="search_form">
+
+ <!-- Nav tabs -->
+ <ul class="nav nav-tabs nav-justified hide_if_nojs" role="tablist" style="margin-bottom:20px;">
+ <li class="active"><a href="#tab_general" role="tab" data-toggle="tab">{{ _('General') }}</a></li>
+ <li><a href="#tab_engine" role="tab" data-toggle="tab">{{ _('Engines') }}</a></li>
+ <li><a href="#tab_plugins" role="tab" data-toggle="tab">{{ _('Plugins') }}</a></li>
+ {% if answerers %}<li><a href="#tab_answerers" role="tab" data-toggle="tab">{{ _('Answerers') }}</a></li>{% endif %}
+ <li><a href="#tab_cookies" role="tab" data-toggle="tab">{{ _('Cookies') }}</a></li>
+ </ul>
+
+ <!-- Tab panes -->
+ <noscript>
+ <h3>{{ _('General') }}</h3>
+ </noscript>
+ <div class="tab-content">
+ <div class="tab-pane active" id="tab_general">
+ <fieldset>
+ <div class="container-fluid">
+ <div class="row form-group">
+ {% if rtl %}
+ <div class="col-sm-11 col-md-10">
+ {% include 'oscar/categories.html' %}
+ </div>
+ <label class="col-sm-3 col-md-2">{{ _('Default categories') }}</label>
+ {% else %}
+ <label class="col-sm-3 col-md-2">{{ _('Default categories') }}</label>
+ <div class="col-sm-11 col-md-10 search-categories">
+ {% include 'oscar/categories.html' %}
+ </div>
+ {% endif %}
+ </div>
+ {% set language_label = _('Search language') %}
+ {% set language_info = _('What language do you prefer for search?') %}
+ {{ preferences_item_header(language_info, language_label, rtl) }}
+ {% include 'oscar/languages.html' %}
+ {{ preferences_item_footer(language_info, language_label, rtl) }}
+
+ {% set locale_label = _('Interface language') %}
+ {% set locale_info = _('Change the language of the layout') %}
+ {{ preferences_item_header(locale_info, locale_label, rtl) }}
+ <select class="form-control" name='locale'>
+ {% for locale_id,locale_name in locales.items() | sort %}
+ <option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
+ {% endfor %}
+ </select>
+ {{ preferences_item_footer(locale_info, locale_label, rtl) }}
+
+ {% set autocomplete_label = _('Autocomplete') %}
+ {% set autocomplete_info = _('Find stuff as you type') %}
+ {{ preferences_item_header(autocomplete_info, autocomplete_label, rtl) }}
+ <select class="form-control" name="autocomplete">
+ <option value=""> - </option>
+ {% for backend in autocomplete_backends %}
+ <option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
+ {% endfor %}
+ </select>
+ {{ preferences_item_footer(autocomplete_info, autocomplete_label, rtl) }}
+
+ {% set image_proxy_label = _('Image proxy') %}
+ {% set image_proxy_info = _('Proxying image results through searx') %}
+ {{ preferences_item_header(image_proxy_info, image_proxy_label, rtl) }}
+ <select class="form-control" name='image_proxy'>
+ <option value="1" {% if image_proxy %}selected="selected"{% endif %}>{{ _('Enabled') }}</option>
+ <option value="" {% if not image_proxy %}selected="selected"{% endif %}>{{ _('Disabled')}}</option>
+ </select>
+ {{ preferences_item_footer(image_proxy_info, image_proxy_label, rtl) }}
+
+ {% set method_label = _('Method') %}
+ {% set method_info = _('Change how forms are submited, <a href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods" rel="external">learn more about request methods</a>') %}
+ {{ preferences_item_header(method_info, method_label, rtl) }}
+ <select class="form-control" name='method'>
+ <option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
+ <option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
+ </select>
+ {{ preferences_item_footer(method_info, method_label, rtl) }}
+
+ {% set safesearch_label = _('SafeSearch') %}
+ {% set safesearch_info = _('Filter content') %}
+ {{ preferences_item_header(safesearch_info, safesearch_label, rtl) }}
+ <select class="form-control" name='safesearch'>
+ <option value="2" {% if safesearch == '2' %}selected="selected"{% endif %}>{{ _('Strict') }}</option>
+ <option value="1" {% if safesearch == '1' %}selected="selected"{% endif %}>{{ _('Moderate') }}</option>
+ <option value="0" {% if safesearch == '0' %}selected="selected"{% endif %}>{{ _('None') }}</option>
+ </select>
+ {{ preferences_item_footer(safesearch_info, safesearch_label, rtl) }}
+
+ {% set theme_label = _('Themes') %}
+ {% set theme_info = _('Change searx layout') %}
+ {{ preferences_item_header(theme_info, theme_label, rtl) }}
+ <select class="form-control" name="theme">
+ {% for name in themes %}
+ <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
+ {% endfor %}
+ </select>
+ {{ preferences_item_footer(theme_info, theme_label, rtl) }}
+
+ {{ preferences_item_header(_('Choose style for this theme'), _('Style'), rtl) }}
+ <select class="form-control" name='oscar-style'>
+ <option value="logicodev" >Logicodev</option>
+ <option value="pointhi" {% if cookies['oscar-style'] == 'pointhi' %}selected="selected"{% endif %}>Pointhi</option>
+ </select>
+ {{ preferences_item_footer(_('Choose style for this theme'), _('Style'), rtl) }}
+
+ {% set label = _('Results on new tabs') %}
+ {% set info = _('Open result links on new browser tabs') %}
+ {{ preferences_item_header(info, label, rtl) }}
+ <select class="form-control" name='results_on_new_tab'>
+ <option value="1" {% if results_on_new_tab %}selected="selected"{% endif %}>{{ _('On') }}</option>
+ <option value="0" {% if not results_on_new_tab %}selected="selected"{% endif %}>{{ _('Off')}}</option>
+ </select>
+ {{ preferences_item_footer(info, label, rtl) }}
+ </div>
+ </fieldset>
+ </div>
+ <div class="tab-pane active_if_nojs" id="tab_engine">
+
+ <!-- Nav tabs -->
+ <ul class="nav nav-tabs nav-justified hide_if_nojs" role="tablist" style="margin-bottom:20px;">
+ {% for categ in all_categories %}
+ <li{% if loop.first %} class="active"{% endif %}><a href="#tab_engine_{{ categ|replace(' ', '_') }}" role="tab" data-toggle="tab">{{ _(categ) }}</a></li>
+ {% endfor %}
+ </ul>
+
+ <noscript>
+ <h3>{{ _('Engines') }}</h3>
+ </noscript>
+
+ <!-- Tab panes -->
+ <div class="tab-content">
+ {% for categ in all_categories %}
+ <noscript><label>{{ _(categ) }}</label>
+ </noscript>
+ <div class="tab-pane{% if loop.first %} active{% endif %} active_if_nojs" id="tab_engine_{{ categ|replace(' ', '_') }}">
+ <div class="container-fluid">
+ <fieldset>
+ <div class="table-responsive">
+ <table class="table table-hover table-condensed table-striped">
+ <tr>
+ {% if not rtl %}
+ <th>{{ _("Allow") }}</th>
+ <th>{{ _("Engine name") }}</th>
+ <th>{{ _("Shortcut") }}</th>
+ <th>{{ _("Supports selected language") }}</th>
+ <th>{{ _("SafeSearch") }}</th>
+ <th>{{ _("Time range") }}</th>
+ <th>{{ _("Avg. time") }}</th>
+ <th>{{ _("Max time") }}</th>
+ {% else %}
+ <th>{{ _("Max time") }}</th>
+ <th>{{ _("Avg. time") }}</th>
+ <th>{{ _("SafeSearch") }}</th>
+ <th>{{ _("Supports selected language") }}</th>
+ <th>{{ _("Shortcut") }}</th>
+ <th>{{ _("Engine name") }}</th>
+ <th>{{ _("Allow") }}</th>
+ {% endif %}
+ </tr>
+ {% for search_engine in engines_by_category[categ] %}
+ {% if not search_engine.private %}
+ <tr>
+ {% if not rtl %}
+ <td class="onoff-checkbox">
+ {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
+ </td>
+ <th>{{ search_engine.name }}</th>
+ <td>{{ shortcuts[search_engine.name] }}</td>
+ <td><input type="checkbox" {{ "checked" if current_language == 'all' or current_language in search_engine.supported_languages or current_language.split('-')[0] in search_engine.supported_languages else ""}} readonly="readonly" disabled="disabled"></td>
+ <td><input type="checkbox" {{ "checked" if search_engine.safesearch==True else ""}} readonly="readonly" disabled="disabled"></td>
+ <td><input type="checkbox" {{ "checked" if search_engine.time_range_support==True else ""}} readonly="readonly" disabled="disabled"></td>
+ <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
+ <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
+ {% else %}
+ <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
+ <td class="{{ 'danger' if stats[search_engine.name]['warn_time'] else '' }}">{{ 'N/A' if stats[search_engine.name].time==None else stats[search_engine.name].time }}</td>
+ <td><input type="checkbox" {{ "checked" if search_engine.safesearch==True else ""}} readonly="readonly" disabled="disabled"></td>
+ <td><input type="checkbox" {{ "checked" if current_language == 'all' or current_language in search_engine.supported_languages or current_language.split('-')[0] in search_engine.supported_languages else ""}} readonly="readonly" disabled="disabled"></td>
+ <td>{{ shortcuts[search_engine.name] }}</td>
+ <th>{{ search_engine.name }}</th>
+ <td class="onoff-checkbox">
+ {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
+ </td>
+ {% endif %}
+ </tr>
+ {% endif %}
+ {% endfor %}
+ </table>
+ </div>
+ </fieldset>
+ </div>
+ </div>
+ {% endfor %}
+ </div>
+ </div>
+ <div class="tab-pane active_if_nojs" id="tab_plugins">
+ <noscript>
+ <h3>{{ _('Plugins') }}</h3>
+ </noscript>
+ <fieldset>
+ <div class="container-fluid">
+ {% for plugin in plugins %}
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h3 class="panel-title">{{ _(plugin.name) }}</h3>
+ </div>
+ <div class="panel-body">
+ <div class="col-xs-6 col-sm-4 col-md-6">{{ _(plugin.description) }}</div>
+ <div class="col-xs-6 col-sm-4 col-md-6">
+ <div class="onoff-checkbox">
+ {{ checkbox_toggle('plugin_' + plugin.id, plugin.id not in allowed_plugins) }}
+ </div>
+ </div>
+ </div>
+ </div>
+ {% endfor %}
+ </div>
+ </fieldset>
+ </div>
+
+ {% if answerers %}
+ <div class="tab-pane active_if_nojs" id="tab_answerers">
+ <noscript>
+ <h3>{{ _('Answerers') }}</h3>
+ </noscript>
+ <p class="text-muted" style="margin:20px 0;">
+ {{ _('This is the list of searx\'s instant answering modules.') }}
+ </p>
+ <table class="table table-striped">
+ <tr>
+ <th class="text-muted">{{ _('Name') }}</th>
+ <th class="text-muted">{{ _('Keywords') }}</th>
+ <th class="text-muted">{{ _('Description') }}</th>
+ <th class="text-muted">{{ _('Examples') }}</th>
+ </tr>
+
+ {% for answerer in answerers %}
+ <tr>
+ <td class="text-muted">{{ answerer.info.name }}</td>
+ <td class="text-muted">{{ answerer.keywords|join(', ') }}</td>
+ <td class="text-muted">{{ answerer.info.description }}</td>
+ <td class="text-muted">{{ answerer.info.examples|join(', ') }}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ </div>
+ {% endif %}
+
+ <div class="tab-pane active_if_nojs" id="tab_cookies">
+ <noscript>
+ <h3>{{ _('Cookies') }}</h3>
+ </noscript>
+ <p class="text-muted" style="margin:20px 0;">
+ {{ _('This is the list of cookies and their values searx is storing on your computer.') }}<br />
+ {{ _('With that list, you can assess searx transparency.') }}<br />
+ </p>
+ {% if cookies %}
+ <table class="table table-striped">
+ <tr>
+ <th class="text-muted" style="padding-right:40px;">{{ _('Cookie name') }}</th>
+ <th class="text-muted">{{ _('Value') }}</th>
+ </tr>
+
+ {% for cookie in cookies %}
+ <tr>
+ <td class="text-muted" style="padding-right:40px;">{{ cookie }}</td>
+ <td class="text-muted">{{ cookies[cookie] }}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ {% else %}
+ {% include 'oscar/messages/no_cookies.html' %}
+ {% endif %}
+ </div>
+ </div>
+ <p class="text-muted" style="margin:20px 0;">{{ _('These settings are stored in your cookies, this allows us not to store this data about you.') }}
+ <br />
+ {{ _("These cookies serve your sole convenience, we don't use these cookies to track you.") }}
+ </p>
+
+ <input type="submit" class="btn btn-primary" value="{{ _('save') }}" />
+ <a href="{{ url_for('index') }}"><div class="btn btn-default">{{ _('back') }}</div></a>
+ <a href="{{ url_for('clear_cookies') }}"><div class="btn btn-default">{{ _('Reset defaults') }}</div></a>
+ </form>
+</div>
+{% endblock %}
diff --git a/searx/templates/oscar/result_templates/code.html b/searx/templates/oscar/result_templates/code.html
new file mode 100644
index 0000000..ba74d03
--- /dev/null
+++ b/searx/templates/oscar/result_templates/code.html
@@ -0,0 +1,18 @@
+{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
+
+{{ result_header(result, favicons) }}
+{{ result_sub_header(result) }}
+
+{% if result.content %}<p class="result-content">{{ result.content|safe }}</p>{% endif %}
+
+{% if result.repository %}<p class="result-content">{{ icon('file') }} <a href="{{ result.repository }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.repository }}</a></p>{% endif %}
+
+<div dir="ltr">
+{{ result.codelines|code_highlighter(result.code_language)|safe }}
+</div>
+
+{% if rtl %}
+{{ result_footer_rtl(result) }}
+{% else %}
+{{ result_footer(result) }}
+{% endif %}
diff --git a/searx/templates/oscar/result_templates/default.html b/searx/templates/oscar/result_templates/default.html
new file mode 100644
index 0000000..3ed0f31
--- /dev/null
+++ b/searx/templates/oscar/result_templates/default.html
@@ -0,0 +1,31 @@
+{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon with context %}
+
+{{ result_header(result, favicons) }}
+{{ result_sub_header(result) }}
+
+{% if result.embedded %}
+ <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader disabled_if_nojs" data-toggle="collapse" data-target="#result-media-{{ index }}" data-btn-text-collapsed="{{ _('show media') }}" data-btn-text-not-collapsed="{{ _('hide media') }}">{{ icon('music') }} {{ _('show media') }}</a></small>
+{% endif %}
+
+{% if result.embedded %}
+<div id="result-media-{{ index }}" class="collapse">
+ {{ result.embedded|safe }}
+</div>
+{% endif %}
+
+{% if result.img_src %}
+<div class="container-fluid">
+ <div class="row">
+<img src="{{ image_proxify(result.img_src) }}" alt="{{ result.title|striptags }}" title="{{ result.title|striptags }}" style="width: auto; max-height: 60px; min-height: 60px;" class="col-xs-2 col-sm-4 col-md-4 result-content">
+{% if result.content %}<p class="result-content col-xs-8 col-sm-8 col-md-8">{{ result.content|safe }}</p>{% endif %}
+ </div>
+</div>
+{% else %}
+{% if result.content %}<p class="result-content">{{ result.content|safe }}</p>{% endif %}
+{% endif %}
+
+{% if rtl %}
+{{ result_footer_rtl(result) }}
+{% else %}
+{{ result_footer(result) }}
+{% endif %}
diff --git a/searx/templates/oscar/result_templates/images.html b/searx/templates/oscar/result_templates/images.html
new file mode 100644
index 0000000..b23f349
--- /dev/null
+++ b/searx/templates/oscar/result_templates/images.html
@@ -0,0 +1,39 @@
+{% from 'oscar/macros.html' import draw_favicon %}
+
+<a href="{{ result.img_src }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %} data-toggle="modal" data-target="#modal-{{ index }}-{{pageno}}">
+ <img src="{% if result.thumbnail_src %}{{ image_proxify(result.thumbnail_src) }}{% else %}{{ image_proxify(result.img_src) }}{% endif %}" alt="{{ result.title|striptags }}" title="{{ result.title|striptags }}" class="img-thumbnail">
+</a>
+
+<div class="modal fade" id="modal-{{ index }}-{{ pageno }}" tabindex="-1" role="dialog" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-wrapper">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
+ <h4 class="modal-title">{% if result.engine~".png" in favicons %}{{ draw_favicon(result.engine) }} {% endif %}{{ result.title|striptags }}</h4>
+ </div>
+ <div class="modal-body">
+ <img class="img-responsive center-block" src="{% if result.thumbnail_src %}{{ image_proxify(result.thumbnail_src) }}{% else %}{{ image_proxify(result.img_src) }}{% endif %}" alt="{{ result.title|striptags }}">
+ {% if result.author %}<span class="photo-author">{{ result.author }}</span><br />{% endif %}
+ {% if result.content %}
+ <p class="result-content">
+ {{ result.content }}
+ </p>
+ {% endif %}
+ </div>
+ <div class="modal-footer">
+ <div class="clearfix"></div>
+ <span class="label label-default pull-right">{{ result.engine }}</span>
+ <p class="text-muted pull-left">{{ result.pretty_url }}</p>
+ <div class="clearfix"></div>
+ <div class="row">
+ <div class="col-md-6">
+ <a href="{{ result.img_src }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %} class="btn btn-default">{{ _('Get image') }}</a>
+ </div>
+ <div class="col-md-6">
+ <a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %} class="btn btn-default">{{ _('View source') }}</a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/searx/templates/oscar/result_templates/map.html b/searx/templates/oscar/result_templates/map.html
new file mode 100644
index 0000000..822c7cd
--- /dev/null
+++ b/searx/templates/oscar/result_templates/map.html
@@ -0,0 +1,72 @@
+{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
+
+{{ result_header(result, favicons) }}
+{{ result_sub_header(result) }}
+
+{% if (result.latitude and result.longitude) or result.boundingbox %}
+ <small> &bull; <a class="text-info btn-collapse collapsed searx_init_map cursor-pointer disabled_if_nojs" data-toggle="collapse" data-target="#result-map-{{ index }}" data-leaflet-target="osm-map-{{ index }}" data-map-lon="{{ result.longitude }}" data-map-lat="{{ result.latitude }}" {% if result.boundingbox %}data-map-boundingbox='{{ result.boundingbox|tojson|safe }}'{% endif %} {% if result.geojson %}data-map-geojson='{{ result.geojson|tojson|safe }}'{% endif %} data-btn-text-collapsed="{{ _('show map') }}" data-btn-text-not-collapsed="{{ _('hide map') }}">{{ icon('globe') }} {{ _('show map') }}</a></small>
+{% endif %}
+
+{% if result.osm and (result.osm.type and result.osm.id) %}
+ <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer searx_overpass_request disabled_if_nojs" data-toggle="collapse" data-target="#result-overpass-{{ index }}" data-osm-type="{{ result.osm.type }}" data-osm-id="{{ result.osm.id }}" data-result-table="result-overpass-table-{{ index }}" data-result-table-loadicon="result-overpass-table-loading-{{ index }}" data-btn-text-collapsed="{{ _('show details') }}" data-btn-text-not-collapsed="{{ _('hide details') }}">{{ icon('map-marker') }} {{ _('show details') }}</a></small>
+{% endif %}
+
+{# {% if (result.latitude and result.longitude) %}
+ <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer disabled_if_nojs" data-toggle="collapse" data-target="#result-geodata-{{ index }}" data-btn-text-collapsed="{{ _('show geodata') }}" data-btn-text-not-collapsed="{{ _('hide geodata') }}">{{ icon('map-marker') }} {{ _('show geodata') }}</a></small>
+{% endif %} #}
+
+<div class="container-fluid">
+
+{% if result.address %}
+<p class="row result-content result-adress col-xs-12 col-sm-5 col-md-4" itemscope itemtype="http://schema.org/PostalAddress">
+ {% if result.address.name %}
+ <strong itemprop="name">{{ result.address.name }}</strong><br/>
+ {% endif %}
+ {% if result.address.road %}
+ <span itemprop="streetAddress">
+ {% if result.address.house_number %}{{ result.address.house_number }}, {% endif %}
+ {{ result.address.road }}
+ </span><br/>
+ {% endif %}
+ {% if result.address.locality %}
+ <span itemprop="addressLocality">{{ result.address.locality }}</span>
+ {% if result.address.postcode %}, <span itemprop="postalCode">{{ result.address.postcode }}</span>{% endif %}
+ <br/>
+ {% endif %}
+ {% if result.address.country %}
+ <span itemprop="addressCountry">{{ result.address.country }}</span>
+ {% endif %}
+</p>
+{% endif %}
+
+{% if result.osm and (result.osm.type and result.osm.id) %}
+ <div class="row result-content collapse col-xs-12 col-sm-7 col-md-8" id="result-overpass-{{ index }}"{% if rtl %} dir="ltr"{% endif %}>
+ <div class="text-center" id="result-overpass-table-loading-{{ index }}"><img src="{{ url_for('static', filename='img/loader.gif') }}" alt="Loading ..."/></div>
+ <table class="table table-striped table-condensed hidden" id="result-overpass-table-{{ index }}">
+ <tr><th>key</th><th>value</th></tr>
+ </table>
+ </div>
+{% endif %}
+
+{# {% if (result.latitude and result.longitude) %}
+ <div class="row collapse col-xs-12 col-sm-5 col-md-4" id="result-geodata-{{ index }}">
+ <strong>Longitude:</strong> {{ result.longitude }} <br/>
+ <strong>Latitude:</strong> {{ result.latitude }}
+ </div>
+{% endif %} #}
+
+{% if result.content %}<p class="row result-content col-xs-12 col-sm-12 col-md-12">{{ result.content|safe }}</p>{% endif %}
+
+</div>
+
+{% if (result.latitude and result.longitude) or result.boundingbox %}
+ <div class="collapse" id="result-map-{{ index }}">
+ <div style="height:300px; width:100%; margin: 10px 0;" id="osm-map-{{ index }}"></div>
+ </div>
+{% endif %}
+
+{% if rtl %}
+{{ result_footer_rtl(result) }}
+{% else %}
+{{ result_footer(result) }}
+{% endif %}
diff --git a/searx/templates/oscar/result_templates/torrent.html b/searx/templates/oscar/result_templates/torrent.html
new file mode 100644
index 0000000..bc2b30f
--- /dev/null
+++ b/searx/templates/oscar/result_templates/torrent.html
@@ -0,0 +1,25 @@
+{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
+
+{{ result_header(result, favicons) }}
+{{ result_sub_header(result) }}
+
+<p class="result-content">{{ icon('transfer') }} {{ _('Seeder') }} <span class="badge">{{ result.seed }}</span> &bull; {{ _('Leecher') }} <span class="badge">{{ result.leech }}</span>
+{% if result.filesize %}<br />{{ icon('floppy-disk') }} {{ _('Filesize') }}
+ <span class="badge">
+ {% if result.filesize < 1024 %}{{ result.filesize }} {{ _('Bytes') }}
+ {% elif result.filesize < 1024*1024 %}{{ '{0:0.2f}'.format(result.filesize/1024) }} {{ _('kiB') }}
+ {% elif result.filesize < 1024*1024*1024 %}{{ '{0:0.2f}'.format(result.filesize/1024/1024) }} {{ _('MiB') }}
+ {% elif result.filesize < 1024*1024*1024*1024 %}{{ '{0:0.2f}'.format(result.filesize/1024/1024/1024) }} {{ _('GiB') }}
+ {% else %}{{ '{0:0.2f}'.format(result.filesize/1024/1024/1024/1024) }} {{ _('TiB') }}{% endif %}
+ </span>{% endif %}
+{% if result.files %}<br />{{ icon('file') }} {{ _('Number of Files') }} <span class="badge">{{ result.files }}</span>{% endif %}
+
+{% if result.content %}<br />{{ result.content|safe }}{% endif %}
+
+</p>
+
+{% if rtl %}
+{{ result_footer_rtl(result) }}
+{% else %}
+{{ result_footer(result) }}
+{% endif %}
diff --git a/searx/templates/oscar/result_templates/videos.html b/searx/templates/oscar/result_templates/videos.html
new file mode 100644
index 0000000..36fb262
--- /dev/null
+++ b/searx/templates/oscar/result_templates/videos.html
@@ -0,0 +1,27 @@
+{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, result_footer_rtl, icon %}
+
+{{ result_header(result, favicons) }}
+{{ result_sub_header(result) }}
+
+{% if result.embedded %}
+ <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader disabled_if_nojs" data-toggle="collapse" data-target="#result-video-{{ index }}" data-btn-text-collapsed="{{ _('show video') }}" data-btn-text-not-collapsed="{{ _('hide video') }}">{{ icon('film') }} {{ _('show video') }}</a></small>
+{% endif %}
+
+{% if result.embedded %}
+<div id="result-video-{{ index }}" class="collapse">
+ {{ result.embedded|safe }}
+</div>
+{% endif %}
+
+<div class="container-fluid">
+ <div class="row">
+ <a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img class="thumbnail col-xs-6 col-sm-4 col-md-4 result-content" src="{{ image_proxify(result.thumbnail) }}" alt="{{ result.title|striptags }} {{ result.engine }}" /></a>
+ {% if result.content %}<p class="col-xs-12 col-sm-8 col-md-8 result-content">{{ result.content|safe }}</p>{% endif %}
+ </div>
+</div>
+
+{% if rtl %}
+{{ result_footer_rtl(result) }}
+{% else %}
+{{ result_footer(result) }}
+{% endif %}
diff --git a/searx/templates/oscar/results.html b/searx/templates/oscar/results.html
new file mode 100644
index 0000000..060b2a1
--- /dev/null
+++ b/searx/templates/oscar/results.html
@@ -0,0 +1,145 @@
+{% extends "oscar/base.html" %}
+{% macro search_form_attrs(pageno) -%}
+ {% for category in selected_categories %}<input type="hidden" name="category_{{ category }}" value="1"/>{% endfor %}
+ <input type="hidden" name="q" value="{{ q|e }}" />
+ <input type="hidden" name="pageno" value="{{ pageno }}" />
+ <input type="hidden" name="time_range" value="{{ time_range }}" />
+ <input type="hidden" name="language" value="{{ current_language }}" />
+{%- endmacro %}
+{%- macro search_url() %}{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}{% if time_range %}&amp;time_range={{ time_range }}{% endif %}{% if current_language != 'all' %}&amp;language={{ current_language }}{% endif %}{% endmacro -%}
+
+{% block title %}{{ q|e }} - {% endblock %}
+{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ search_url() }}&amp;format=rss">{% endblock %}
+{% block content %}
+ {% include 'oscar/search.html' %}
+ <div class="row">
+ <div class="col-sm-8" id="main_results">
+ <h1 class="sr-only">{{ _('Search results') }}</h1>
+
+ {% if corrections %}
+ <div class="result">
+ <span class="result_header text-muted form-inline pull-left suggestion_item">{{ _('Try searching for:') }}</span>
+ {% for correction in corrections %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" role="navigation" class="form-inline pull-left suggestion_item">
+ <input type="hidden" name="q" value="{{ correction }}">
+ <button type="submit" class="btn btn-default btn-xs">{{ correction }}</button>
+ </form>
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ {% if answers %}
+ {% for answer in answers %}
+ <div class="result well">
+ <span>{{ answer }}</span>
+ </div>
+ {% endfor %}
+ {% endif %}
+
+ {% for result in results %}
+ <div class="result {% if result['template'] %}result-{{ result.template|replace('.html', '') }}{% else %}result-default{% endif %}">
+ {% set index = loop.index %}
+ {% if result.template %}
+ {% include get_result_template('oscar', result['template']) %}
+ {% else %}
+ {% include 'oscar/result_templates/default.html' %}
+ {% endif %}
+ </div>
+ {% endfor %}
+
+ {% if not results and not answers %}
+ {% include 'oscar/messages/no_results.html' %}
+ {% endif %}
+
+ <div class="clearfix"></div>
+
+ {% if paging %}
+ {% if rtl %}
+ <div id="pagination">
+ <div class="pull-left">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left">
+ {{ search_form_attrs(pageno+1) }}
+ <button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-backward"></span> {{ _('next page') }}</button>
+ </form>
+ </div>
+ <div class="pull-right">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left">
+ {{ search_form_attrs(pageno-1) }}
+ <button type="submit" class="btn btn-default" {% if pageno == 1 %}disabled{% endif %}><span class="glyphicon glyphicon-forward"></span> {{ _('previous page') }}</button>
+ </form>
+ </div>
+ </div><!-- /#pagination -->
+ <div class="clearfix"></div>
+ {% else %}
+ <div id="pagination">
+ <div class="pull-left">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left">
+ {{ search_form_attrs(pageno-1) }}
+ <button type="submit" class="btn btn-default" {% if pageno == 1 %}disabled{% endif %}><span class="glyphicon glyphicon-backward"></span> {{ _('previous page') }}</button>
+ </form>
+ </div>
+ <div class="pull-right">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left">
+ {{ search_form_attrs(pageno+1) }}
+ <button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-forward"></span> {{ _('next page') }}</button>
+ </form>
+ </div>
+ </div><!-- /#pagination -->
+ <div class="clearfix"></div>
+ {% endif %}
+ {% endif %}
+ </div><!-- /#main_results -->
+
+ <div class="col-sm-4" id="sidebar_results">
+ {% if number_of_results != '0' %}
+ <p><small>{{ _('Number of results') }}: {{ number_of_results }}</small></p>
+ {% endif %}
+ {% if infoboxes %}
+ {% for infobox in infoboxes %}
+ {% include 'oscar/infobox.html' %}
+ {% endfor %}
+ {% endif %}
+
+ {% if suggestions %}
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h4 class="panel-title">{{ _('Suggestions') }}</h4>
+ </div>
+ <div class="panel-body">
+ {% for suggestion in suggestions %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" role="navigation" class="form-inline pull-{% if rtl %}right{% else %}left{% endif %} suggestion_item">
+ <input type="hidden" name="q" value="{{ suggestion }}">
+ <button type="submit" class="btn btn-default btn-xs">{{ suggestion }}</button>
+ </form>
+ {% endfor %}
+ </div>
+ </div>
+ {% endif %}
+
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h4 class="panel-title">{{ _('Links') }}</h4>
+ </div>
+ <div class="panel-body">
+ <form role="form">
+ <div class="form-group">
+ <label for="search_url">{{ _('Search URL') }}</label>
+ <input id="search_url" type="url" class="form-control select-all-on-click cursor-text" name="search_url" value="{{ search_url() }}" readonly>
+ </div>
+ </form>
+
+ <label>{{ _('Download results') }}</label>
+ <div class="clearfix"></div>
+ {% for output_type in ('csv', 'json', 'rss') %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="form-inline pull-{% if rtl %}right{% else %}left{% endif %} result_download">
+ {{ search_form_attrs(pageno) }}
+ <input type="hidden" name="format" value="{{ output_type }}">
+ <button type="submit" class="btn btn-default">{{ output_type }}</button>
+ </form>
+ {% endfor %}
+ <div class="clearfix"></div>
+ </div>
+ </div>
+ </div><!-- /#sidebar_results -->
+ </div>
+{% endblock %}
diff --git a/searx/templates/oscar/search.html b/searx/templates/oscar/search.html
new file mode 100644
index 0000000..59ee468
--- /dev/null
+++ b/searx/templates/oscar/search.html
@@ -0,0 +1,24 @@
+{% from 'oscar/macros.html' import icon %}
+<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form" role="search">
+ <div class="row">
+ <div class="col-xs-12 col-md-8">
+ <div class="input-group search-margin">
+ <input type="search" name="q" class="form-control" id="q" placeholder="{{ _('Search for...') }}" autocomplete="off" value="{{ q }}">
+ <span class="input-group-btn">
+ <button type="submit" class="btn btn-default"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
+ </span>
+ </div>
+ </div>
+ <div class="col-xs-6 col-md-2 search-margin">
+ {% include 'oscar/time-range.html' %}
+ </div>
+ <div class="col-xs-6 col-md-2 search-margin">
+ {% include 'oscar/languages.html' %}
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-sm-12">
+ {% include 'oscar/categories.html' %}
+ </div>
+ </div>
+</form><!-- / #search_form_full -->
diff --git a/searx/templates/oscar/search_full.html b/searx/templates/oscar/search_full.html
new file mode 100644
index 0000000..6fdae40
--- /dev/null
+++ b/searx/templates/oscar/search_full.html
@@ -0,0 +1,18 @@
+{% from 'oscar/macros.html' import icon %}
+
+<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form" role="search">
+ {% if rtl %}
+ <div class="input-group">
+ {% else %}
+ <div class="input-group col-md-8 col-md-offset-2">
+ {% endif %}
+ <input type="search" name="q" class="form-control input-lg autofocus" id="q" placeholder="{{ _('Search for...') }}" autocomplete="off" value="{{ q }}">
+ <span class="input-group-btn">
+ <button type="submit" class="btn btn-default input-lg"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
+ </span>
+ </div>
+ <div class="col-md-8 col-md-offset-2 advanced">
+ {% include 'oscar/advanced.html' %}
+ </div>
+
+</form><!-- / #search_form_full -->
diff --git a/searx/templates/oscar/stats.html b/searx/templates/oscar/stats.html
new file mode 100644
index 0000000..0fb1042
--- /dev/null
+++ b/searx/templates/oscar/stats.html
@@ -0,0 +1,33 @@
+{% extends "oscar/base.html" %}
+{% block title %}{{ _('stats') }} - {% endblock %}
+{% block content %}
+<div class="container-fluid">
+ <h1>{{ _('Engine stats') }}</h1>
+ <div class="row">
+ {% for stat_name,stat_category in stats %}
+ <div class="col-xs-12 col-sm-12 col-md-6">
+ <h3>{{ stat_name }}</h3>
+ <div class="container-fluid">
+ {% for engine in stat_category %}
+ <div class="row">
+ <div class="col-sm-4 col-md-4">{{ engine.name }}</div>
+ <div class="col-sm-8 col-md-8">
+ <div class="progress">
+ <div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="{{ '%i'|format(engine.avg) }}" aria-valuemin="0" aria-valuemax="100" style="width: {{ engine.percentage }}%;">
+ {{ '%.02f'|format(engine.avg) }}
+ </div>
+ </div>
+ </div>
+ </div>
+ {% endfor %}
+ {% if not stat_category %}
+ <div class="col-sm-12 col-md-12">
+ {% include 'oscar/messages/no_data_available.html' %}
+ </div>
+ {% endif %}
+ </div>
+ </div>
+ {% endfor %}
+ </div>
+</div>
+{% endblock %}
diff --git a/searx/templates/oscar/time-range.html b/searx/templates/oscar/time-range.html
new file mode 100644
index 0000000..d5efe91
--- /dev/null
+++ b/searx/templates/oscar/time-range.html
@@ -0,0 +1,17 @@
+<select name="time_range" id="time-range" class="custom-select form-control">
+ <option id="time-range-anytime" value="" {{ "selected" if time_range=="" or not time_range else ""}}>
+ {{ _('Anytime') }}
+ </option>
+ <option id="time-range-day" value="day" {{ "selected" if time_range=="day" else ""}}>
+ {{ _('Last day') }}
+ </option>
+ <option id="time-range-week" value="week" {{ "selected" if time_range=="week" else ""}}>
+ {{ _('Last week') }}
+ </option>
+ <option id="time-range-month" value="month" {{ "selected" if time_range=="month" else ""}}>
+ {{ _('Last month') }}
+ </option>
+ <option id="time-range-year" value="year" {{ "selected" if time_range=="year" else ""}}>
+ {{ _('Last year') }}
+ </option>
+</select>
diff --git a/searx/templates/pix-art/404.html b/searx/templates/pix-art/404.html
new file mode 100644
index 0000000..389bb5e
--- /dev/null
+++ b/searx/templates/pix-art/404.html
@@ -0,0 +1,9 @@
+{% extends "pix-art/base.html" %}
+{% block content %}
+<div class="center">
+ <h1>{{ _('Page not found') }}</h1>
+ {% autoescape false %}
+ <p>{{ _('Go to %(search_page)s.', search_page=unicode('<a href="{}">{}</a>').format(url_for('index'), _('search page'))) }}</p>
+ {% endautoescape %}
+</div>
+{% endblock %}
diff --git a/searx/templates/pix-art/about.html b/searx/templates/pix-art/about.html
new file mode 100644
index 0000000..f76a689
--- /dev/null
+++ b/searx/templates/pix-art/about.html
@@ -0,0 +1,4 @@
+{% extends 'pix-art/base.html' %}
+{% block content %}
+{% include '__common__/about.html' %}
+{% endblock %}
diff --git a/searx/templates/pix-art/base.html b/searx/templates/pix-art/base.html
new file mode 100644
index 0000000..6af8823
--- /dev/null
+++ b/searx/templates/pix-art/base.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}>
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
+ <meta name="keywords" content="searx, search, search engine, metasearch, meta search" />
+ <meta name="generator" content="searx/{{ searx_version }}">
+ <meta name="referrer" content="no-referrer">
+ <meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=1" />
+ <title>{% block title %}{% endblock %}searx</title>
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" type="text/css" media="screen" />
+ <link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.png') }}?v=2" />
+ {% block styles %}
+ {% endblock %}
+ {% block meta %}{% endblock %}
+ {% block head %}
+ {% endblock %}
+ <script type="text/javascript">
+ var favicons = [[]];
+ </script>
+ </head>
+ <body>
+ <div id="container">
+ {% block content %}
+ {% endblock %}
+ <script type="text/javascript">
+ searx = {};
+ </script>
+ <script src="{{ url_for('static', filename='js/searx.js') }}" ></script>
+ <script type="text/javascript">
+ load_images(1);
+ </script>
+ </div>
+ </body>
+</html>
diff --git a/searx/templates/pix-art/index.html b/searx/templates/pix-art/index.html
new file mode 100644
index 0000000..a0c61f9
--- /dev/null
+++ b/searx/templates/pix-art/index.html
@@ -0,0 +1,12 @@
+{% extends "pix-art/base.html" %}
+{% block content %}
+<div class="center">
+ <div class="title"><h1><img src="{{ url_for('static', filename='img/searx-pixel.png') }}" alt="searx Logo"/></h1></div>
+ {% include 'pix-art/search.html' %}
+ <p class="top_margin">
+ <a href="{{ url_for('about') }}" class="hmarg">{{ _('about') }}</a>
+ <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>
+ </p>
+</div>
+{% endblock %}
+
diff --git a/searx/templates/pix-art/preferences.html b/searx/templates/pix-art/preferences.html
new file mode 100644
index 0000000..05876de
--- /dev/null
+++ b/searx/templates/pix-art/preferences.html
@@ -0,0 +1,82 @@
+{% extends "legacy/base.html" %}
+{% block head %} {% endblock %}
+{% block content %}
+<div class="row">
+ <h2>{{ _('Preferences') }}</h2>
+
+ <form method="post" action="{{ url_for('preferences') }}" id="search_form">
+ <fieldset>
+ <legend>{{ _('Search language') }}</legend>
+ <p>
+ <select name='language'>
+ <option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Default language') }}</option>
+ {% for lang_id,lang_name,country_name,english_name in language_codes | sort(attribute=1) %}
+ <option value="{{ lang_id }}" {% if lang_id == current_language %}selected="selected"{% endif %}>{{ lang_name }} {% if country_name %}({{ country_name }}) {% endif %}- {{ lang_id }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Interface language') }}</legend>
+ <p>
+ <select name='locale'>
+ {% for locale_id,locale_name in locales.items() | sort %}
+ <option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Method') }}</legend>
+ <p>
+ <select name='method'>
+ <option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
+ <option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Themes') }}</legend>
+ <p>
+ <select name="theme">
+ {% for name in themes %}
+ <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Currently used search engines') }}</legend>
+
+ <table>
+ <tr>
+ <th>{{ _('Engine name') }}</th>
+ <th>{{ _('Allow') }} / {{ _('Block') }}</th>
+ </tr>
+ {% for categ in all_categories %}
+ {% for search_engine in engines_by_category[categ] %}
+
+ {% if not search_engine.private %}
+ <tr>
+ <td>{{ search_engine.name }} ({{ shortcuts[search_engine.name] }})&lrm;</td>
+ <td class="engine_checkbox">
+ <input type="checkbox" id="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}" name="engine_{{ search_engine.name }}__{{ categ }}"{% if (search_engine.name, categ) in disabled_engines %} checked="checked"{% endif %} />
+ <label class="allow" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Allow') }}</label>
+ <label class="deny" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Block') }}</label>
+ </td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ </table>
+ </fieldset>
+ <p class="small_font">{{ _('These settings are stored in your cookies, this allows us not to store this data about you.') }}
+ <br />
+ {{ _("These cookies serve your sole convenience, we don't use these cookies to track you.") }}
+ </p>
+
+ <input type="submit" value="{{ _('save') }}" />
+ <div class="{% if rtl %}left{% else %}right{% endif %} preferences_back"><a href="{{ url_for('index') }}">{{ _('back') }}</a></div>
+ </form>
+</div>
+{% endblock %}
diff --git a/searx/templates/pix-art/result_templates/default.html b/searx/templates/pix-art/result_templates/default.html
new file mode 100644
index 0000000..ada81e5
--- /dev/null
+++ b/searx/templates/pix-art/result_templates/default.html
@@ -0,0 +1,7 @@
+<a href="{{ result.url }}" title="{{ result.title | striptags }}" rel="noreferrer">
+ <canvas id="canvas-{{ pageno }}-{{ index }}" class="icon" width="16" height="16"></canvas>
+</a>
+<script type="text/javascript">
+favicons[{{ pageno }}][{{ index }}] = 'http://{{ result.url | extract_domain }}/favicon.ico';
+</script>
+
diff --git a/searx/templates/pix-art/result_templates/images.html b/searx/templates/pix-art/result_templates/images.html
new file mode 100644
index 0000000..d85f841
--- /dev/null
+++ b/searx/templates/pix-art/result_templates/images.html
@@ -0,0 +1,6 @@
+<div class="image_result">
+ <p>
+ <a href="{{ result.img_src }}" rel="noreferrer"><img src="{% if result.thumbnail_src %}{{ image_proxify(result.thumbnail_src) }}{% else %}{{ image_proxify(result.img_src) }}{% endif %}" title="{{ result.title|striptags }}" alt="{{ result.title|striptags }}" /></a>
+ <span class="url"><a href="{{ result.url }}" rel="noreferrer" class="small_font">{{ _('original context') }}</a></span>
+ </p>
+</div>
diff --git a/searx/templates/pix-art/results.html b/searx/templates/pix-art/results.html
new file mode 100644
index 0000000..8999e05
--- /dev/null
+++ b/searx/templates/pix-art/results.html
@@ -0,0 +1,32 @@
+{% if pageno > 1 %}
+ {% for result in results %}
+ {% set index = loop.index %}
+ {% include 'pix-art/result_templates/default.html' %}
+ {% endfor %}
+{% else %}
+{% extends "pix-art/base.html" %}
+{% block title %}{{ q|e }} - {% endblock %}
+{% block meta %}{% endblock %}
+{% block content %}
+<div id="logo"><a href="./"><img src="{{ url_for('static', filename='img/searx-pixel-small.png') }}" alt="searx Logo"/></a></div>
+<div class="preferences_container right"><a href="{{ url_for('preferences') }}" id="preferences"><span>preferences</span></a></div>
+<div class="small search center">
+ {% include 'pix-art/search.html' %}
+</div>
+<script type="text/javascript">
+ favicons[{{ pageno }}] = [];
+</script>
+<div id="results">
+ <span id="results_list">
+ {% for result in results %}
+ {% set index = loop.index %}
+ {% include 'pix-art/result_templates/default.html' %}
+ {% endfor %}
+ </span>
+ <div id="pagination">
+ <br />
+ <input type="button" onclick="load_more('{{ q|e }}', {{ pageno+1 }})" id="load_more" value="{{ _('Load more...') }}" />
+ </div>
+</div>
+{% endblock %}
+{% endif %}
diff --git a/searx/templates/pix-art/search.html b/searx/templates/pix-art/search.html
new file mode 100644
index 0000000..4d129ec
--- /dev/null
+++ b/searx/templates/pix-art/search.html
@@ -0,0 +1,9 @@
+<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form">
+ <div id="search_wrapper">
+ <input type="text" placeholder="{{ _('Search for...') }}" id="q" class="q" name="q" tabindex="1" size="100" {% if q %}value="{{ q }}"{% endif %}/>
+ <input type="submit" value="" id="search_submit" />
+ {% for category in categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ </div>
+</form>
diff --git a/searx/templates/pix-art/stats.html b/searx/templates/pix-art/stats.html
new file mode 100644
index 0000000..372447e
--- /dev/null
+++ b/searx/templates/pix-art/stats.html
@@ -0,0 +1,22 @@
+{% extends "legacy/base.html" %}
+{% block head %} {% endblock %}
+{% block content %}
+<h2>{{ _('Engine stats') }}</h2>
+
+{% for stat_name,stat_category in stats %}
+<div class="left">
+ <table>
+ <tr colspan="3">
+ <th>{{ stat_name }}</th>
+ </tr>
+ {% for engine in stat_category %}
+ <tr>
+ <td>{{ engine.name }}</td>
+ <td>{{ '%.02f'|format(engine.avg) }}</td>
+ <td class="percentage"><div style="width: {{ engine.percentage }}%">&nbsp;</div></td>
+ </tr>
+ {% endfor %}
+ </table>
+</div>
+{% endfor %}
+{% endblock %}
diff --git a/searx/testing.py b/searx/testing.py
new file mode 100644
index 0000000..0d17b2a
--- /dev/null
+++ b/searx/testing.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+"""Shared testing code."""
+
+
+import os
+import subprocess
+import traceback
+
+
+from os.path import dirname, join, abspath
+
+from splinter import Browser
+from unittest2 import TestCase
+
+
+class SearxTestLayer:
+ """Base layer for non-robot tests."""
+
+ __name__ = u'SearxTestLayer'
+
+ def setUp(cls):
+ pass
+ setUp = classmethod(setUp)
+
+ def tearDown(cls):
+ pass
+ tearDown = classmethod(tearDown)
+
+ def testSetUp(cls):
+ pass
+ testSetUp = classmethod(testSetUp)
+
+ def testTearDown(cls):
+ pass
+ testTearDown = classmethod(testTearDown)
+
+
+class SearxRobotLayer():
+ """Searx Robot Test Layer"""
+
+ def setUp(self):
+ os.setpgrp() # create new process group, become its leader
+
+ # get program paths
+ webapp = os.path.join(
+ os.path.abspath(os.path.dirname(os.path.realpath(__file__))),
+ 'webapp.py'
+ )
+ exe = 'python'
+
+ # set robot settings path
+ os.environ['SEARX_SETTINGS_PATH'] = abspath(
+ dirname(__file__) + '/settings_robot.yml')
+
+ # run the server
+ self.server = subprocess.Popen(
+ [exe, webapp],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT
+ )
+
+ def tearDown(self):
+ os.kill(self.server.pid, 9)
+ # remove previously set environment variable
+ del os.environ['SEARX_SETTINGS_PATH']
+
+
+# SEARXROBOTLAYER = SearxRobotLayer()
+def run_robot_tests(tests):
+ print('Running {0} tests'.format(len(tests)))
+ for test in tests:
+ with Browser() as browser:
+ test(browser)
+
+
+class SearxTestCase(TestCase):
+ """Base test case for non-robot tests."""
+
+ layer = SearxTestLayer
+
+
+if __name__ == '__main__':
+ import sys
+ # test cases
+ from tests import robot
+
+ base_dir = abspath(join(dirname(__file__), '../tests'))
+ if sys.argv[1] == 'robot':
+ test_layer = SearxRobotLayer()
+ errors = False
+ try:
+ test_layer.setUp()
+ run_robot_tests([getattr(robot, x) for x in dir(robot) if x.startswith('test_')])
+ except Exception:
+ errors = True
+ print('Error occured: {0}'.format(traceback.format_exc()))
+ test_layer.tearDown()
+ sys.exit(1 if errors else 0)
diff --git a/searx/translations/bg/LC_MESSAGES/messages.po b/searx/translations/bg/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..5ff2d20
--- /dev/null
+++ b/searx/translations/bg/LC_MESSAGES/messages.po
@@ -0,0 +1,843 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# ubone <van_ds_ff@mail.bg>, 2015
+# ubone <van_ds_ff@mail.bg>, 2016-2017
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: ubone <van_ds_ff@mail.bg>\n"
+"Language-Team: Bulgarian (http://www.transifex.com/asciimoo/searx/language/bg/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: bg\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "файлове"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "общо"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "музика"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "социална мрежа"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "изображения"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "видео"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "IT"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "новини"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "карта"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "наука"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Неправилни настройки, моля проверете предпочитанията си."
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "грешка при търсенето"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "преди {minutes} минута(минути)"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "преди {hours} час(ове), {minutes} минута(минути)"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr ""
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr ""
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr ""
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Страницата зарежда (сек)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Брой резултати"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr ""
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr ""
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Грешки"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr ""
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr ""
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Поправи HTTP връзки на HTTPS, ако е възможно"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Списък без страници."
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Автоматично зареждане на следващата страница."
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Отвори връзките в нов раздел."
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Отвори връзките в нов прозорец."
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Търси при избор на категория"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Търси веднага при избрана категория. Изключи за избор на няколко категории. (Необходим е JavaScript)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Показва IP-то ви и др. инфо, ако търсенето е \"ip\" или \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr ""
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Страницата не е намерена."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr ""
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "търси страница"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "предпочитания"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "относно"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Предпочитания"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Първоначални категории"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Език на търсене"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Търси на език"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Език"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Автоматично допълване"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Включено"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Изключено"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Метод"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Безопасно търсене"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Стриктно"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Умерено"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Нищо"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Облик"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Цвят"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Синьо (първоначален)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Виолетов"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Зелено"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "зелено-синьо"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Оранжево"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Червено"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Използвани търсачки в момента "
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Име на търсачка"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Категория"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Позволи"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Забрани"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Тези настройки се съхраняват във вашите бисквитки. Това ни позволява да не съхраняваме тази информация за вас."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Тези бисквитки служат за ваше удобство. Ние не ги използваме, за да ви следим."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "запази"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Върни първоначалните"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "назад"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Адрес на търсенето"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Свали резултатите"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Отговори"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Предложения"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "предишна страница"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "следваща страница"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Търси за..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Статистика на търсачката"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "оригинален контекст"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Сийдър"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Лийчър"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "магнитна връзка"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "торент файл"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Кликнете лупичката, за да изпълните търсене"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr ""
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Включено"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Изключено"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "кеширана"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Допълнителни настройки"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Затвори"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Грешка!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr ""
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr ""
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Общи"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Търсачки"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Добавки"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Отговори"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Бисквитки"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Кой език предпочитате за търсене?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Промени езика на оформлението"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Намери докато пишеш"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Филтрирай съдържание"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Промени оформлението на searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Избери стил за избрания облик"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Стил"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Пряк път"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Средно време"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Макс. време"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Име"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Ключови думи"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Описание"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Примери"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Това е списък на бисквитки с техните стойности, които searx съхранява на вашия компютър."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Име на бисквитката"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Стойност"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Резултати от търсенето"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Връзки"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Започни търсене"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "статистики"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Внимание!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Изглежда използвате searx за първи път."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Информация!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "В момента няма налични бисквитки."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Няма налична достъпна информация."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Съжалявам!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "не намерихме резултати. Моля пробвайте други ключови думи или търсете в повече категории."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Браво!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Настройките са успешно запазени."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Да му се не види!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Нещо се обърка."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "покажи медия"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "скрий медия"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Вземи изображение"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Покажи източник"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "покажи карта"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "скрий картата"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "покажи детайлите"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "скрий детайлите"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Размер на файла"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Байта"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "килобайт"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "мегабайт"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "гигабайт"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "терабайт"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Брой на Файлове"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "покажи видео"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "скрий видеото"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Зареди още..."
diff --git a/searx/translations/cs/LC_MESSAGES/messages.po b/searx/translations/cs/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..88f5bdd
--- /dev/null
+++ b/searx/translations/cs/LC_MESSAGES/messages.po
@@ -0,0 +1,842 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Clon <fillip1@seznam.cz>, 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Clon <fillip1@seznam.cz>\n"
+"Language-Team: Czech (http://www.transifex.com/asciimoo/searx/language/cs/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: cs\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "soubory"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "obecné"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "hudba"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "sociální media"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "obrázky"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "videa"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "IT"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "zprávy"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "mapa"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "věda"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Neplatné nastavení, upravte svoje předvolby"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "chyba vyhledávání"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "před {minutes} minutamy"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "před {hours} hodinami, {minutes} minutami"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Generátor náhodné hodnoty"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Generuje různé náhodné hodnoty"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Statistické funkce"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Vypočítá {functions} daného argumentu"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Strojový čas (s)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Načítání stránky (s)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Počet výsledků"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr ""
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr ""
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Chyb"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(ZASTARALÉ)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Tato položka byla nahrazena"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Předejít placené sekce přesměrováním na verze s otevřený přístupem pokud je to možné"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Pokud je to možné přepsat HTTP linky na HTTPS"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Nekonečné rolování"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Automaticky načíst další stránku při dorolování na konec současné"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Otevřít odkazy výsledků na nové panelu"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "V základu jsou výsledky jsou otevřeny ve stejném okně. Tento plugin přepisuje základní chování tak že se odkazy otevírají na nové panelu/okně. (požaduje JavaScript)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Hledat ve vybrané kategorii"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr ""
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Odstraňovač sledovacích URL"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Odstranit sledovací parametry z navrácených URL"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Zkratky jako ve Vim"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr ""
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Stránka nenalezena"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr ""
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "stránka vyhledávání"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "nastavení"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "o nás"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Nastavení"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Základní kategorie"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Jazyk hledání"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Základní jazyk"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Jazyk prostředí"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Automatické dokončování"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Aktivní"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Vypnuto"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Metoda"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Bezpečné vyhledávání"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Striktní"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Střední"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Žádné"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Motivy"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Barva"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Modrá (základní)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Zelená"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Oranžová"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Červená"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Nyní používaný vyhledávač"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Jméno vyhledávače"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Kategorie"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Povolit"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Blokovat"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Tato nastavení jsou uložena ve vašem cookies, to nám umožňuje taková data o vás neukládat."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Tato cookie slouží výhradně vašemu pohodlí, neužíváme je pro vaše sledování."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "uložit"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Obnovit základní"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "zpět"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Hledat URL"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Výsledky stahování"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Odpovědi"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Návrhy"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "předchozí stránka"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "další stránka"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Hledat…"
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Statistiky vyhledávače"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "původní kontext"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "soubor torrentu"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr ""
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Výsledky na novém panelu"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Zapnuto"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Vypnuto"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr ""
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Pokročilá nastavení"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Zavřít"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Chyba!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr ""
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "soukromý respektující, heknutelný meta-vyhledávač"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Obecné"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Vyhledávače"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Zásuvné moduly"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Odpovědi"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Jaký jazyk vyhledávání upřednostňujete?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Změnít jazyk prostředí"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtrovat obsah"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Změnit uspořádání searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Vybrat styl tohoto motivu"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Styl"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Zkratka"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Podporuje vybraný jazyk"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Čásový interval"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Prům. čas"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Max. čas"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Toto je seznam modulů okamžité odpovědi searxu."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Jméno"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Klíčová slova"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Popis"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Příklady"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Toto je seznam cookies a jejich hodnot které searx ukládá ve vašem počítači."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "S tímto seznamem můžete posoudit průhlednost searxu"
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Název cookie"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Hodnota"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Výsledky vyhledávání"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Odkazy"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Začít hledat"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "statistiky"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Kdykoli"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Dnes"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Tento týden"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Měsíc"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Rok"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Hlavy vzhůru!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Zdá se že používáte searx poprvé."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Informace!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "nyní, nejsou definovány žádné cookies."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Data nyní nejsou dostupná."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Pardón!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "nenašly jsme žádné výsledky. Prosím použíjte jiný dotaz nebo hledejte ve více kategoriích."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Výborně!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Nastavení úspěšně uloženo."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Ale ne!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Něco se nepovedlo."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "ukázat media"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "skrýt media"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Získat obrázek"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Zobrazit zdroj"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "zobrazit mapu"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "skrýt mapu"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "zobrazit detaily"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "skrýt detaily"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Velikost"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "bajtů"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Počet souborů"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "zobrazit video"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "skrýt video"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Načíst více…"
diff --git a/searx/translations/de/LC_MESSAGES/messages.po b/searx/translations/de/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..6848a91
--- /dev/null
+++ b/searx/translations/de/LC_MESSAGES/messages.po
@@ -0,0 +1,852 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Bamstam, 2017
+# Thomas Pointhuber, 2014-2015
+# Ettore Atalan <atalanttore@googlemail.com>, 2016
+# Jona Abdinghoff <jona.abdinghoff@gmail.com>, 2016
+# Max <theshirinzu@gmail.com>, 2015
+# pointhi, 2014
+# rike, 2014
+# stf <stefan.marsiske@gmail.com>, 2014
+# stf <stefan.marsiske@gmail.com>, 2014
+# Thomas Pointhuber, 2016-2017
+# rike, 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Thomas Pointhuber\n"
+"Language-Team: German (http://www.transifex.com/asciimoo/searx/language/de/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: de\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "Dateien"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "Allgemein"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "Musik"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "Soziale Medien"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "Bilder"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "Videos"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "IT"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "Neuigkeiten"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "Karte"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "Wissenschaft"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Inkorrekte Einstellungen. Bitte bearbeiten Sie diese"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "Suchfehler"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "vor {minutes} Minute(n)"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "vor {hours} Stunde(n), {minutes} Minute(n)"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Zufallszahlengenerator"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Erzeuge diverse Zufallszahlen"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Statistikfuntionen"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Berechne {functions} des Argument"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Suchmaschinen Zeit (sek)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Ladezeit (sek)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Trefferanzahl"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Punkte"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Punkte pro Treffer"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Fehler"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(OBSOLET)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Dieser Eintrag wurde überschrieben von"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "DOAI umschreiben"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Vermeidet Bezahlschranken durch die Weiterleitung zu der Open-Access Version falls möglich"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Wandelt wenn möglich HTTP Links in HTTPS Links um"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Undendliches Scrollen"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Lädt automatisch die nächste Seite wenn das Ende der aktuellen Seite erreicht wurde"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Öffne Links in einem neuen Browser-Tab"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Links werden normalerweise im gleichen Fenster geöffnet. Dieses Plugin überschreibt dieses Verhalten und öffnet Links in einem neuen Browser Tab bzw Fenster.\n(es wird JavaScript benötigt)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Starte Suche wenn Kategorie angeklickt wird"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Starte die Suche sofort wenn eine Kategorie ausgewählt wird. Es ist nicht mehr möglich mehrere Kategorien auszuwählen. (JavaScript benötigt)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Zeigt deine IP-Adresse wenn die Suche \"ip\" lautet, und deinen User-Agent wenn deine Suche \"user agent\" beinhaltet."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Tracker-URL-Entferner"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Tracker-Argumente von den zurückgegebenen URLs entfernen"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "An Vim angelehnte Tastenkombinationen"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Navigiere in der Ergebnisseite mit Vim ähnlichen Tastataurkombinationen (es wird JavaScript benötigt).\nDrücke \"h\" auf der Start bzw. Ergebnisseite um eine Hifefenster anzuzeigen"
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Seite nicht gefunden"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Gehe zu %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "Suchseite"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "Einstellungen"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "Über uns"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Einstellungen"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Standardkategorien"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Suchsprache"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Standardsprache"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Oberflächensprache"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Autovervollständigung"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Bilder-Proxy"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Aktiviert"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Deaktiviert"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Methode"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Sichere Suche (SafeSearch)"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Streng"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Moderat"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Keine"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Designs"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Farbe"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Blau (Standard)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violett"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Grün"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Cyan"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Orange"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Rot"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Aktuell benutzte Suchmaschinen"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Suchmaschinenname"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Kategorie"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Erlauben"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Blockieren"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Diese Informationen werden in Cookies auf Ihrem Rechner gespeichert, damit wir keine Ihrer persönlichen Daten speichern müssen."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Diese Cookies dienen einzig Ihrem Komfort. Wir verwenden sie nicht, um Sie zu überwachen."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "Speichern"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Zurücksetzen"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "Zurück"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Such-URL"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Ergebnisse herunterladen"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Antworten"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Vorschläge"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "vorherige Seite"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "nächste Seite"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Suche nach..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Suchmaschinenstatistiken"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "ursprüngliche Seite"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Seeder"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Leecher"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "Magnet Link"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "Torrent"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "klicke auf die Lupe, um die Suche zu starten"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Ergebnisse in neuem Tab"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Ein"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Aus"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "Im Cache"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Erweiterte Einstellungen"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Schließen"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Fehler!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Bereitgestellt von"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "eine privatsphären-respektierende, hackbare Metasuchmaschine"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "proxied"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Allgemein"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Suchmaschinen"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Erweiterungen"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Antworten"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "welche Sprache bevorzugst du für die Suche?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "ändere die Sprache des Layouts"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "zeige Vorschläge während der Eingabe an"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "tunnel Bilder durch searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "ändere wie Formulare übertragen werden, <a href=\"https://de.wikipedia.org/wiki/Hypertext_Transfer_Protocol#HTTP-Anfragemethoden\" rel=\"external\">lerne mehr über Anfragemethoden</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Inhalt filtern"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "ändere das Aussehen von searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Wähle den Stil für dieses Thema"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Aussehen"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Abkürzung"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Unterstützt die ausgewählten Sprachen"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Zeitbereich"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "mittlere Zeit"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "max. Zeit"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Dies ist die Liste der in searx verfügbaren Module für Sofortantworten "
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Name"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Schlüsselwörter"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Beschreibung"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Beispiele"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Diese Liste zeigt alle Daten welche searx auf deinem Computer speichert."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Mit dieser Liste können Sie die Transparenz von searx bewerten"
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Cookie-Name"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Wert"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Suchergebnisse"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Links"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Suche starten"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "Statistiken"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "beliebiger Zeitpunkt"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Letzter Tag"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Letzte Woche"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Letztes Monat"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Letztes Jahr"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Achtung!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Es sieht so aus, als ob das erstes mal mit searx arbeitest."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Information!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "derzeit sind keine Cookies gesetzt."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Es sind derzeit keine Daten vorhanden."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Entschuldigung!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "Es konnten keine Suchergebnisse gefunden werden. Bitte nutze einen anderen Suchbegriff, oder suche das gewünschte in einer anderen Kategorie. "
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Gut gemacht!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Einstellungen wurden erfolgreich gespeichert."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Oh nein!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Irgendetwas ist falsch gelaufen."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "Medien anzeigen"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "Medien verstecken"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Bild ansehen"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Seite besuchen"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "Karte anzeigen"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "Karte verstecken"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "Details anzeigen"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "Details verstecken"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Dateigröße"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bytes"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Anzahl der Dateien"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "Video anzeigen"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "Video verstecken"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Lade mehr..."
diff --git a/searx/translations/de_DE/LC_MESSAGES/messages.po b/searx/translations/de_DE/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..7beacc7
--- /dev/null
+++ b/searx/translations/de_DE/LC_MESSAGES/messages.po
@@ -0,0 +1,844 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Bamstam, 2016-2017
+# Benjamin Richter <benjamin@hacktherack.de>, 2015
+# cy8aer <cybaer42@web.de>, 2016-2017
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-05-19 20:17+0000\n"
+"Last-Translator: cy8aer <cybaer42@web.de>\n"
+"Language-Team: German (Germany) (http://www.transifex.com/asciimoo/searx/language/de_DE/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: de_DE\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "Dateien"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "Allgemein"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "Musik"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "Soziale Medien"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "Fotos"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "Videos"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "IT"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "Nachrichten"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "Karten"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "Wissenschaft"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Ungültige Auswahl, bitte überprüfen Sie die Einstellungen"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "Fehler bei der Suche"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "vor {minutes} Minute(n)"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "vor {hours} Stunde(n). {minutes} Minute(n)"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Zufallswertgenerator"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Zufallswerte generieren"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Statistik-Funktionen"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "{functions} der Argumente berechnen"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Zeitbedarf (s)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Ladezeit (s)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Anzahl Ergebnisse"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Punktwerte"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Punktwerte pro Ergebnis"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Fehler"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(OBSOLET)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Dieser Eintrag wurde ersetzt durch"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "DOAI-Umgehung"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Paywalls umgehen, indem wenn möglich auf Open-Access-Versionen von Publikationen umgeleitet wird"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Umschreiben von HTTP-Links nach HTTPS, wenn möglich"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Unbegrenztes Scrollen"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Nächste Seite automatisch laden, wenn zum Seitenende gescrollt wird"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Öffne Links in einem neuen Browser-Tab"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Suchergebnisse werden standardmäßig im gleichen Fenster geöffnet. Dieses Plug-in überschreibt dieses Standardverhalten und öffnet Links in neuen Tabs/Fenstern (benötigt JavaScript)."
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Suchen nach Kategorie"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Suche sofort durchführen, wenn eine Kategorie ausgewählt wird. Deaktivieren Sie diese Option, um mehrere Kategorien auswählen zu können (benötigt JavaScript)."
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Zeigt Ihre IP-Adresse an, wenn \"ip\" als Suchanfrage eingegeben wird und den User Agent bzw. das verwendete Client-Programm, wenn die Suchanfrage den Ausdruck \"user agent\" enthält."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Tracking-URLs bereinigen"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Tracker-Argumente der erhaltenen URL entfernen"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Vim-ähnliche Hotkeys"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Durch Suchergebnisse navigieren mit Vim-ähnlichen Hotkeys (benötigt JavaScript). \"h\" drücken auf der Hauptseite oder der Ergebnisseite, um Hilfe zu erhalten."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Seite nicht gefunden"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Gehe zu %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "Seite durchsuchen"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "Einstellungen"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "Über uns"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Einstellungen"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Standardkategorien"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Suchsprache"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Standardsprache"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Sprache der Benutzeroberfläche"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Autovervollständigen"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Proxy-Server für Bilder"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Aktiviert"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Deaktiviert"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Methode"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "SafeSearch"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Streng"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Moderat"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Keine"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Oberflächen"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Farbe"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Blau (Standard)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violett"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Grün"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Türkis"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Orange"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Rot"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Momentan genutzte Suchmaschinen"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Suchmaschinen-Name"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Kategorie"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Zulassen"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Blockieren"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Diese Einstellungen werden in Ihren Cookies gespeichert, deshalb müssen wir diese persönlichen Daten nicht bei uns speichern."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Diese Cookies ermöglichen lediglich eine komfortablere Nutzung, wir verwenden diese Cookies nicht, um Sie zu tracken."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "speichern"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Voreinstellungen wiederherstellen"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "zurück"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Such-URL"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Suchergebnisse herunterladen"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Antworten"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Vorschläge"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "vorherige Seite"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "nächste Seite"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Suchen nach ..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Suchmaschinen-Statistiken"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "Ursprünglicher Kontext"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Seeder"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Leecher"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "Magnet-Link"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "Torrent-Datei"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Klicken Sie auf das Vergrößerungsglas, um die Suche zu starten"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Ergebnisse in neuen Tabs"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "An"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Aus"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "im Cache"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Erweiterte Einstellungen"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Schließen"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Fehler!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Bereitgestellt von"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "eine die Privatsphäre respektierende, hackbare Meta-Suchmaschine"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "via Proxy-Server"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Allgemein"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Suchmaschinen"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Plug-ins"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Instant Answers/Sofortantworten"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Welche Sprache möchten Sie für die Suche verwenden?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Sprache des Layouts ändern"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Bereits während der Eingabe suchen"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Bilder-Suchergebnisse über den searx-Proxy-Server laden"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "HTTP-Anfragemethode ändern <a href=\"https://de.wikipedia.org/wiki/Hypertext_Transfer_Protocol#HTTP-Anfragemethoden\" rel=\"external\">(weiterführende Informationen zu HTTP-Anfragemethoden)</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Inhalte filtern"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "searx-Layout ändern"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Stilrichtung für diese Benutzeroberfläche auswählen"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Stilrichtung"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Kürzel"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Unterstützt die ausgewähle Sprache"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Zeitraum"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Durchschn. Zeit"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Maximale Zeit"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Auflistung der searx-Module für Sofortantworten:"
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Name"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Schlüsselwörter"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Beschreibung"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Beispiele"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Hier werden die Cookies und die gespeicherten Cookie-Informationen aufgelistet, die searx auf Ihrem Computer speichert."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Mit Hilfe dieser Auflistung können Sie die Transparenz der searx-Suche einschätzen."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Cookie-Name"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Wert"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Durchsuche Ergebnisse"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Links"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Suche starten"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "Statistiken"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Beliebiger Zeitunkt"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Gestern"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Letzte Woche"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Letzter Monat"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Letztes Jahr"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Aufgepasst!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Anscheinend benutzen Sie searx zum ersten Mal."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Zur Information!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "Zur Zeit sind keine Cookies definiert."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Zur Zeit sind keine Daten verfügbar."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Entschuldigung!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "Leider konnten wir keine Suchergebnisse finden. Bitte verwenden Sie eine andere Suchabfrage oder erweitern Sie die Suche auf mehr Kategorien."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Gut gemacht!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Einstellungen erfolgreich gespeichert."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Hoppla!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Ein Fehler ist aufgetreten."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "Medien anzeigen"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "Medien verbergen"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Bild herunterladen"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Quelle anzeigen"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "Karte anzeigen"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "Karte verbergen"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "Details anzeigen"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "Details verbergen"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Dateigröße"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bytes"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Anzahl Dateien"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "Video anzeigen"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "Video verbergen"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Mehr anzeigen ..."
diff --git a/searx/translations/el_GR/LC_MESSAGES/messages.po b/searx/translations/el_GR/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..56d254b
--- /dev/null
+++ b/searx/translations/el_GR/LC_MESSAGES/messages.po
@@ -0,0 +1,842 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Dimitris T. <dimitris@stinpriza.org>, 2015
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Adam Tauber <asciimoo@gmail.com>\n"
+"Language-Team: Greek (Greece) (http://www.transifex.com/asciimoo/searx/language/el_GR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: el_GR\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "αρχεία"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "γενικά"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "μουσική"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "κοινωνικά δίκτυα"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "εικόνες"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr ""
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr ""
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "νέα"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "χάρτης"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr ""
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr ""
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr ""
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr ""
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr ""
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr ""
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr ""
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr ""
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr ""
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Αριθμός αποτελεσμάτων"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr ""
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr ""
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Λάθη"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr ""
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr ""
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr ""
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr ""
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr ""
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr ""
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr ""
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr ""
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr ""
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr ""
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr ""
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr ""
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr ""
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "προτιμήσεις"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "σχετικά"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Προτιμήσεις"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Γλώσσα αναζήτησης"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Ενεργοποιημένο"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Απενεργοποιημένο"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Μέθοδος"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Θέματα"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Χρώμα"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Μπλε (προεπιλεγμένο)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Βιολετί"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Πράσινο"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Κυανό"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Πορτοκαλί"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Κόκκινο"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Κατηγορία"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "αποθήκευση"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "πίσω"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr ""
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr ""
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Απαντήσεις"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Προτάσεις"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "προηγούμενη σελίδα"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "επόμενη σελίδα"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Αναζήτηση για..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr ""
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr ""
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr ""
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr ""
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr ""
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr ""
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr ""
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr ""
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr ""
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr ""
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr ""
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Τι γλώσσα προτιμάτε για αναζήτηση;"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr ""
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Αποτελέσματα αναζήτησης"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Σύνδεσμοι"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Έναρξη αναζήτησης"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "στατιστικά"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr ""
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr ""
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr ""
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr ""
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Συγνώμη!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr ""
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr ""
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr ""
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr ""
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr ""
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "προβολή χάρτη"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "απόκρυψη χάρτη"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "προβολή λεπτομερειών"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "απόκρυψη λεπτομερειών"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Μέγεθος αρχείου"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr ""
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr ""
diff --git a/searx/translations/en/LC_MESSAGES/messages.po b/searx/translations/en/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..1f7f591
--- /dev/null
+++ b/searx/translations/en/LC_MESSAGES/messages.po
@@ -0,0 +1,695 @@
+# English translations for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-01-21 16:05+0100\n"
+"PO-Revision-Date: 2014-01-30 15:22+0100\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language: en\n"
+"Language-Team: en <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.2.0\n"
+
+#: searx/webapp.py:114
+msgid "files"
+msgstr ""
+
+#: searx/webapp.py:115
+msgid "general"
+msgstr ""
+
+#: searx/webapp.py:116
+msgid "music"
+msgstr ""
+
+#: searx/webapp.py:117
+msgid "social media"
+msgstr ""
+
+#: searx/webapp.py:118
+msgid "images"
+msgstr ""
+
+#: searx/webapp.py:119
+msgid "videos"
+msgstr ""
+
+#: searx/webapp.py:120
+msgid "it"
+msgstr ""
+
+#: searx/webapp.py:121
+msgid "news"
+msgstr ""
+
+#: searx/webapp.py:122
+msgid "map"
+msgstr ""
+
+#: searx/webapp.py:123
+msgid "science"
+msgstr ""
+
+#: searx/webapp.py:415
+msgid "{minutes} minute(s) ago"
+msgstr ""
+
+#: searx/webapp.py:417
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr ""
+
+#: searx/engines/__init__.py:185
+msgid "Page loads (sec)"
+msgstr ""
+
+#: searx/engines/__init__.py:189
+msgid "Number of results"
+msgstr ""
+
+#: searx/engines/__init__.py:193
+msgid "Scores"
+msgstr ""
+
+#: searx/engines/__init__.py:197
+msgid "Scores per result"
+msgstr ""
+
+#: searx/engines/__init__.py:201
+msgid "Errors"
+msgstr ""
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr ""
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr ""
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr ""
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr ""
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/default/index.html:8 searx/templates/default/index.html:12
+#: searx/templates/oscar/navbar.html:7 searx/templates/oscar/navbar.html:35
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr ""
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/default/index.html:10 searx/templates/oscar/about.html:3
+#: searx/templates/oscar/navbar.html:8 searx/templates/oscar/navbar.html:34
+#: searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/default/preferences.html:5
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/default/preferences.html:9
+#: searx/templates/oscar/preferences.html:36
+#: searx/templates/oscar/preferences.html:38
+msgid "Default categories"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/default/preferences.html:14
+#: searx/templates/oscar/preferences.html:44
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/default/preferences.html:17
+#: searx/templates/oscar/preferences.html:48
+#: searx/templates/pix-art/preferences.html:12
+msgid "Automatic"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/default/preferences.html:25
+#: searx/templates/oscar/preferences.html:55
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/default/preferences.html:35
+#: searx/templates/oscar/preferences.html:65
+msgid "Autocomplete"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/default/preferences.html:46
+#: searx/templates/oscar/preferences.html:76
+msgid "Image proxy"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/default/preferences.html:49
+#: searx/templates/oscar/preferences.html:80
+msgid "Enabled"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/default/preferences.html:50
+#: searx/templates/oscar/preferences.html:81
+msgid "Disabled"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/default/preferences.html:55
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/default/preferences.html:64
+#: searx/templates/oscar/preferences.html:94
+#: searx/templates/oscar/preferences.html:144
+#: searx/templates/oscar/preferences.html:150
+msgid "SafeSearch"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/default/preferences.html:67
+#: searx/templates/oscar/preferences.html:98
+msgid "Strict"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/default/preferences.html:68
+#: searx/templates/oscar/preferences.html:99
+msgid "Moderate"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/default/preferences.html:69
+#: searx/templates/oscar/preferences.html:100
+msgid "None"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/default/preferences.html:74
+#: searx/templates/oscar/preferences.html:104
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/default/preferences.html:84
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/default/preferences.html:88
+#: searx/templates/oscar/preferences.html:142
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/default/preferences.html:89
+msgid "Category"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/default/preferences.html:90
+#: searx/templates/default/preferences.html:101
+#: searx/templates/oscar/macros.html:71
+#: searx/templates/oscar/preferences.html:141
+#: searx/templates/oscar/preferences.html:153
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/default/preferences.html:90
+#: searx/templates/default/preferences.html:102
+#: searx/templates/oscar/macros.html:70
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/default/preferences.html:110
+#: searx/templates/oscar/preferences.html:235
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store "
+"this data about you."
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/default/preferences.html:112
+#: searx/templates/oscar/preferences.html:237
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/default/preferences.html:115
+#: searx/templates/oscar/preferences.html:240
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/default/preferences.html:116
+#: searx/templates/oscar/preferences.html:242
+msgid "Reset defaults"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/default/preferences.html:117
+#: searx/templates/oscar/preferences.html:241
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr ""
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/default/results.html:13
+#: searx/templates/oscar/results.html:110
+msgid "Search URL"
+msgstr ""
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/default/results.html:17
+#: searx/templates/oscar/results.html:115
+msgid "Download results"
+msgstr ""
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/default/results.html:35
+msgid "Answers"
+msgstr ""
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/default/results.html:43
+#: searx/templates/oscar/results.html:90
+msgid "Suggestions"
+msgstr ""
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/default/results.html:81
+#: searx/templates/oscar/results.html:51 searx/templates/oscar/results.html:63
+msgid "previous page"
+msgstr ""
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/default/results.html:92
+#: searx/templates/oscar/results.html:44 searx/templates/oscar/results.html:71
+msgid "next page"
+msgstr ""
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/default/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr ""
+
+#: searx/templates/courgette/stats.html:4 searx/templates/default/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/default/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/default/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/default/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/default/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:21
+msgid "magnet link"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/default/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:22
+msgid "torrent file"
+msgstr ""
+
+#: searx/templates/default/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr ""
+
+#: searx/templates/default/result_templates/code.html:3
+#: searx/templates/default/result_templates/default.html:3
+#: searx/templates/default/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:20
+msgid "cached"
+msgstr ""
+
+#: searx/templates/oscar/base.html:78
+msgid "Powered by"
+msgstr ""
+
+#: searx/templates/oscar/base.html:78
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr ""
+
+#: searx/templates/oscar/navbar.html:9 searx/templates/oscar/navbar.html:33
+msgid "home"
+msgstr ""
+
+#: searx/templates/oscar/navbar.html:14 searx/templates/oscar/navbar.html:24
+msgid "Toggle navigation"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:17
+#: searx/templates/oscar/preferences.html:25
+msgid "General"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:18
+#: searx/templates/oscar/preferences.html:126
+msgid "Engines"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:19
+#: searx/templates/oscar/preferences.html:187
+msgid "Plugins"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:20
+#: searx/templates/oscar/preferences.html:210
+msgid "Cookies"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:45
+msgid "What language do you prefer for search?"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:56
+msgid "Change the language of the layout"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:66
+msgid "Find stuff as you type"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:77
+msgid "Proxying image results through searx"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:86
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:95
+msgid "Filter content"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:105
+msgid "Change searx layout"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:143
+#: searx/templates/oscar/preferences.html:151
+msgid "Shortcut"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:145
+#: searx/templates/oscar/preferences.html:149
+msgid "Avg. time"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:146
+#: searx/templates/oscar/preferences.html:148
+msgid "Max time"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:213
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:214
+msgid "With that list, you can assess searx transparency."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:219
+msgid "Cookie name"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:220
+msgid "Value"
+msgstr ""
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr ""
+
+#: searx/templates/oscar/results.html:105
+msgid "Links"
+msgstr ""
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr ""
+
+#: searx/templates/oscar/search_full.html:15
+msgid "Show search filters"
+msgstr ""
+
+#: searx/templates/oscar/search_full.html:15
+msgid "Hide search filters"
+msgstr ""
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr ""
+
+#: searx/templates/oscar/messages/js_disabled.html:2
+msgid "Warning!"
+msgstr ""
+
+#: searx/templates/oscar/messages/js_disabled.html:3
+msgid "Please enable JavaScript to use full functionality of this site."
+msgstr ""
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr ""
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr ""
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr ""
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr ""
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr ""
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr ""
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr ""
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr ""
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr ""
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/images.html:23
+msgid "Get image"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/images.html:24
+msgid "View source"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr ""
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr ""
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr ""
+
+#~ msgid "Localization"
+#~ msgstr ""
+
+#~ msgid "Yes"
+#~ msgstr ""
+
+#~ msgid "No"
+#~ msgstr ""
+
diff --git a/searx/translations/eo/LC_MESSAGES/messages.po b/searx/translations/eo/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..25d270d
--- /dev/null
+++ b/searx/translations/eo/LC_MESSAGES/messages.po
@@ -0,0 +1,844 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Jack Stehn <jaxadeo@gmail.com>, 2017
+# juanda097 <juanda097@openmailbox.org>, 2015-2016
+# pizzaiolo, 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Jack Stehn <jaxadeo@gmail.com>\n"
+"Language-Team: Esperanto (http://www.transifex.com/asciimoo/searx/language/eo/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: eo\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "dosieroj"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "ĝenerala"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "muziko"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "sociaj retservoj"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "bildoj"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "videoj"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "komputiko"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "novaĵoj"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "mapo"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "scienco"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr ""
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "serĉa eraro"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "antaŭ {minutes} minuto(j)"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "antaŭ {hours} horo(j), {minutes} minuto(j)"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr ""
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr ""
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr ""
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Paĝŝarĝo (sekundoj)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Nombro da rezultoj"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Poentaroj"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Poentaroj por unu rezulto"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Eraroj"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr ""
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "DOAI-reverko"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Eviti pagomurojn alidirektante al liberaj versioj de eldonaĵoj kiam eblas"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Reverki HTTP ligiloj HTTP se eble"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Senfina rulumado"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Aŭtomate ŝarĝi la sekvan paĝon kiam rulumante al la subo de la nuna paĝo"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Malfermi rezultligilojn en novaj retumilaj langetoj"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Oni malfermas rezultojn en la sama langeto defaŭlte. Ĉi tiu aldonaĵo ŝanĝas la kutima agmaniero por malfermi ligilojn en novaj langetoj/fenestroj. (ĜavaSkripto bezonata)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Serĉi en elektita kategorio"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Serĉi tuj se oni elektas kategorion. Malŝaltu ĝin por elekti plurajn kategoriojn (ĜavaSkripto bezonata)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Montras vian IP-adreson se la serĉofrazo estas \"ip\" kaj vian klientan aplikaĵon se la serĉofrazo enhavas \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Forigilo de URL-spuriloj"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Forviŝi spuraj esprimoj de la URL"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Vim-ŝajnaj klavkomandoj"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Tranavigi serĉrezultojn per Vim-ŝajnaj klavkomandoj (ĜavaSkripto bezonata). Premu \"h\" por helptekstaro en rezultpaĝo."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Paĝo ne trovita"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Iri al %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "Serĉopaĝo"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "preferoj"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "pri"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Preferoj"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Defaŭltaj kategorioj"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Serĉolingvo"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Fasada lingvo"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Aŭtomate kompletigi"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Prokurila servilo por bildoj"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Ŝaltita"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Malŝaltita"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Metodo"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr " SekuraSerĉo"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Strikta"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Modera"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Neniu"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Temoj"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Koloro"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Blua (defaŭlta)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Viola"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Verda"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Bluverda"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Oranĝa"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Ruĝa"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr " Aktuale uzitajn serĉilojn"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr " Motora nomo"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Kategorio"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Permesi"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Bloki"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Tiuj agordoj estas konservitaj en viaj kuketoj kaj tio eblas, ke ni ne konservu tiujn datumojn pri vi."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Tiuj kuketoj estas nur por via plaĉo, ni ne uzas ilin por spuri vin."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "konservi"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Reagordi al defaŭlto"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "antaŭe"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Serĉi URL"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Alŝutaj rezultoj"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Respondoj"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Sugestoj"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr " antaŭa paĝo"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr " sekvanta paĝo"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Serĉi..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Statistikoj pri la motoro"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "originala kunteksto"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Fonto"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Ricevanto"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "magnetligilo"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "torentodosiero"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Alklaku la lupeon por serĉi"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Rezultoj en novaj langetoj"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Ŝaltita"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Malŝaltita"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "kaŝmemorigita"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Altgradaj agordoj"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Fermi"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr ""
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Funkciigita de"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "kodumebla metaserĉilo kiu respektas vian privatecon"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Ĝenerala"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Motoroj"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Aldonaĵoj"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Kuketoj"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Kiun lingvon vi pli ŝatas por serĉi?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Ŝanĝi la fasonadan lingvon"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Trovi aferojn dum tajpado"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Prokuri bildrezultojn per searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr " Ŝanĝi kiel formoj estas senditaj, < href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\"> Lerni pli pri peto-metodoj</> "
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtri enhavon"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Ŝanĝi searx-fasonadon"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Elekti stilon por ĉi tiu temo"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Stilo"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Fulmoklavo"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Mezkvanta tempo"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Maksimuma tempo"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Ĉi tiu estas la listo de kuketoj kaj siaj valoroj, kiujn searx konservas en via komputilo."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Kun tiu listo, vi povas kontroli la travideblecon de searx."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Nomo de kuketo"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Valoro"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Serĉrezultoj"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Ligiloj"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Komenci serĉon"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "statistikoj"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Ie"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Pasinta tago"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Pasinta semajno"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Pasinta monato"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Atentu!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Ŝajnas, ke ĉi tiu estas via unua fojo uzante searx"
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Informoj!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "ĉi-momente, ne estas kuketoj difinitaj."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Nun ne estas datumoj disponeblaj."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Mizera!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "ni ne trovis rezultojn. Bonvole uzu alian serĉfrazon aŭ serĉu en pliaj kategorioj."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Bonfarite!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Agordoj konservitaj sukcese."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Ho ve!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Io fuŝiĝis."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "montri aŭdvidaĵojn"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "kaŝi aŭdvidaĵojn"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Akiri bildon"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Vidi fonton"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "montri mapon"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "kaŝi mapon"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "montri detalojn"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "kaŝi detalojn"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Dosiergrando"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bitokoj"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Nombro da Dosieroj"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "montri videojn"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "kaŝi videojn"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Ŝarĝi pli..."
diff --git a/searx/translations/es/LC_MESSAGES/messages.po b/searx/translations/es/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..e13fdd0
--- /dev/null
+++ b/searx/translations/es/LC_MESSAGES/messages.po
@@ -0,0 +1,850 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Adam Tauber <asciimoo@gmail.com>, 2015
+# Alejandro León Aznar, 2014
+# Alejandro León Aznar, 2014-2016
+# Carmen Fernández B., 2016
+# juanda097 <juanda097@openmailbox.org>, 2016
+# marc, 2016
+# Oscar <ocf@openmailbox.org>, 2015
+# rivera valdez <riveravaldezmail@gmail.com>, 2016
+# wefwefew ewfewfewf <nnnedmz0d@moakt.ws>, 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: juanda097 <juanda097@openmailbox.org>\n"
+"Language-Team: Spanish (http://www.transifex.com/asciimoo/searx/language/es/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "Archivos"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "General"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "Música"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "Medios sociales"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "Imágenes"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "Vídeos"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "Informática"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "Noticias"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "Mapa"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "Ciencia"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Parámetros incorrectos, por favor, cambia tus preferencias"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "error en la búsqueda"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "hace {minutes} minuto(s)"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "hace {hours} hora(s) y {minutes} minuto(s)"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Generador de valores aleaorios"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Generar varios valores aleatorios"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Funciones de estadística"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Computar {functions} de parámetros"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Motor de tiempo (seg)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Tiempo de carga (segundos)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Número de resultados"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Puntuaciones"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Puntuaciones por resultado"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Errores"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(OBSOLETO)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Esta entrada la ha sustituido"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "Cambiar a DOAI"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Evitar barreras de pago redireccionando a las versiones de acceso libre de las publicaciones cuando estén disponibles"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Cambiar los enlaces HTTP a HTTPS si es posible"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Deslizamiento infinito"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Cargar automáticamente la siguiente página al deslizarse hasta el final de la página actual"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Abrir los enlaces resultantes en nuevas pestañas del navegador"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Los resultados se abren en la misma ventana por defecto. Este plugin sobrescribe el comportamiento por defecto para abrir enlaces en nuevas pestañas / ventanas. (es necesario JavaScript)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Buscar en la categoría seleccionada"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Realizar una búsqueda inmediatamente si se ha seleccionado una categoría. Desactivar para seleccionar varias categorías. (Se requiere JavaScript)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Muestra tu dirección IP si la consulta es \"ip\" y tu Agente de Usuario si la consulta contiene \"agente de usuario\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Eliminador de URL rastreadora"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Eliminar los argumentos de los rastreadores en la URL devuelta"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Teclas de acceso rápido como-Vim"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Navegar por los resultados de búsqueda con las teclas de acceso rápido como-Vim (es necesario JavaScript). Pulse la tecla \"h\" en la página principal o en el resultado para obtener ayuda."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Página no encontrada"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Ir a %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "Página de búsqueda"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "preferencias"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "acerca de"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Preferencias"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Categorías predeterminadas"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Idioma de búsqueda"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Idioma por defecto"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Idioma de la interfaz"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Autocompletar"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Imagen proxy"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Activado"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Desactivado"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Método"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Búsqueda segura"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Riguroso"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Moderado"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Ninguno"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Temas"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Color"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Azul (predeterminado)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violeta"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Verde"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Cian"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Naranja"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Rojo"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Motores de búsqueda actualmente en uso"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Nombre del motor de búsqueda"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Categoría"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Permitir"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Bloquear"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Esta configuración se guarda en sus cookies, lo que nos permite no almacenar dicha información sobre usted."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Estas cookies son para su propia comodidad, no las utilizamos para rastrearle."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "Guardar"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Restablecer configuración por defecto"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "Atrás"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "URL de la búsqueda"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Descargar resultados"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Respuestas"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Sugerencias"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "Página anterior"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "Página siguiente"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Buscar..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Estadísticas del motor de búsqueda"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "contexto original"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Fuente"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Descargador"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "magnet link (enlace sin archivo)"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "archivo torrent"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Haz clic en la lupa para realizar la búsqueda"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Resultados en nuevas pestañas"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Activado"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Desactivado"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "en caché"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Ajustes avanzados"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Cerrar"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "¡Error!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Creado por"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "un metabuscador hackable que respeta la privacidad"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "proxied"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "General"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Motores"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Plugins"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Respondedores"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "¿Qué idioma prefieres para la búsqueda?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Cambiar idioma de la interfaz"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Buscar mientras escribes"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Filtrado de resultados de imágenes en searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Modifica cómo se envian los formularios <a href=\"http://es.wikipedia.org/wiki/Hypertext_Transfer_Protocol#M.C3.A9todos_de_petici.C3.B3n\" rel=\"external\">más información sobre métodos de peticiones</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtro de contenido"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Cambiar aspecto de searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Elige un estilo para este tema"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Estilo"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Atajo"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Soporta el idioma seleccionado"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Rango de tiempo"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Tiempo promedio"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Tiempo máximo"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Esta es la lista de los módulos de respuesta inmediata de searx."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Nombre"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Plabras clave"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Descripción"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Ejemplos"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Esta es la lista de cookies y sus valores que searx está almacenando en tu ordenador."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Con esa lista puedes valorar la transparencia de searx."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Nombre de la cookie"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Valor"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Buscar"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Enlaces"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Comenzar búsqueda"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "estadísitcas"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "En cualquier momento"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Último día"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Última semana"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Último mes"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Último año"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "¡Atención!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Parece que estás usando searx por primera vez."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "¡Información!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "No existen cookies definidas actualmente."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Actualmente no hay datos disponibles."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "¡Lo siento!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "No encontramos nada. Por favor, formule su búsqueda de otra forma o busque en más categorías."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "¡Bien hecho!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Configuración guardada correctamente."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "¡Mecachis!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Algo ha ido mal."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "mostrar multimedia"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "ocultar multimedia"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Obtener imagen"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Ver fuente"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "mostrar mapa"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "ocultar mapa"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "ver detalles"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "ocultar detalles"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Tamaño de archivo"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bytes"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "KiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Número de archivos"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "mostrar vídeo"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "ocultar video"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Cargar más"
diff --git a/searx/translations/fi/LC_MESSAGES/messages.po b/searx/translations/fi/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..f875568
--- /dev/null
+++ b/searx/translations/fi/LC_MESSAGES/messages.po
@@ -0,0 +1,842 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Jiri Grönroos <jiri.gronroos@iki.fi>, 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-05-14 11:06+0000\n"
+"Last-Translator: Jiri Grönroos <jiri.gronroos@iki.fi>\n"
+"Language-Team: Finnish (http://www.transifex.com/asciimoo/searx/language/fi/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: fi\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "tiedostot"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "yleiset"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "musiikki"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "sosiaalinen media"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "kuvat"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "videot"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "it"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "uutiset"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "kartta"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "tiede"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Virheelliset asetukset, muokkaa siis asetuksia"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "hakuvirhe"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} min sitten"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} t, {minutes} min sitten"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Satunnaisluvun generaattori"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Generoi satunnaislukuja"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Tilastolliset funktiot"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Laske argumenttien {functions}"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Hakukoneen aika (s)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Sivun lataus (s)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Tulosten määrä"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Pisteet"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Pisteet per tulos"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Virheet"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(VANHENTUNUT)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Tämän kohdan on korvannut"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "DOAI-uudelleenkirjoitus"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Vältä maksumuureja ohjaamalla julkaisujen avoimiin versioihin jos mahdollista"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Muuta HTTP-linkit HTTPS-muotoon jos mahdollista"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Loputon vieritys"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Lataa automaattisesti seuraava sivu, kun nykyisen sivun loppu saavutetaan"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Avaa tulokset uusiin välilehtiin"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Tulokset avataan oletuksena samaan ikkunaan. Tämä lisäosa korvaa oletustoiminnan avaamalla linkit uusiin välilehtiin tai ikkunoihin. (JavaScript vaaditaan)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Etsi valitsemalla luokka"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Suorita haku välittömästi, jos luokka valitaan. Poista käytöstä valitaksesi useita luokkia. (JavaScript vaaditaan)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Näyttää IP-osoitteesi jos hakuehtosi on \"ip\" ja selaimen tunnistetiedot jos hakuehtosi sisältää sanat \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Seurantapalvelimen osoitteen poistaja"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Poista seurantapalvelinten argumentit palautetusta osoitteesta"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Vim-kaltaiset pikanäppäimet"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Liiku hakutulossivuilla Vim-kaltaisin näppäinkomennoin (JavaScript vaaditaan). Paina \"h\" pää- tai hakutulossivulla nähdäksesi ohjeet."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Sivua ei löytynyt"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Siirry %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "hakusivulle"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "asetukset"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "tietoja"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Asetukset"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Oletusluokat"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Haun kieli"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Oletuskieli"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Käyttöliittymän kieli"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Automaattinen täydentäminen"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Kuvat välityspalvelimen kautta"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Käytössä"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Ei käytössä"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Tapa"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "SafeSearch"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Tiukka"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Keskitaso"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Ei mitään"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Teemat"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Väri"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Sininen (oletus)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violetti"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Vihreä"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Syaani"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Oranssi"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Punainen"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Nyt käytetyt hakukoneet"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Hakukoneen nimi"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Luokka"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Salli"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Estä"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Nämä asetukset tallennetaan evästeisiisi. Tämän ansioista Searxin ei tarvitse tallentaa sinuun liittyviä henkilökohtaisia tietoja."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Kyseiset evästeet palvelevat ainoastaan sinua, eikä niitä käytetä seuraamiseesi."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "Tallenna"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Palauta oletukset"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "Takaisin"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Haun osoite"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Lataa tulokset"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Vastaukset"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Ehdotukset"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "edellinen sivu"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "seuraava sivu"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Etsi..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Hakukoneen tilastot"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "alkuperäinen konteksti"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Lähettäjä"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Lataaja"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "magnet-linkki"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "torrent-tiedosto"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Napsauta suurennuslasia suorittaaksesi haun"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Tulokset uusiin välilehtiin"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Päällä"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Pois"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "välimuistissa"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Lisäasetukset"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Sulje"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Virhe!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Taustavoimana"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "yksityisyyttä kunnioittava, muokattava metahakukone"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "välityspalvelimella"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Yleiset"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Hakukoneet"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Lisäosat"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Vastaajat"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Evästeet"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Millä kielellä haluat etsiä ensisijaisesti?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Vaihda asettelun kieltä"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Löydä tuloksia kirjoittaessasi"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Kuvatulokset välitetään searxin välityspalvelimen kautta"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Muuta tapaa, miten lomaketiedot välitetään. <a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">Lisätietoja eri välitystavoista.</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Suodata hakutulosten sisältöä"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Muuta searxin asettelua"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Valitse tyyli tälle teemalle"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Tyyli"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Oikoreitti"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Tukee valittua kieltä"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Aikaväli"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Keskimääräinen\naika"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Enimmäisaika"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Tämä on luettelo searxin vastaajamoduuleista."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Nimi"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Avainsanat"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Kuvaus"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Esimerkit"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Ohessa on lista evästeistä ja niiden arvoista, joita searx tallentaa tietokoneellesi."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Tämän luettelon avulla voit arvioida searxin läpinäkyvyyden."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Evästeen nimi"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Arvo"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Hakutulokset"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Linkit"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Aloita haku"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "tilastot"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Milloin tahansa"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Viimeinen päivä"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Viimeinen viikko"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Viimeinen kuukausi"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Viimeinen vuosi"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Pää pystyyn!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Vaikuttaa siltä, että käytät searxia ensimmäistä kertaa."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Huomio!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "Evästeitä ei ole määritetty tällä hetkellä."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Tietoja ei ole juuri nyt saatavilla."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Pahoittelut!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "Yhtäkään hakuasi vastaavaa tulosta ei löytynyt. Etsi käyttäen eri hakuehtoja tai ulota hakusi nykyistä useampiin eri luokkiin."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Hyvin tehty!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Asetukset tallennettiin onnistuneesti"
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Voi ei!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Jokin meni pieleen."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "näytä media"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "piilota media"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Avaa kuva"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Näytä lähde"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "näytä kartta"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "piilota kartta"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "näytä tiedot"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "piilota tiedot"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Tiedostokoko"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Tavua"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Tiedostojen määrä"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "näytä video"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "piilota video"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Lataa lisää..."
diff --git a/searx/translations/fr/LC_MESSAGES/messages.po b/searx/translations/fr/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..ea60553
--- /dev/null
+++ b/searx/translations/fr/LC_MESSAGES/messages.po
@@ -0,0 +1,849 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Alexandre Flament <alex@al-f.net>, 2017
+# Benjamin Sonntag <benjamin@sonntag.fr>, 2014
+# Cqoicebordel <david.barouh@wanadoo.fr>, 2014
+# Cqoicebordel <david.barouh@wanadoo.fr>, 2014-2016
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014
+# Noémi Ványi <sitbackandwait@gmail.com>, 2017
+# rike, 2014
+# rike, 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 22:08+0000\n"
+"Last-Translator: Noémi Ványi <sitbackandwait@gmail.com>\n"
+"Language-Team: French (http://www.transifex.com/asciimoo/searx/language/fr/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "fichiers"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "général"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "musique"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "réseaux sociaux"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "images"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "vidéos"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "informatique"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "actus"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "carte"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "science"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Paramètres non valides, veuillez éditer vos préférences"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "erreur de recherche"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "il y a {minutes} minute(s)"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "il y a {hours} heure(s), {minutes} minute(s)"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Générateur aléatoire"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Crée des valeurs aléatoires différentes"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Fonctions statistiques"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Calcule les {functions} des arguments"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Temps du moteur (sec)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Chargement de la page (sec)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Nombre de résultats"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Score"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Score par résultat"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Erreurs"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{titre}&nbsp;(OBSOLETE)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Cet item a été remplacé par"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "Utiliser DOAI"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Contourne les verrous payants de certaines publications scientifiques en redirigeant vers la version ouverte de ces papiers si elle est disponible."
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Réécrire les liens HTTP en HTTPS si possible"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Défilement infini"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Charge automatiquement la page suivante quand vous arriver en bas de la page"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Ouvrir les liens de résultats dans un nouvel onglet"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Les résultats sont ouvert dans la même fenêtre par défaut. Cette extension change le comportement par défaut pour ouvrir les liens dans des nouveaux onglets ou fenêtres (Javascript est nécessaire)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Lancer la recherche lors du choix d'une catégorie"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Exécute la recherche immédiatement si une catégorie est sélectionnée. Désactiver pour sélectionner plusieurs catégories (nécessite JavaScript)."
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Affiche votre adresse IP si la requête est \"ip\", et affiche votre user-agent si la requete contient \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Nettoyeur d'URL de suivis"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Retire les arguments utilisés pour vous pister des URL retournées"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Raccourcis comme Vim"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Parcourez les résultats de recherche avec des raccourcis clavier similaires à Vim (Javascript est nécessaire. Appuyez sur \"h\" dans la fenêtre principale de résultats pour afficher de l'aide."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Page non trouvée"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Aller à %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "la page d'accueil"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "préférences"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "À propos"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Préférences"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Catégories par défaut"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Langue de recherche"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Langue par défaut"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Langue de l'interface"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Complétion automatique"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Proxy d'images"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Activé"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Désactivé"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Méthode"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Recherche sécurisée"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Stricte"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Modérée"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Pas du tout"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Thème"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Couleur"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Bleu (défaut)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violet"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Vert"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Cyan"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Orange"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Rouge"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Moteurs de recherche actuellement utilisés"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Nom du moteur"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Catégorie"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Autoriser"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Bloquer"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Ces paramètres sont stockés dans vos cookies ; ceci nous permet de ne pas collecter vos données."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Ces cookies existent pour votre confort d'utilisation, nous ne les utilisons pas pour vous espionner."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "enregistrer"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Remettre les valeurs par défaut"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "retour"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "URL de recherche"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Télécharger les résultats"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Réponses"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Suggestions"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "page précédente"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "page suivante"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Rechercher..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Statistiques des moteurs"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "contexte original"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Sources"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Téléchargeurs"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "lien magnet"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "fichier torrent"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Cliquez sur la loupe pour effectuer une recherche"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Résultats dans de nouveaux onglets"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "On"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Off"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "en cache"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Paramètres avancés"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Fermer"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Erreur !"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Powered by"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "un meta-moteur de recherche hackable et respectueux de la vie privée"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "proxifié"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Général"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Moteurs"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Plugins"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Répondants"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Dans quelle langue préférez-vous effectuer la recherche ?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Changer la langue d'affichage"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Chercher au fil de la saisie"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Proxifier les images de résultats à travers searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Permet de choisir comment la recherche est envoyée, <a href=\"https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol#M.C3.A9thodes\" rel=\"external\">en savoir plus sur les méthodes HTTP</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtrer le contenu"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Modifier l'affichage de searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Choisir un style pour ce thème"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Style"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Raccourcis"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Supporte la langue sélectionnée"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Espace temporel"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Temps moy."
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Temps max"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Voici la liste des module de searx produisant une réponse instantanée."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Nom"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Mots clés"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Description"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Exemples"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "C'est une liste de cookies et de leurs valeurs que searx enregistre sur votre ordinateur."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Avec cette liste, vous pouvez juger de la transparence de searx."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Nom du cookie"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Valeur"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Résultats de recherche"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Liens"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Lancer une recherche"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "statistiques"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "N'importe quand"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Dernières 24h"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Semaine précédente"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Mois précédent"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Année précédente"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Astuces !"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Il semble que ce soit la première fois que vous utilisez searx."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Information !"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "il n'y a pas de cookies définis pour le moment."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Aucune donnée disponible pour l'instant. "
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Désolé !"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "nous n'avons trouvé aucun résultat. Effectuez une autre recherche ou changez de catégorie."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Bravo !"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Paramètres sauvés avec succès."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Oups !"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Il y a un problème."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "afficher le média"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "cacher le media"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Voir l'image"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Voir la source"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "afficher la carte"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "cacher la carte"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "afficher les détails"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "cacher les détails"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Taille de fichier"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "octets"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kio"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "Mio"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "Gio"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "Tio"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Nombre de fichiers"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "afficher la vidéo"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "cacher la vidéo"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Afficher plus..."
diff --git a/searx/translations/he/LC_MESSAGES/messages.po b/searx/translations/he/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..aee051e
--- /dev/null
+++ b/searx/translations/he/LC_MESSAGES/messages.po
@@ -0,0 +1,846 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# GenghisKhan <genghiskhan@gmx.ca>, 2015
+# GenghisKhan <genghiskhan@gmx.ca>, 2015-2016
+# pointhi, 2014
+# rike, 2014
+# stf <stefan.marsiske@gmail.com>, 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: GenghisKhan <genghiskhan@gmx.ca>\n"
+"Language-Team: Hebrew (http://www.transifex.com/asciimoo/searx/language/he/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: he\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "קבצים"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "כללי"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "מוזיקה"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "מדיה חברתית"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "תמונות"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "סרטונים"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "IT"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "חדשות"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "מפות"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "מדע"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "הגדרות שגויות, אנא ערוך את ההעדפות שלך"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "שגיאת חיפוש"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "לפני {minutes} דקות"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "לפני {hours} שעות, {minutes} דקות"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "מפיק ערך אקראי"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "צור ערכים אקראיים שונים"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "פונקציות סטטיסטיקה"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr ""
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "זמן מנוע (שניות)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "עומס עמוד (שניות)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "מספר תוצאות"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "דירוג"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "דירוג לכל תוצאה"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "שגיאות"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr ""
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "רשומה זו הוחלפה על ידי"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "שכתוב DOAI"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "הימנעות מקירות-תשלום (paywalls) על ידי הכוונה מחודשת לגרסאות כניסה-חופשית של כתבי-עת כאשר זמינות"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "שכתוב קישורי HTTP לקישורי HTTPS כאשר ניתן"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "גלילה אינסופית"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "טען אוטומטית עמוד הלאה כאשר גוללים לתחתית של עמוד נוכחי"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "פתיחת קישורי תוצאה בתוך כרטיסיות דפדפן חדשות"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "תוצאות נפתחות בתוך אותו חלון באופן שגרתי. תוסף זה משכתב את ההתנהגות השגרתית כדי לפתוח קישורים בתוך כרטיסיות/חלונות חדשים. (JavaScript נדרש)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "חיפוש בעת בחירת קטגוריה"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "ביצוע חיפוש כאשר קטגוריה נבחרת. יש לנטרל את תוסף זה כדי לבחור קטגוריות מרובות. (מצריך JavaScript)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "הצגת כתובת IP המשוייכת לך אם השאילתא היא \"ip\" וגם סוכן משתמש אם השאילתא מכילה \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "הסרת Tracker URL"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "הסרת ארגומנטים מאתרים מתוך URL מוחזר"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "מקשים חמים Vim-like"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "ניווט בתוצאות בעזרת מקשים חמים Vim-like (JavaScript נדרש). לחיצה על קליד \"h\" במסך ראשי או תוצאות תציג דו שיח עזרה."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "עמוד לא נמצא"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "המשך אל %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "עמוד חיפוש"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "העדפות"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "אודות"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "העדפות"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "קטגוריות ברירת מחדל"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "שפת חיפוש"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "שפה ברירת מחדל"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "שפת ממשק"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "השלמה אוטומטית"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "מתווך תמונה"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "מאופשר"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "מנוטרל"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "שיטה"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "חיפוש בטוח"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "מחמיר"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "מתון"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "בלי"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "עיצובים"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "צבע"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "כחול (שגרתי)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "סגול"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "ירוק"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "ציאן"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "כתום"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "אדום"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "מנועי חיפוש בשימוש עתה"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "שם מנוע"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "קטגוריה"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "התר"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "חסום"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "הגדרות אלו מאוחסנות בתוך העוגיות שלך, אלו מאפשרות לנו להימנע מלאחסן את מידע זה אודותייך."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "עוגיות אלו משרתות את נוחותך הבלעדית, אנחנו לא משתמשים בהן כדי לעקוב אחריך."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "שמור"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "אפס ברירות מחדל"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "חזור"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "קישור חיפוש"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "הורד תוצאות"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "תשובות"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "הצעות"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "עמוד קודם"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "עמוד הבא"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "חיפוש..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "סטטיסטיקות מנוע"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "הקשר מקורי"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "זורעים"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "יונקים"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "קישור magnet"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "קובץ torrent"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "לחצו על הזכוכית מגדלת כדי לבצע חיפוש"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "תוצאות בכרטיסיות חדשות"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "פועל"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "כבוי"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "מוטמן"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "הגדרות מתקדמות"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "סגור"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "שגיאה!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "מופעל באמצעות"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "מנוע מטא-חיפוש גמיש, ומכבד פרטיות"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "כללי"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "מנועים"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "תוספים"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "תשובות"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "עוגיות"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "איזו שפה אתם מעדיפים לצורך חיפוש?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "שינוי שפת הממשק"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "מציאת דברים בזמן הקלדה"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "שליפת תוצאות תמונה דרך searx (מבעד Proxy)"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "שינוי האופן בו טפסים נשלחים, <a href=\"https://he.wikipedia.org/wiki/Hypertext_Transfer_Protocol#.D7.A9.D7.99.D7.98.D7.95.D7.AA_.D7.91.D7.A7.D7.A9.D7.94\" rel=\"external\">למדו עוד אודות שיטות בקשה (request methods)</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "סינון תוכן"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "שינוי ממשק searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "בחירת סגנון עבור עיצוב זה"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "סגנון"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "קיצור דרך"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "זמן ממוצע"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "זמן מירבי"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "שם"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "מילות מפתח"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "תיאור"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "דוגמאות"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "זוהי רשימה של עוגיות וערכיהן אשר searx מאחסנת על המחשב שלך."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "בעזרת רשימה זו, באפשרותך לגשת אל searx transparency."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "שם עוגייה"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "ערך"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "תוצאות חיפוש"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "קישורים"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "התחל חיפוש"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "סטטיסטיקות"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "כל עת"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "יום אחרון"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "שבוע אחרון"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "חודש אחרון"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "שנה אחרונה"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "זהירות!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "נראה כי אתם משתמשים ב-searx בפעם הראשונה."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "מידע!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "ברגע זה, אין עוגיות מוגדרות."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "אין כעת מידע זמין. "
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "מצטערים!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "לא מצאנו תוצאות. אנא נסו שאילתא אחרת או חפשו בתוך יותר קטגוריות."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "כל הכבוד!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "הגדרות נשמרו בהצלחה."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "אבוי!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "משהו השתבש."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "הצג מדיה"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "הסתר מדיה"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "השג תמונה"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "צפה במקור"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "הצג מפה"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "הסתר מפה"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "הצג פרטים"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "הסתר פרטים"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "גודל קובץ"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "בייטים"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "קי״ב"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "מי״ב"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "גי״ב"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "טי״ב"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "מספר קבצים"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "הצג וידאו"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "הסתר וידאו"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "טען עוד..."
diff --git a/searx/translations/hu/LC_MESSAGES/messages.po b/searx/translations/hu/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..6c5a28b
--- /dev/null
+++ b/searx/translations/hu/LC_MESSAGES/messages.po
@@ -0,0 +1,845 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Adam Tauber <asciimoo@gmail.com>, 2014-2016
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014
+# Noémi Ványi <sitbackandwait@gmail.com>, 2016
+# Noémi Ványi <sitbackandwait@gmail.com>, 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Noémi Ványi <sitbackandwait@gmail.com>\n"
+"Language-Team: Hungarian (http://www.transifex.com/asciimoo/searx/language/hu/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: hu\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "fájlok"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "általános"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "zene"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "közösségi média"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "képek"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "videók"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "it"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "hírek"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "térkép"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "tudomány"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Érvénytelen beállítások"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "keresési hiba"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} perce"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} óra, {minutes} perce"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Véletlen érték generátor"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Különböző random értékek generálása"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Statisztikai függvények"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "{functions} függvények alkalmazása az argumentumokon"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Keresési idő (másodperc)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Válaszidők (sec)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Találatok száma"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Pontszámok"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Pontszámok találatonként"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Hibák"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(ELAVULT)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Ezt a bejegyzést törölte:"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "Szabad publikációs oldalak"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Publikácós linkeknél szabad forrás használat, amennyiben lehetséges"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "HTTP linkek lecserélése HTTPS-re"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Végtelenített találatok"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "További találatok automatikus betöltése"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Találatok megnyitása új lapon"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "A találatok az aktuális oldalon nyílnak meg alapértelmezetten. Ez a plugin megváltoztatja ezt a működést és új lapra nyitja meg a találatokat. (ez a funkció JavaScript-et igényel)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Azonnali keresés kategória választással"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Keresés megkezdése kategória kiválasztáskor. Több kategória kiválasztásához deaktiválás szükséges. (Javascript szükséges)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Saját IP cím, user agent megjelenítés az \"ip\", \"user agent\" szavakra keresve."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Tracker URL eltávolítás"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Felhasználó követéshez használt találati URL paraméterek eltávolítása"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Vim típusú billentyűzetes navigáció"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Navigálj néhány gombnyomással a találatok között. Aktiválás után a \"h\" betű lenyomásával jeleníthető meg részletes segítség a használatról. (Ez a funkció JavaScript-et igényel)"
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Az oldal nem található"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Vissza a %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "kereső oldalra"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "beállítások"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "rólunk"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Beállítások"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Alapértelmezett kategóriák"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Keresés nyelve"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Alapértelmezett nyelv"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Felület nyelve"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Automatikus kiegészítés"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Kép proxy"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Engedélyez"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Inaktivál"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Method"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Felnőtt tartalom szűrés"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Erős"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Enyhe"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Nincs"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Megjelenés"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Szín"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Kék"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Ibolya"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Zöld"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Türkiz"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Narancs"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Piros"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Jelenleg használt keresők"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Kereső neve"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Kategória"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Engedélyezés"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Tiltás"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Ezek a beállítások csak a böngésző cookie-jaiban tárolódnak."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Ezek a cookie-k csak kényelmi funkciókat látnak el, nem használjuk a felhasználók követésére."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "mentés"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Alapbeállítások visszaállítása"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "vissza"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Keresési URL"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Találatok letöltése"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Válaszok"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Javaslatok"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "előző oldal"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "következő oldal"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Keresés..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Kereső statisztikák"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "eredeti kontextus"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Seeder"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Leecher"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "magnet link"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "torrent fájl"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "A nagyítóra kattintva indítható a keresés"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Eredmények új tabon"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "On"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Off"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "tárolt"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Keresés beállításai"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Bezár"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Hiba!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Az oldalt kiszolgálja: "
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "egy privátszféra tisztelő, könnyen módosítható metakereső"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "proxy nézet"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Általános"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Keresőmotorok"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Pluginek"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Válaszok"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Sütik"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Preferált keresési nyelv"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Felület nyelve"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Autómatikus kereső kifejezés kiegészítés"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Kép találatok betöltése searx-ön keresztül"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Keresés metódusa (<a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">bővebben</a>)"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Tartalom szűrés"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Megjelenés"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Válassz megjelenést ehhez a témához"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Megjelenés"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Rövidítés"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Támogatja a kiválasztott nyelvet"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Idő szűrés"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Átlag idő"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Maximális idő"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Az alábbi lista tartalmazza searx instant válaszoló moduljait."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Név"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Kulcsszavak"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Leírás"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Példák"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Searx által használt sütik listája."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Ez a lista a kereső transzparenciáját hivatott megmutatni."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Süti név"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Érték"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Keresési eredmények"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Linkek"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Keresés indítása"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "statisztikák"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Bármikor"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Legutóbbi nap"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Legutóbbi hét"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Legutóbbi hónap"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Előző év"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Figyelem!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Úgy tűnik először használod a keresőt."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Figyelem!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "jelenleg nincs süti definiálva"
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Nincs megjeleníthető adat."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Elnézést!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "Nincs megjeleníthető találat."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Siker!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Beállítások mentve"
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Oh!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Hiba történt"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "médium mutatása"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "médium elrejtése"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Kép megjelenítése"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Forrás megtekintése"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "Térkép"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "Térkép elrejtése"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "Részletek"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "Részletek elrejtése"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Fájl méret"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Byte"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Fájlok száma"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "video mutatása"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "video elrejtése"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "További találatok betöltése"
diff --git a/searx/translations/it/LC_MESSAGES/messages.po b/searx/translations/it/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..f1de17c
--- /dev/null
+++ b/searx/translations/it/LC_MESSAGES/messages.po
@@ -0,0 +1,846 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# caoswave, 2016
+# caoswave, 2016
+# dp <d.pitrolo@gmx.com>, 2014
+# dp <d.pitrolo@gmx.com>, 2014,2017
+# Luc <luc.absil2@gmail.com>, 2015
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: dp <d.pitrolo@gmx.com>\n"
+"Language-Team: Italian (http://www.transifex.com/asciimoo/searx/language/it/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: it\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "documenti"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "generale"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "musica"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "social media"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "immagini"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "video"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "it"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "notizie"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "mappe"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "scienza"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Impostazioni non valide, modifica le tue preferenze"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "errore di ricerca"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "di {minutes} minuti fa"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "di {ore} ore e {minutes} minuti fa"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Generatore di valore casuale"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Genera diversi valori casuali"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Funzioni statistiche"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Calcola {functions} degli argomenti"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Tempo del motore (secondi)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr " Caricamento della pagina (secondi)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Numero di risultati"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Punteggio"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Punteggio per risultato"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Errori"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(OBSOLETE)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Questa voce è stata sostituita da"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "Rewrite DOAI"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Quando possible evita i paywall redirigendo su delle versioni in accesso libero delle pubblicazioni"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Se possible converti gli indirizzi HTTP in HTTPS"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Scorrimento infinito"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Carica automaticamente la pagina successiva quando si scorre sino alla fine della pagina attuale"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Apre i link in una nuova scheda del browser"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Di base i risultati sono aperti nella stessa finestra. Questa estensione modifica il comportamento di base per aprire gli indirizzi in nuove schede/finestre. (Javascript necessario)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Cerca nella categoria selezionata"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Esegui la ricerca immediatamente se una categoria è selezionata. Disabilita per selezionare più categorie. (Javascript necessario)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Mostra il tuo IP se la ricerca è \"ip\" ed il tuo user agent se la ricerca contiene \"user agent\""
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Tracker URL remover"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Elimina elementi traccianti dall'URL fornita"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Vim-like hotkeys"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Naviga tra i risultati di ricerca con comandi da tastiera alla Vim (JavaScript necessario). Premi il tasto \"h\" sulla pagina principale o dei risultati per ottenere aiuto."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Pagina non trovata"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Vai a %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "cerca nella pagina"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "preferenze"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "informazioni"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Preferenze"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Categorie predefinite"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Lingua di ricerca"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Lingua predefinita"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Linguaggio dell'interfaccia"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Completamento automatico"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Proxy immagini"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Attivo"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Disabilitato"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Metodo"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Ricerca Sicura"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Rigoroso"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Moderato"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Nessuno"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Temi"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Colore"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Blu (predefinito)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Viola"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Verde"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Ciano"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Arancione"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Rosso"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Motori di ricerca attualmente in uso"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Nome del motore"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Categoria"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Autorizza"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Blocca"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Queste impostazioni sono salvate nei tuoi cookie, consentendoci di non conservare dati su di te."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "I cookie sono funzionali ad un servizio migliore. Non usiamo i cookie per sorvegliarti."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "salva"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Reimpostazione dei valori iniziali"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "indietro"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "URL della ricerca"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Scarica i risultati"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Riposte"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Suggerimenti"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "pagina precedente"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "pagina successiva"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Cerca…"
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Statistiche dei motori"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "contesto originale"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Seeder"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Leecher"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "link magnet"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "file torrent"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Premi sulla lente per cercare"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Risultati in una nuova scheda"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "On"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Off"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "in cache"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Impostazioni avanzate"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Chiud"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Errore!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Grazie a"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "Un metasearch engine hackerabile e rispettoso della privacy"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "via proxy"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Generale"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Motori"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Plugins"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Risponditori"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Che lingua preferisci per la ricerca?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Cambia la lingua dell'impaginazione"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Trova materiale mentre digiti"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Proxa le immagini ottenute attraverso searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Cambia la maniera in cui vengono inviati i formulari, <a href=\"https://it.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Messaggio_di_richiesta\" rel=\"external\">più informazioni su metodi di richiesta</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtra il contenuto"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Cambia l'impaginazione di searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Scegli lo stile per questo tema"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Stile"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Scorciatoia"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "La lingua selezionata è disponibile"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Intervallo di tempo"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Tempo medio"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Tempo massimo"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Questa è la lista dei moduli searx con risposta immediata"
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Nome"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Parole chiave"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Descrizione"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Esempi"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Questa è la lista dei cookie che searx conserva sul tuo computer e dei loro valori"
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Con quella lista, puoi constatare la trasparenza di searx"
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Nome del cookie"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Valore"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Risultati della ricerca"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Collegamenti"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Inizia la ricerca"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "statistiche"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Di sempre"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Ultimo giorno"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Ultima settimana"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Ultimo mese"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "L'anno scorso"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Avviso!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Sembra che tu stia utilizzando searx per la prima volta."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Informatione!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "attualmente non ci sono cookie definiti."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Non ci sono dati attualmente disponibili."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Scusa!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "non abbiamo nessun risultato. Fa' una ricerca diversa o cerca in più categorie"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Ottimo!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Impostazioni salvate con successo."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Mannaggia!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Qualcosa è andato storto."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "mostra media"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "nascondi media"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Ottieni l'immagine"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Guarda la fonte"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "mostra mappa"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "nascondi mappa"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "mostra dettagli"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "nascondi dettagli"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Dimensioni file"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bytes"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Numero di Files"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "mostra video"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "nascondi video"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Carica altro..."
diff --git a/searx/translations/ja/LC_MESSAGES/messages.po b/searx/translations/ja/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..f93d5e4
--- /dev/null
+++ b/searx/translations/ja/LC_MESSAGES/messages.po
@@ -0,0 +1,848 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Akio Nishimura <akionux@gmail.com>, 2016-2017
+# Thomas Pointhuber, 2014-2015
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014,2016
+# Lucas Phillips <mail@lep.pw>, 2015
+# Max <theshirinzu@gmail.com>, 2015
+# pointhi, 2014
+# Thomas Pointhuber, 2015-2016
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Akio Nishimura <akionux@gmail.com>\n"
+"Language-Team: Japanese (http://www.transifex.com/asciimoo/searx/language/ja/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: ja\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "ファイル"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "一般"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "音楽"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "ソーシャルメディア"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "画像"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "動画"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "情報技術"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "お知らせ"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "地図"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "学問"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "不正な設定です。設定を編集してください。"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "検索エラー"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes}分前"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours}時間と{minutes}分前"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "ランダム値の生成"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "新しいランダム値を生成する"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "統計機能"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "変数の{functions}を計算する"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "検索時間 (秒)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "ページ読み込み時間 (秒)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "通知の数"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "スコア"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "検索結果当たりスコア"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "エラー"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(廃止)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "このエントリーの優先"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "DOAI再書き込み"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "可能ならば出版物のオープンアクセス版へリダイレクトして有料の壁を避ける"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "可能ならばHTTPリンクをHTTPSリンクに書き換える"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "無限スクロール"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "現在のページの下端でスクロールすると自動的に次のページを読み込む"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "検索結果のリンクを新しいタブで開く"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "デフォルトでは結果は同じウィンドウで開きます。このプラグインはデフォルトの動作を書き換えて新しいタブ/ウィンドウで開くようにします。(JavaScriptが必要です)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "カテゴリ選択したら検索を実行"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "カテゴリが選択されたときに検索を実行します。複数のカテゴリを選択する場合は無効にします。(JavaScriptが必要です)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "クエリが \"ip\" の場合にあなたのIPを、クエリに\"user agent\"が含まれる場合にあなたのユーザーエージェントを表示します。"
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "トラッカーURLリムーバー"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "返されたURLからトラッカー引数を消去します。"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Vim風のホットキー"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "検索結果をVim風のホットキーで操作します(JavaScriptが必要)。メインページまたは検索結果ページで\"h\"キーを押してヘルプを表示します。"
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "ページが見付かりません"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "%(search_page)s へ行く。"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "検索ページ"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "設定"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "このサイトについて"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "設定"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "デフォルトのカテゴリ"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "検索の言語"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "デフォルト言語"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "インターフェースの言語"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "オートコンプリート"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "画像プロキシ"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "有効にする"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "使用不可能にする"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "方法"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "安全な検索"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "厳しく"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "ゆるく"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "なし"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "テーマ"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "色"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "青 (初期設定)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "紫"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "緑"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "シアン"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "オレンジ"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "赤"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "現在使用中の検索エンジン"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "検索エンジン名"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "カテゴリー"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "許可する"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "禁止する"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "これらの設定はあなたのクッキーに保存されますが、これはサーバーがあなたの情報の保存するわけではありません。"
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "クッキーはあなたが便利に使えるようにするために使うのであって、サーバーはあなたを追跡するためにクッキーを使うことはありません。"
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "保存"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "デフォルト設定に戻す"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "戻る"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "URLを検索する"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "ダウンロードするファイル形式"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "回答"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "提案"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "前のページ"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "次のページ"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "検索する..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "検索エンジンの状態"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "元の文脈"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "シーダー"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "リーチャー"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "マグネットリンク"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "トレントファイル"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "検索を実行するには虫めがねをクリックしてください"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "新しいタブに結果を表示"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "有効"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "無効"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "キャッシュ"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "発展的な設定"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "閉じる"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "エラー!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "提供:"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "プライバシー保護を重視した、ハッカブルなメタサーチエンジン"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "プロキシされている"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "一般設定"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "検索エンジン"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "プラグイン"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "回答者"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "クッキー"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "検索に使う言語はどれが良いですか?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "表示する言語を変更できます"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "入力補助に使う検索エンジン"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "画像検索結果をsearxでプロキシする"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "フォームがどの方法で送信されるかを変更できます。<a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">リクエストメソッドについて詳しく知るにはここをクリック</a>。"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "コンテンツをフィルタリングする"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "searxのレイアウトの変更"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "このテーマのスタイルを選択"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "スタイル"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "ショートカット"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "選択している言語のサポート"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "時間範囲"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "平均時間"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "最大時間"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "これはsearxの即席回答モジュールのリストです。"
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "名前"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "キーワード"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "説明"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "例"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "これはクッキーのリストで、これらの値はあなたのコンピュータに保存されています。"
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "このリストによって、あなたはsearxの透明性を評価できます。"
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "クッキー名"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "値"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "検索結果"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "リンク"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "検索を開始"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "統計"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "いつでも"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "先日"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "先週"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "先月"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "去年"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "お知らせ"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "searxを使うのは初めてようですね。"
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "お知らせ"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "現在、クッキーは定義されていません。"
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "現在データがありません。"
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "申し訳ありません!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "検索結果はありませんでした。別カテゴリで、他のクエリまたは検索を試してください。"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "あっぱれ。"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "設定の保存に成功しました。"
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "ちぇっ"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "なにか問題が起こっているようです。"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "メディアを表示する"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "メディアを隠す"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "画像を取得する"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "ソースを閲覧する"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "地図を表示する"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "地図を隠す"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "詳細を表示する"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "詳細を隠す"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "ファイル・サイズ"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "バイト"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "キロバイト"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "メガバイト"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "ギガバイト"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "テラバイト"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "ファイル数"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "動画を表示する"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "動画を隠す"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "もっと見る…"
diff --git a/searx/translations/nl/LC_MESSAGES/messages.po b/searx/translations/nl/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..7220ae4
--- /dev/null
+++ b/searx/translations/nl/LC_MESSAGES/messages.po
@@ -0,0 +1,844 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# André Koot <meneer@tken.net>, 2014-2017
+# Nathan Follens, 2015-2016
+# Rejo Zenger <rejo@zenger.nl>, 2016-2017
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: André Koot <meneer@tken.net>\n"
+"Language-Team: Dutch (http://www.transifex.com/asciimoo/searx/language/nl/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: nl\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "bestanden"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "algemeen"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "muziek"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "social media"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "afbeeldingen"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "video's"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "IT"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "nieuws"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "kaart"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "wetenschap"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Ongeldige instellingen, werk je voorkeuren bij"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "zoekfout"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} min geleden"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} uur, {minutes} min geleden"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Random value generator"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Genereer verschillende willekeurige waarden"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Statistische functies"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Bereken {functions} van de argumenten"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Snelheid zoekmachine (sec)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Pagina laadt (sec)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Aantal zoekresultaten"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Scores"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Scores per zoekresultaat"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Fouten"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(VEROUDERD)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Dit object is vervangen door"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "DOAI herschrijven"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Omzeil betaalmuren met een doorverwijzing naar vrij toegankelijke versies van publicaties indien beschikbaar"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Herschrijf HTTP-koppelingen naar HTTPS, indien mogelijk"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Oneindig scrollen"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Automatisch nieuwe pagina laden bij het bereiken van de onderkant van de huidige pagina"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Open koppelingen in nieuwe tabbladen"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Resultaten worden standaard in hetzelfde venster geopend. Deze plug-in overschrijft het standaardgedrag zodat koppelingen in nieuwe tabbladen/vensters geopend worden. (JavaScript vereist)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Zoeken bij selecteren van categorie"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Zoekopdracht onmiddellijk uitvoeren wanneer een categorie geselecteerd wordt. Zet dit uit om meerdere categorieën te selecteren. (JavaScript vereist)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Geeft je IP-adres weer als de zoekopdracht \"ip\" is en je gebruikersagent als de zoekopdracht \"user agent\" bevat."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Tracker-URL verwijderaar"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Verwijdert trackerargumenten van de gekregen URL"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Sneltoetsen als in Vim"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Blader door zoekresultaten met sneltoetsen zoals die in Vim (JavaScript vereist). Druk op \"h\" op de hoofdpagina of de pagina met resultaten voor hulp."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Pagina niet gevonden"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Ga naar %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "zoekpagina"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "voorkeuren"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "over"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Voorkeuren"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Standaardcategorieën"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Zoektaal"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Standaard taal"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Interfacetaal"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Auto-aanvullen"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Afbeeldingenproxy"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Ingeschakeld"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Uitgeschakeld"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Methode"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "SafeSearch"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Strikt"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Gemiddeld"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Geen"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Thema's"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Kleur"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Blauw (standaard)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violet"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Groen"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Cyaan"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Oranje"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Rood"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Momenteel gebruikte zoekmachines"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Naam zoekmachine"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Categorie"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Toestaan"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Blokkeren"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Deze instellingen worden bewaard in je cookies. Hierdoor hoeven wij niets over jou te bewaren."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Deze cookies zijn alleen voor je eigen gemak, we gebruiken deze cookies niet om je te volgen."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "bewaren"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Standaardinstellingen herstellen"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "terug"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Zoek-URL"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Zoekresultaten downloaden"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Antwoorden"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Suggesties"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "vorige pagina"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "volgende pagina"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Zoeken naar..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Zoekmachinestatistieken"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "originele inhoud"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Aanbieder"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Ophaler"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "magneetlink"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "torrentbestand"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Klik op het vergrootglas om te zoeken"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Resultaten op nieuwe tabbladen"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Aan"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Uit"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "gecached"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Geavanceerde instellingen"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Sluiten"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Fout!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Zoekmachine"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "een privacy-eerbiedigende, aanpasbare metazoekmachine"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "geproxyt"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Algemeen"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Zoekmachines"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Plug-ins"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Beantwoorders"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Welke taal wil je gebruiken voor het zoeken?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Wijzig de taal van de layout"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Zoek tijdens het typen"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Afbeeldingsresultaten via searx laden"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Bepaal hoe de formulieren worden ingestuurd, <a href=\"http://nl.wikipedia.org/wiki/Hypertext_Transfer_Protocol#HTTP-requests\" rel=\"external\">lees meer over request methodes</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filteren op inhoud"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Wijzig searx layout"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Kies een stijl voor dit thema"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Stijl"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Snelkoppeling"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Ondersteunt geselecteerde taal"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Tijdspanne"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Gem. duur"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Max. duur"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Dit is het overzicht van searx's direct antwoordende modules."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Naam"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Kernwoorden"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Beschrijving"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Voorbeelden"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Dit is de lijst van cookies en hun waarden die searx op je computer opslaat."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Met deze lijst kan je de openheid van searx beoordelen."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Cookienaam"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Waarde"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Zoekresultaten"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Links"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Start zoeken"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "stats"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Altijd"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Voorbije dag"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Vorige week"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Vorige maand"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Vorig jaar"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Heads up!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Het lijkt erop dat je searx voor de eerste keer gebruikt."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Informatie!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "er zijn momenteel geen cookies gedefinieerd."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Er zijn momenteel geen gegevens beschikbaar."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Sorry!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "we kregen geen resultaat. Probeer een andere opvraag of zoek in meer categorieën."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Goed gedaan!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Instellingen succesvol opgeslagen."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Oops!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Er ging iets fout."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "toon media"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "verberg media"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Toon afbeelding"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Bekijk bron"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "toon kaart"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "verberg kaart"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "toon details"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "verberg details"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Bestandsgrootte"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bytes"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Aantal bestanden"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "toon video"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "verberg video"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Meer laden..."
diff --git a/searx/translations/pt/LC_MESSAGES/messages.po b/searx/translations/pt/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..0399112
--- /dev/null
+++ b/searx/translations/pt/LC_MESSAGES/messages.po
@@ -0,0 +1,842 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# António Ribeiro <contact@antonioribeiro.xyz>, 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-04-11 20:13+0000\n"
+"Last-Translator: António Ribeiro <contact@antonioribeiro.xyz>\n"
+"Language-Team: Portuguese (http://www.transifex.com/asciimoo/searx/language/pt/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: pt\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "ficheiros"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "geral"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "música"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "redes sociais"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "imagens"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "vídeos"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "ti"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "notícias"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "mapa"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "ciência"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Definições inválidas, por favor edite as suas preferências"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "erro de procura"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} minuto(s) atrás"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} hora(s), {minutes} minuto(s) atrás"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Gerador de valores aleatórios"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Gerar valores aleatórios diferentes"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Funções de estatística"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Calcular {functions} dos argumentos"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Tempo de pesquisa (seg)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Página carregada (seg)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Número de resultados"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Contagens"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Contagens por resultado"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Erros"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(OBSOLETE)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Esta entrada foi substituída por"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "Reformulação DOAI"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Evite acessos pagos acedendo a versões de livre acesso sempre que disponível"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Reformulação de hiperligações HTTP para HTTPS se possível"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Deslocação Infinita"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Carregar automaticamente a próxima página assim que se desloque para o fim da página atual"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Abrir resultados num novo separador"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Por defeito os resultados são abertos no mesmo separador. Esta extensão sobreguarda o comportamento padrão de abrir hiperligações num novo separador. (Necessário JavaScript)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Pesquisar na seleção de categoria"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Realizar imediatamente uma pesquisa após selecionar uma categoria. Desative esta opção para selecionar várias categorias. (Necessário JavaScript)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Mostrar IP se a pesquisar por \"IP\" e mostrar o user agent se pesquisar por \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Remover rastreio de hiperligação"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Remover argumentos de rastreio da hiperligação devolvida"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Atalhos Vim"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Navegar resultados de pesquisa com atalhos semelhantes ao Vim (Necessário JavaScript). Pressione a tecla \"h\" para obter ajuda."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Página não encontrada"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Ir para %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "pesquisar página"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "preferências"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "sobre"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Preferências"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Categorias Padrão"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Idioma de pesquisa"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Idioma padrão"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Idioma de interface"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Preenchimento automático"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Proxy de imagem"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Ativado"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Desativado"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Método"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Pesquisa segura"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Rigoroso"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Moderado"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Nenhum"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Temas"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Cor"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Azul (padrão)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violeta"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Verde"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Ciano"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Laranja"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Vermelho"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Motores de pesquisa utilizados"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Nome do motor de pesquisa"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Categoria"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Permitir"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Bloquear"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Estas definições são guardadas nos seus cookies, isto permite-nos que não guardemos informação sobre si."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Estes cookies servem somente para sua conveniência, não os utilizamos para o rastrear."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "guardar"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Repor predefinições"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "atrás"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Procurar hiperligação"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Resultados de transferências"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Respostas"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Sugestões"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "página anterior"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "página seguinte"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Procurar por..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Estatísticas de motor de pesquisa"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "contexto original"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Seeder"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Leecher"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "hiperligação magnética"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "ficheiro torrent"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Clique na lupa para realizar a pesquisa"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Resultados em novos separadores"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Ligado"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Desligado"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "armazenados em cache"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Definições avançadas"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Fechar"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Erro!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Produzido por"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "um motor de metapesquisa editável e respeitador da sua privacidade"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "via proxy"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Geral"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Motores de pesquisa"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Extensões"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Remetente"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Que idioma de pesquisa prefere?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Alterar o idioma do esquema"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Pesquisar enquanto escreve"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Resultados de imagens pelo proxy searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Alterar como formulários são submetidos, <a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">aprender mais sobre métodos de pedidos</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtrar conteúdo"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Alterar esquema do searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Escolher estilo para este tema"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Estilo"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Atalho"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Suporta idioma selecionado"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Período de tempo"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Tempo médio"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Tempo máximo"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Esta é a lista dos modulos instantâneos de resposta do searx"
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Nome"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Palavras-chave"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Descrição"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Exemplos"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Esta é a lista de cookies e os valores que o searx está a guardar no seu computador."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Com essa lista pode aceder à transparência do searx."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Nome de cookie"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Valor"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Resultados de pesquisa"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Hiperligações"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Começar pesquisa"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "estatísticas"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Qualquer altura"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Ontem"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Semana passada"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Mês passado"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Ano passado"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Atenção!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Parece que está a utilizar o searx pela primeira vez."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Informação!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "atualmente não existem cookies definidos"
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Não existem dados disponíveis."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Desculpe!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "não encontramos nenhum resultado. Por favor pesquise outra coisa ou utilize mais categorias na sua pesquisa."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Muito bem!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Definições guardadas com sucesso."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Ora bolas!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Alguma coisa correu mal."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "mostrar média"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "esconder média"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Obter imagem"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Ver fonte"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "mostrar mapa"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "esconder mapa"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "mostrar detalhes"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "esconder detalhes"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Tamanho de ficheiro"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bytes"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Número de Ficheiros"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "mostrar vídeo"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "esconder vídeo"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Carregar mais..."
diff --git a/searx/translations/pt_BR/LC_MESSAGES/messages.po b/searx/translations/pt_BR/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..6c016eb
--- /dev/null
+++ b/searx/translations/pt_BR/LC_MESSAGES/messages.po
@@ -0,0 +1,844 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Guimarães Mello <maeslor@cryptolab.net>, 2017
+# Neton Brício <fervelinux@gmail.com>, 2015
+# pizzaiolo, 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Guimarães Mello <maeslor@cryptolab.net>\n"
+"Language-Team: Portuguese (Brazil) (http://www.transifex.com/asciimoo/searx/language/pt_BR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: pt_BR\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "arquivos"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "geral"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "áudio"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "redes sociais"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "imagens"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "vídeos"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "códigos"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "notícias"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "mapas"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "ciência"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Configurações inválidas, por favor, edite suas preferências"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "erro de busca"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutos} minuto(s) atrás"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{horas} hora(s), {minutos} minuto(s) atrás"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Gerador de valor aleatório"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Gere diferentes valores aleatórios"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Funções estatísticas"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Compute {functions} dos argumentos"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Tempo do motor (segundos)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Carregamento da página (sec)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Número de resultados"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Pontuações"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Pontuações por resultado"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Erros"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(OBSOLETA)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Esta entrada foi substituída por"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "Redirecionamento ao DOAI"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Evita \"paywalls\" ao redirecionar para versões de acesso livre de publicações, quando possível"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Redirecionar conexões HTTP para HTTPS, se possível"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Scroll infinito"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Automaticamente carregar a próxima página quando ir até o fim da página atual"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Abrir resultados em novas abas do navegador"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Os resultados são abertos na mesma janela por padrão. Este complemento muda o comportamento padrão ao abrir links em novas abas/janelas (JavaScript necessário)."
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Pesquisar na categoria selecionada"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Executar a busca imediatamente se a categoria está selecionada. Desativar para selecionar várias categorias. (Necessário JavaScript)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Exibe o seu IP se a consulta é \"ip\" e seu agente de usuário, se a consulta contém \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Remover Tracker da url"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Remover argumentos de url retornáveis"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Atalhos estilo Vim"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Navegar pelos resultados de busca com atalhos à la Vim (JavaScript necessário). Aperte \"h\" na página de resultados para obter ajuda."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Página não encontrada"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Ir a %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "página de busca"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "configurações"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "sobre"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Configurações"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Categoria padrão"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Idioma de busca"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Língua padrão"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Idioma da interface "
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Autocompletar"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Imagem proxy"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Habilitado "
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Desabilitado"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Método"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Busca Segura"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Forte"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Moderado"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Nenhum"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Temas"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Cor"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Azul (padrão)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violeta"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Verde"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Ciano"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Laranja"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Vermelho"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Serviço de busca usado atualmente"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Nome do serviço"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Categoria"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Ativo"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Bloqueado"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Essas configurações são armazenadas em seus cookies, nos não armazenamos nenhum dado a seu respeito."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Estes cookies servem ao seu único propósito, nós não usamos esses cookies para rastreá-lo."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "salvar"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Redefinir configurações"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "voltar"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Buscar URL"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Resultados para download"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Perguntas"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Sugestões"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "Página anterior"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "Próxima página"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Buscar por..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Estatísticas de busca"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "Contexto original"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Semeador"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Leecher"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "Link magnético"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "Arquivo torrent"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Clique na lupa para executar a busca"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Resultados em novas abas"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Ligado"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Desligado"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "em cache"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Configurações avançadas"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Fechar"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Erro!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Distribuído por"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "um mecanismo de metabusca que respeita a sua privacidade"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "por proxy"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Geral"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Buscadores"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Complementos"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Operadores de Resposta"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Qual idioma padrão para pesquisar?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Alterar o idioma da interface"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Exibir sugestões enquanto você digita"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Usar proxy para resultado de imagens exibidas através do searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Alterar o modo como os formulários são submetidos<a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">ganhar desempenho sobre métodos de solicitação</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtrar conteúdo"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Alterar interface do searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Escolher um estilo para este tema"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Estilo"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Atalhos"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Suporta a língua selecionada"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Intervalo de tempo"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Avg.tempo"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Tempo máximo"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Esta é a lista do módulos de resposta instantânea do searx."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Nome"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Palavras-chave"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Descrição"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Exemplos"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Esta é a lista de cookies que searx está armazenando em seu computador."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Com essa lista, você pode avaliar a transparência do searx."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Nome do cookie"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Valor"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Procurar resultados"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Links"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Iniciar busca"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "estatísticas"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "A qualquer tempo"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Ontem"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Semana passada"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Mês passado"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Ano passado"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Atenção!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Parece que você está usando searx primeira vez."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Informação"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "Atualmente, não há cookies definidos"
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Atualmente, não há dados disponíveis."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Desculpe!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "Não encontramos nenhum resultado. Utilize outra consulta ou pesquisa em mais categorias."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Muito bem!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Configurações salvas com sucesso"
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Oh não!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Algo deu errado."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "exibir mídia"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "ocultar mídia"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Obter imagem"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Ver código-fonte"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "exibir mapas"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "ocultar mapas"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "Exibir detalhes"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "ocultar detalhes"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Tamanho do arquivo"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bytes"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Número de Arquivos"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "exibir vídeo"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "ocultar vídeo"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Mostrar mais ..."
diff --git a/searx/translations/ro/LC_MESSAGES/messages.po b/searx/translations/ro/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..6801f9c
--- /dev/null
+++ b/searx/translations/ro/LC_MESSAGES/messages.po
@@ -0,0 +1,842 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# adrian.fita <adrian.fita@gmail.com>, 2015
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Adam Tauber <asciimoo@gmail.com>\n"
+"Language-Team: Romanian (http://www.transifex.com/asciimoo/searx/language/ro/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: ro\n"
+"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "fișiere"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "general"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "muzică"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "rețele sociale"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "imagini"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "videouri"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "el(ea)"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "știri"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "hartă"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr ""
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr ""
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr ""
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} minut(e) în urmă"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} oră(e), {minutes} minut(e) în urmă"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr ""
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr ""
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr ""
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Încărcarea paginilor (sec)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Numărul de rezultate"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Scoruri"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Scoruri per rezultat"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Erori"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr ""
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr ""
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Rescrie legăturile HTTP cu HTTPS dacă e posibil"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr ""
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr ""
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr ""
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr ""
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Căutare în categoria selectată"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr ""
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr ""
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr ""
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr ""
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr ""
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "preferințe"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "despre"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Preferințe"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Categorii implicite"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Limba de căutare"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Limba interfeței"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Completare automată"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Proxy de imagini"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Activat"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Dezactivat"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Metodă"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "CăutareSigură"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Strict"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Moderat"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Nimic"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Teme"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Culoare"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Albastru (implicit)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violet"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Verde"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Azuriu"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Portocaliu"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Roșu"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Motoarele de căutare folosite curent"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Numele motorului"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Categorie"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Permite"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Blochează"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Aceste setări sunt stocate în cookie-urile d-voastră, aceasta ne permite să nu stocăm aceste date despre d-voastră."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Aceste cookie-uri servesc doar pentru confortul d-voastră, noi nu stocăm aceste cookie-uri pentru a vă urmări."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "salvează"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Resetează valorile implicite"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "înapoi"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "URL de căutare"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Descarcă rezultate"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Răspunsuri"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Sugestii"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "pagina anterioară"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "pagina următoare"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Caută după..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Statisticile motorului"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "contextul original"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Seeder"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Leecher"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "legătură magnet"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "fișier torrent"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Apăsați pe lupă pentru a executa căutarea"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr ""
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr ""
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr ""
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "stocat temporar"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr ""
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Închide"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr ""
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Motorizat de"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "un meta-motor de căutare care respectă confidențialitatea"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "General"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Motoare"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Module"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Ce limbă preferați pentru căutare?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Schimbă limba aspectului"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Găsește lucruri în timp ce tastezi"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Transferă rezultatele cu imagini prin searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Modificați cum sunt trimise formularele, <a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">învățați mai multe despre metodele de transfer</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtrează conținutul"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Schimbă aspectul lui searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr ""
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Rezultatele căutării"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Legături"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Pornește căutarea"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "statistici"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Atenție!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Se pare că folosiți searx pentru prima dată."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr ""
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr ""
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Deocamdată nu există date disponibile."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Îmi pare rău!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "n-am găsit nici un rezultat. Vă rog folosiți o altă interogare sau căutați în mai multe categorii."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Bravo!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Setările au fost salvate cu succes."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Vai!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Ceva n-a funcționat corect."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "arată media"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "ascunde media"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Obține imaginea"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Vizualizare sursă"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "arată harta"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "ascunde harta"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "arată detalii"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "ascunde detalii"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Dimensiune fișier"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Octeți"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Numărul fișierelor"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "arată video"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "ascunde video"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Încarcă mai multe..."
diff --git a/searx/translations/ru/LC_MESSAGES/messages.po b/searx/translations/ru/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..1ffcf5e
--- /dev/null
+++ b/searx/translations/ru/LC_MESSAGES/messages.po
@@ -0,0 +1,845 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Andrey, 2017
+# dimqua <dimqua@riseup.net>, 2015
+# dimqua <dimqua@riseup.net>, 2015
+# Дмитрий Михирев, 2016-2017
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-05-23 09:36+0000\n"
+"Last-Translator: Andrey\n"
+"Language-Team: Russian (http://www.transifex.com/asciimoo/searx/language/ru/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: ru\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "файлы"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "общие"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "музыка"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "социальные сети"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "картинки"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "видео"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "IT"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "новости"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "карты"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "наука"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Неправильные параметры, измените настройки"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "ошибка поиска"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} минут(а) назад"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} час(ов), {minutes} минут(а) назад"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Генератор случайных значений"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Генерирует различные случайные значения"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Статистические функции"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Вычисляет {functions} от аргументов"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Время поиска (сек)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Загрузка страниц (сек)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Количество результатов"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Попаданий"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Попаданий за результат"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Ошибки"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(УСТАРЕВШИЕ)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Этот пункт был заменён на"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "Перезапись DOAI"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Позволяет получить бесплатную версию запрашиваемой статьи, если таковая имеется"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Заменять в ссылках HTTP на HTTPS если это возможно"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Бесконечная прокрутка"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Автоматически загружать следующую страницу при прокрутке до конца текущей"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Открывать ссылки из результатов поиска в новых вкладках"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "По умолчанию результаты открываются в том же окне. Этот плагин переопределяет поведение по умолчанию для открытия ссылок в новых вкладках/окнах. (Требуется JavaScript)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Поиск по выбранной категории"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Выполните поиск немедленно, если выбрана категория. Отключите для выбора нескольких категорий. (требуется JavaScript)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Отображает ваш IP-адрес при запросе \"ip\" и отпечаток браузера при запросе \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Удаление трекера URL-адресов"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Удаляет аргументы отслеживания из возвращенного URL-адреса"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Горячие клавиши в стиле vim"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Навигация по результатам поиска с помощью горячих клавиш в стиле Vim (требуется JavaScript). Чтобы получить справку, нажмите клавишу \"h\" на главной странице или на страницах результатов."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Страница не найдена"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Перейти к %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "страница поиска"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "настройки"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "о сайте"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Настройки"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Категории по умолчанию"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Язык поиска"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Язык по умолчанию"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Язык интерфейса"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Подгрузка результатов"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Прокси для изображений"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Включен"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Выключен"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Метод"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Безопасный поиск"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Строгий"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Умеренный"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Выключен"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Темы"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Цвет"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Синий (по умолчанию)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Фиолетовый"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Зеленый"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Бирюзовый"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Оранжевый"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Красный"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Используемые поисковые машины"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Имя машины"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Категория"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Разрешить"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Блокировать"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Настройки хранятся в ваших файлах cookie, что позволяет нам не сохранять эти сведения о вас."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Эти файлы cookie используются для вашего удобства, мы не используем их чтобы отслеживать вас."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "сохранить"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Восстановить настройки по умолчанию"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "назад"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Ссылка на поиск"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Загрузить результаты"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Ответы"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Предложения"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "предыдущая страница"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "следующая страница"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Искать..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Статистика машин"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "исходный контекст"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Сидер"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Личер"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "магнет-ссылка"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "торрент-файл"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Нажмите на лупу, чтобы выполнить поиск"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Результаты в новых вкладках"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Вкл."
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Выкл."
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "архив"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Дополнительные настройки"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Закрыть"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Ошибка!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Используется"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "уважающая вашу приватность, открытая метапоисковая машина"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "proxy"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Общие"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Машины"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Плагины"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Ответчики"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookie"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "На каком языке вы предпочитаете искать?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Изменение языка сайта"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Поисковые предложения по мере ввода"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Проксировать найденные изображения с помощью searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Изменяет способ отправки запросов. <a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">Узнать больше о методах запроса</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Фильтрация содержимого для взрослых в результатах поиска."
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Изменить вид сайта"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Стиль для выбранной темы"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Стиль"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Сокращение"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Поддержка выбранного языка"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Временной диапазон"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Среднее время"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Максимальное время"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Это список модулей мгновенного ответа searx"
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Имя"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Ключевые слова"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Описание"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Примеры"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Это список файлов cookie и их значения, которые searx хранит на вашем компьютере."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "С помощью этого списка можно оценить прозрачность searx."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Имя файла cookie"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Значение"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Результаты поиска"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Ссылки"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Начать поиск"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "статистика"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Когда угодно"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Последние сутки"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Последняя неделя"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Последний месяц"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Последний год"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Эй!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Похоже, вы используете searx впервые."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Информация!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "в настоящее время не определены файлы cookie."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "На данный момент данные недоступны."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Сожалеем!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "мы не нашли никаких результатов. Попробуйте другой запрос или поищите в других категориях."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Отлично!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Настройки успешно сохранены."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Вот черт!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Что-то пошло не так."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "показать медиа"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "скрыть медиа"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Скачать изображение"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Посмотреть источник"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "показать карту"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "скрыть карту"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "показать подробности"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "скрыть подробности"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Размер файла"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Байт"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "КБ"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "МБ"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "ГБ"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "ТБ"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Число файлов"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "показать видео"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "скрыть видео"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Загрузить еще…"
diff --git a/searx/translations/sk/LC_MESSAGES/messages.po b/searx/translations/sk/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..3ed0813
--- /dev/null
+++ b/searx/translations/sk/LC_MESSAGES/messages.po
@@ -0,0 +1,842 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Jan Hovancik, 2017
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Jan Hovancik\n"
+"Language-Team: Slovak (http://www.transifex.com/asciimoo/searx/language/sk/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: sk\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "súbory"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "všeobecné"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "hudba"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "sociálne médiá"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "obrázky"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "videá"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "technika"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "správy"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "mapy"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "veda"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Neplatné nastavenia, upravte svoje hodnoty, prosím"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "chyba vyhľadávania"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} min. pred"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} hod., {minutes} min. pred"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Generátor nahodných hodnôt"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Vytvoriť iné náhodné hodnoty"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Štatistické funkcie"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Vypočítať {functions} argumentov"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Načítanie vyhľadávača (sek)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Načítanie stránky (sek)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Počet výsledkov"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr ""
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr ""
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Chyby"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(ZASTARANÉ)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Táto položka bola nahradená"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "Prepis DOAI"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Vyhnúť sa plateným bránam presmerovaním na verejne prístupné verzie publikácií ak sú k dispozícii"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Prepísať odkazy HTTP na HTTPS, ak je to možné"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Nekonečné posúvanie"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Automaticky načítať ďalšiu stránku pri posunutí na koniec aktuálnej stránky"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Otvoriť odkazy v novom tabe"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Výsledky sú otvorené v rovnakom okne predvolene. Tento plugin prepíše predvolené správanie otvoriania odkazov na nových taboch a oknách. (Je potrebný JavaScript)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Vyhľadávanie pri výbere kategórie"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Vyhľadávať okamžite, ak je kategória vybraná. Vypnúť pre vyberanie viacerých kategórií. (Je potrebný JavaScript)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Zobrazí vašu IP ak je dotaz \"ip\" a user agenta ak dotaz obsahuje \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Odstraňovanie sledovacích argumentov"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Odstrániť sledovacie argumenty z vrátenej URL"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Skratky ako vo VIM"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Prechádzať výsledky vyhľadávania klávesovými skratkami ako VIM (je potrebný JavaScript). Stlačte klávesy \"h\" na hlavnej stránke alebo na stránke s výsledkami pre zobrazenie pomoci."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Stránka sa nenašla."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Choď na %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "stránka vyhľadávania"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "nastavenia"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "o nás"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Nastavenia"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Predvolené kategórie"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Jazyk vyhľadávania"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Predvolený jazyk"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Jazyk rozhrania"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Automatické dokončovanie"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Proxy pre obrázky"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Povolené"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Zakázané"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Metóda"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Bezpečné vyhľadávanie"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Striktné"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Mierne"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Žiadne"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Téma"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Farba"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Modrá (predvolené)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Fialová"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Zelená"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Azúrová"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Oranžová"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Červená"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "poruba@contours.cz"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Názov vyhľadávača"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Kategória"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Povoliť"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Blokovať"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Tieto nastavenia sú uložené v cookies, čo nám umožňuje neukladať dáta o vás na našej strane."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Tieto cookies slúžia výhradné pre vaše pohodlie a nie sú používané na sledovanie."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "uložiť"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Obnoviť predvolené"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "späť"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Adresa URL vyhľadávania"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Výsledky na stiahnutie"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Odpovede"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Návrhy"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "predchádzajúca strana"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "ďalšia strana"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Hľadať..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Štatistiky vyhľadávača"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "pôvodný kontext"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr ""
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "odkaz na magnet"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "torrent súbor"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Kliknite na lupu pre vyhľadávanie"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Výsledky v novom tabe"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Zapnuté"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Vypnuté"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "z vyrovnávacej pamäte"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Pokročilé nastavenia"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Zatvoriť"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Chyba!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Používame"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "prispôsobitelný meta-vyhľadávač, ktorý rešpektuje vaše súkromie"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "cez proxy"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Všeobecné"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Vyhľadávače"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Zásuvné moduly"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Rýchle odpovede"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookies"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Aký jazyk preferujete pre vyhľadávanie?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Zmena jazyku rozhrania"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Vyhľadávať počas písania"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Zobrazovanie výsledkov obrázkov cez searx proxy"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Zmeniť spôsob, akým sú odosielané formuláre, <a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">dozvedieť sa viac o týchto metódach</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtrovanie obsahu"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Zmena rozhrania searx"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Vyberte si štýl pre túto tému"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Štýl"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Skratka"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Podporuje zvolený jazyk"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Časový rozsah"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Priemerný čas"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Maximálny čas"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Toto je zoznam modulov rýchlej odpovede pre searx."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Názov"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Kľúčové slová"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Popis"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Príklady"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Toto je zoznam cookies a ich hodnôt uložených searx na vašom počítači"
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Pomocou tohto zoznamu môžte vidieť transparentnosť searx."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Názov cookie"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Hodnota"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Výsledky vyhľadávania"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Odkazy"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Začať vyhľadávanie"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "štatistiky"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Kedykoľvek"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Posledný deň"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Posledný týždeň"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Posledný mesiac"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Posledný rok"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Pozor!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Zdá sa, že používate searx prvýkrát."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Informácia!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "momentálne nie su definované žiadne cookies."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Momentálne nie su dostupné žiadne dáta."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Je nám ľúto!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "nepodarilo sa nájsť žiadne výsledky. Skúste použiť iné zadanie alebo vyhľadávajte vo viacerých kategóriach. "
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Dobrá práca!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Nastavenia sa uložili."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Ó nie!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Stalo sa niečo neočakávané."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "ukázať médiá"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "skryť médiá"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Získať obrázok"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Zobraziť zdroj"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "ukázať mapu"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "skryť mapu"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "ukázať detaily"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "skryť detaily"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Veľkosť súboru"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "bajtov"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Počet súborov"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "ukázať video"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "skryť video"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Načítať viac..."
diff --git a/searx/translations/sv/LC_MESSAGES/messages.po b/searx/translations/sv/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..31ae163
--- /dev/null
+++ b/searx/translations/sv/LC_MESSAGES/messages.po
@@ -0,0 +1,842 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Jonatan Nyberg <jonatan@autistici.org>, 2016-2017
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-04-17 06:23+0000\n"
+"Last-Translator: Jonatan Nyberg <jonatan@autistici.org>\n"
+"Language-Team: Swedish (http://www.transifex.com/asciimoo/searx/language/sv/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: sv\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "filer"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "allmän"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "musik"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "sociala media"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "bilder"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "videor"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "it"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "nyheter"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "karta"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "vetenskap"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Ogiltiga inställningar, vänligen redigerar dina inställningar"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "sök fel"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} minut(er) sedan"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} timm(e/ar), {minutes} minut(er) sedan"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Slumpvärdesgenerator"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Generera olika slumpmässiga värden"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Statistikfunktioner"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Beräkna {functions} av argumenten"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Sökmotor tid (sek)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Sidan laddas (sek)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Antal resultat"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Poäng"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Poäng per resultat"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Fel"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(FÖRÅLDRAD)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Detta inlägg har ersatts av"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "DOAI omskrivning"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr "Undvik betalväggar genom att omdirigera till öppen tillgång versioner av publikationer när de är tillgängliga"
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Omskriv HTTP-länkar till HTTPS om möjligt"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Oändlig bläddring"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Automatiskt ladda nästa sida när du bläddrar till botten av aktuell sida"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Öppna resultat länkar i nya webbläsarflikar"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "Resultat öppnas i samma fönster som standard. Denna insticksmodul skriver över standardbeteende för att öppna länkar i nya flikar/fönster. (JavaScript krävs)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Sök på kategori välj"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Utför sökning omedelbart om en kategori är vald. Inaktivera att välja flera kategorier. (JavaScript krävs)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Visar din IP om förfrågan är \"ip\" och din användaragent om förfrågan innehåller \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "Tracker-webbadress borttagare"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "Ta bort tracker-argument från den återgivna webbadressen"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "Vim-liknande snabbtangenter"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "Navigera sökresultat med Vim-liknande snabbtangenter (JavaScript krävs). Tryck på \"h\"-tangenten på huvud- eller resultatsida för att få hjälp."
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Sidan hittades inte"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Gå till %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "söksida"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "inställningar"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "om"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Inställningar"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Standard kategorier"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Sök språk"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Standard språk"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Gränssnitt språk"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Autoslutför"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Bildproxy"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Aktiverad"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Inaktiverad"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Metod"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "SafeSearch"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Sträng"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Måttlig"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Inga"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Teman"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Färg"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Blå (standard)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Violett"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Grön"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Turkos"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Orange"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Röd"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "För tillfället används sökmotorer"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Sökmotorns namn"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Kategori"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Tillåt"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Blockera"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Dessa inställningar lagras i dina kakor, detta gör att vi inte lagrar data om dig."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Dessa kakor tjänar din egen bekvämlighet, vi använder inte dessa cookies för att spåra dig."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "spara"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Återställ standardvärden"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "tillbaka"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Sök webbadress"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Nedladdningsresultat"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Svar"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Förslag"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "föregående sida"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "nästa sida"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Sök efter..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Sökmotor statistik"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "ursprungliga sammanhang"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Distributör"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Reciprokör"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "magnet länk"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "torrentfil"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Klicka på förstoringsglaset för att utföra sökning"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Resultat i nya flikar"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "På"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Av"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "cachad"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Avancerade inställningar"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Stäng"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Fel!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Drivet av"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "en integritetsrespekterande, hackningsbar metasökmotor"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "proxade"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Allmän"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Sökmotorer"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Insticksmoduler"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Svarare"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Kakor"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Vilket språk föredrar du för att söka?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Ändra språk för layouten"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Hitta saker medan du skriver"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Proxya bildresultat genom searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Ändra hur formulär inlämnas, <a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">lär dig mera om förfrågningsmetoder</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Filtrera innehåll"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Ändra searx layout"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Välj stil för detta tema"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Stil"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Genväg"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Stöder valda språket"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Tidsintervall"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Genomsnittstid"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Max tid"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Detta är en lista över searxs snabbsvarsmoduler."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Namn"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Nyckelord"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Beskrivning"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Exempel"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Detta är en lista över kakor och deras värden searx lagrar på din dator."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "Med denna lista kan du bedöma searx öppenhet."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Kaknamn"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Värde"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Sökresultat"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Länkar"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Starta sökning"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "statistik"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "Närsom"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "Senaste dag"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "Senaste vecka"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "Senaste månad"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "Senaste år"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Se upp!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Det ser ut som om du använder searx första gången."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Information!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "för närvarande finns det inga kakor definierade."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Det finns för närvarande ingen data tillgänglig."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Ursäkta!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "vi hittade inte några resultat. Använd en annan förfråga eller sök i flera kategorier."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Bra gjort!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Inställningar sparats."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Oh plötsligt!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Något gick fel."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "visa media"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "göm media"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Hämta bild"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Visa källa"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "visa karta"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "göm karta"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "visa detaljer"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "göm detaljer"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Filstorlek"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bytes"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Antal filer"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "visa video"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "göm video"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Ladda fler..."
diff --git a/searx/translations/tr/LC_MESSAGES/messages.po b/searx/translations/tr/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..06eb9f7
--- /dev/null
+++ b/searx/translations/tr/LC_MESSAGES/messages.po
@@ -0,0 +1,843 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# Caner Başaran <basaran.caner@protonmail.com>, 2014-2016
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2014
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Adam Tauber <asciimoo@gmail.com>\n"
+"Language-Team: Turkish (http://www.transifex.com/asciimoo/searx/language/tr/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: tr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "dosyalar"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "genel"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "müzik"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "sosyal medya"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "görseller"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "videolar"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "bilişim"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "haberler"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "harita"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "bilim"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr ""
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr ""
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} dakika() önce"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} saat(), {minutes} dakika() önce"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr ""
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr ""
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr ""
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr ""
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Yüklenen sayfa (sn)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Sonuç sayısı"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr ""
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr ""
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Hatalar"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr ""
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr ""
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "Mümkünse HTTP bağlantıları HTTPS olarak düzelt"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr ""
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr ""
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Bağlantıları yeni sekmede aç"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr ""
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr ""
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr ""
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr ""
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr ""
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr ""
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr ""
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "tercihler"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "hakkında"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Tercihler"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Öntanımlı kategoriler"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Arama dili"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Arayüz dili"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Otomatik tamamlama"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Görsel vekil sunucu"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Etkin"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Devre dışı"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Sorgu gönderim yöntemi"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "Güvenli Arama"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Sıkı"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Orta"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Yok"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Temalar"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Renk"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Mavi (varsayılan)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Menekşe rengi"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Yeşil"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Camgöbeği"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Turuncu"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Kırmızı"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Şu anda kullanılan arama motorları"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Motor adı"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Türü"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "İzin ver"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Engelle"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Ayarlar çerezlerinizde saklanır. Verdiğiniz izinler, sizin hakkınızda veri saklamak için değildir."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Bu çerezler size kolaylık sağlar. Sizi takip etmek için kullanılmaz."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "kaydet"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Varsayılanları sıfırla"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "geri"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Arama Bağlantısı"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Arama sonuçlarını indir"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr ""
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Öneriler"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "önceki sayfa"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "sonraki sayfa"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Aramak için..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Arama motoru istatistikleri"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "özgün içerik"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Besleyenler"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Sömürenler"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "mıknatıs bağlantı"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "torrent dosyası"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Arama yapmak için büyütece tıklayın"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr ""
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr ""
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr ""
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "önbellek"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr ""
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Kapat"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr ""
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Gücümün kaynağı"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "kişisel gizliliğe saygılı ve merak edenlerin kurcalayabildiği bir meta arama motoru"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Genel"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Motorlar"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Eklentiler"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Tercih ettiğiniz arama sonuçları dili"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Site dilini değiştir"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Leb demeden leblebiyi anlasın"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Görsel arama sonuçlarını, searx vekil sunucusu üzerinden geçir"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Aramaların nasıl gönderildiğini değiştir, <a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">istek yöntemleri hakkında daha fazla bilgi</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "İçeriyi süz"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "searx yerleşim düzenini değiştir"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr ""
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Arama sonuçları"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Bağlantılar"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Aramayı başlat"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "istatistikler"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr ""
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Dikkat et!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "İlk defa searx kullanıyor gibi görünüyorsunuz."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr ""
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr ""
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "Şuan uygun veri yok."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Üzgünüz!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "herhangi bir sonuç bulamadık. Lütfen, başka sorgu kullanın veya daha fazla kategoride arama yapın."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Aferin!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Ayarlar başarıyla kaydedildi."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "Tüh tüh!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Bazı bazı şeylerde problem olmuş."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "medya göster"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "medya gizle"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Görseli indir"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Kaynağı göster"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "haritayı göster"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "haritayı gizle"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "ayrıntıları göster"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "ayrıntıları gizle"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Dosya boyutu"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Bayt"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "kiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TiB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Dosya Sayısı"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "video göster"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "video gizle"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Daha fazlasını getir..."
diff --git a/searx/translations/uk/LC_MESSAGES/messages.po b/searx/translations/uk/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..9e8ad85
--- /dev/null
+++ b/searx/translations/uk/LC_MESSAGES/messages.po
@@ -0,0 +1,843 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# yRI pvm <pvhn4@protonmail.com>, 2017
+# zubr139, 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: yRI pvm <pvhn4@protonmail.com>\n"
+"Language-Team: Ukrainian (http://www.transifex.com/asciimoo/searx/language/uk/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: uk\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "файли"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "загальні"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "музика"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "соцмережі"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "зображення"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "відео"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "IT"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "новини"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "карти"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "наука"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "Невірні налаштування, будь ласка, зробіть зміни в налаштуваннях"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "помилка пошуку"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes} хвилин тому"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours} годин, {minutes} хвилин тому"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "Генератор випадкових значень"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "Створити різні випадкові значення"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "Функції статистики"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr "Розрахувати {functions} аргументів"
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr "Час пошуку (сек)"
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "Час завантадення (сек)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "Число результатів"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "Влучань"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "Влучань за результат"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "Помилок"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr "{title}&nbsp;(OBSOLETE)"
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr "Цей запис був змінений"
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr ""
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "За можливістю замінити в посиланнях HTTP на HTTPS"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "Нескінченна прокрутка"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "Автоматично завантажувати наступну стрінку при прокручуванні поточної до кінця"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "Відкривати посилання результатів в нових вкладках"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "За замовчанням результати відкриваються в тому ж вікні. Цей плагін змінює поведінку за замовчанням, щоб посилання відкривались в нових вкладках/вікнах. (Необхідний JavaScript)"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "Пошук по обраній категорії"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "Виконувати пошук зразу при обранні категорії. Вимкнути вибір декількох категорій. (Необхідний JavaScript)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "Відображає IP-адресу при запиті \"ip\" та ваш user-agent при запиті \"user agent\"."
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr ""
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr ""
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr ""
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "Сторінка не знайдена"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "Перейти до %(search_page)s."
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "сторінки пошуку"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "опцій"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "про сайт"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "Опції"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "Категорії за замовчуванням"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "Мова пошуку"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr "Мова за замовчуванням"
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "Мова інтерфейсу"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "Автозаповнення"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "Проксі для зображень"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "Ввімкнено"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "Вимкнено"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "Метод"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "БезпечнийПошук"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "Жорский"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "Помірний"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "Вимкнений"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "Теми"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "Колір"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "Синій (за замовчуванням)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "Фіолетовий"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "Зелений"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "Блакитний"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "Помаранчевий"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "Червоний"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "Пошукові системи, які використовуються"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "Назва пошукової системи"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "Категорія"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "Дозволити"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "Заблокувати"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "Налаштування зберігаються в ваших cookie-файлах, що дає нам змогу не зберігати ці відомості про вас."
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "Ці cookie-файли необхідні винятково для вашої зручності, ми не використовуємо ці cookie-файли, щоб відслідковувати вас."
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "зберегти"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "Відновити налаштування за замовчуванням"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "назад"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "Посилання на пошук"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "Завантажити результати"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "Відповіді"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "Пропозиції"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "попередня сторінка"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "наступна сторінка"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "Шукати..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "Статистика пошукової системи"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "в контексті"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Сідер"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Лічер"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "магнет-посилання"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "торрент-файл"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "Натисніть лупу, щоб виконати пошук"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "Результати в нових вкладках"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "Ввімк."
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "Вимк."
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "архівовано"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "Додаткові налаштування"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "Закрити"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr "Помилка!"
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Використовується"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "вільна система метапошуку, яка поважає вашу приватність"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr "проксовано"
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "Загальні"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "Пошукові системи"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "Плагіни"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr "Відповідачі"
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookie-файли"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "Якій мові ви віддаєте перевагу для пошуку?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "Змінити мову сайту"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "Шукати підчас набору"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "Проксувати знайдені зображення за допомогою searx"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "Змінити спосіб відправки запитів, <a href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">детальніше про методи запитів</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "Фільтр контенту"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "Змінити вигляд сайту"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "Обрати стиль для цієї теми"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "Стиль"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "Гарячі клавіші"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr "Підтримка обраної мови"
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr "Часовий діапазон"
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "Середній час"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "Максимальний час"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr "Список модулів миттєвих відповідей searx."
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "Назва"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "Ключові слова"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "Опис"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "Приклади"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "Це список cookie-файлів та їх значень, які searx зберігає на вашому комп'ютері."
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "По цьому списку ви можете оцінити відкритість searx."
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "Ім'я cookie"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "Значення"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "Результати пошуку"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "Посилання"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "Розпочати пошук"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "статистика"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "За весь час"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "За останній день"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "За останній тиждень"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "За останній місяць"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr "За останній рік"
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "Отакої!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "Схоже, що ви використовуєте searx вперше."
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "Інформація!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "в даний час cookie-файли не встановлені."
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "В даний час немає доступних даних."
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "Вибачте!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "ми не знайшли жодних результатів. Будь ласка, використайте інший запит або виконайте пошук в декількох категоріях."
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "Чудово!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "Налаштування успішно збережені."
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "От халепа!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "Щось пішло не так."
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "показати медіа"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "приховати медіа"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "Завантажити зображення"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "Переглянути джерело"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "показати карту"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "приховати карту"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "показати деталі"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "приховати деталі"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "Розмір файла"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "Байтів"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "КіБ"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "МіБ"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "ГіБ"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "ТіБ"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "Кількість Файлів"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "показати відео"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "приховати відео"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "Завантажити більше..."
diff --git a/searx/translations/zh_CN/LC_MESSAGES/messages.po b/searx/translations/zh_CN/LC_MESSAGES/messages.po
new file mode 100644
index 0000000..c37c71e
--- /dev/null
+++ b/searx/translations/zh_CN/LC_MESSAGES/messages.po
@@ -0,0 +1,843 @@
+# Translations template for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+#
+# Translators:
+# wenke, 2015
+# wenke, 2015-2016
+msgid ""
+msgstr ""
+"Project-Id-Version: searx\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-12-29 10:42+0100\n"
+"PO-Revision-Date: 2017-03-18 20:30+0000\n"
+"Last-Translator: Adam Tauber <asciimoo@gmail.com>\n"
+"Language-Team: Chinese (China) (http://www.transifex.com/asciimoo/searx/language/zh_CN/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+"Language: zh_CN\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+#: searx/webapp.py:123
+msgid "files"
+msgstr "文件"
+
+#: searx/webapp.py:124
+msgid "general"
+msgstr "全部"
+
+#: searx/webapp.py:125
+msgid "music"
+msgstr "音乐"
+
+#: searx/webapp.py:126
+msgid "social media"
+msgstr "社交媒体"
+
+#: searx/webapp.py:127
+msgid "images"
+msgstr "图片"
+
+#: searx/webapp.py:128
+msgid "videos"
+msgstr "视频"
+
+#: searx/webapp.py:129
+msgid "it"
+msgstr "it"
+
+#: searx/webapp.py:130
+msgid "news"
+msgstr "新闻"
+
+#: searx/webapp.py:131
+msgid "map"
+msgstr "地图"
+
+#: searx/webapp.py:132
+msgid "science"
+msgstr "学术"
+
+#: searx/webapp.py:384 searx/webapp.py:594
+msgid "Invalid settings, please edit your preferences"
+msgstr "无效设置,请编辑你的首选项"
+
+#: searx/webapp.py:425
+msgid "search error"
+msgstr "搜索错误"
+
+#: searx/webapp.py:467
+msgid "{minutes} minute(s) ago"
+msgstr "{minutes}分钟之前"
+
+#: searx/webapp.py:469
+msgid "{hours} hour(s), {minutes} minute(s) ago"
+msgstr "{hours}小时{minutes}分钟之前"
+
+#: searx/answerers/random/answerer.py:48
+msgid "Random value generator"
+msgstr "随机数生成器"
+
+#: searx/answerers/random/answerer.py:49
+msgid "Generate different random values"
+msgstr "生成不同的随机数"
+
+#: searx/answerers/statistics/answerer.py:49
+msgid "Statistics functions"
+msgstr "统计功能"
+
+#: searx/answerers/statistics/answerer.py:50
+msgid "Compute {functions} of the arguments"
+msgstr ""
+
+#: searx/engines/__init__.py:192
+msgid "Engine time (sec)"
+msgstr ""
+
+#: searx/engines/__init__.py:196
+msgid "Page loads (sec)"
+msgstr "页面加载 (秒)"
+
+#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
+msgid "Number of results"
+msgstr "结果数"
+
+#: searx/engines/__init__.py:204
+msgid "Scores"
+msgstr "得分"
+
+#: searx/engines/__init__.py:208
+msgid "Scores per result"
+msgstr "每个结果等分"
+
+#: searx/engines/__init__.py:212
+msgid "Errors"
+msgstr "错误"
+
+#: searx/engines/pdbe.py:87
+msgid "{title}&nbsp;(OBSOLETE)"
+msgstr ""
+
+#: searx/engines/pdbe.py:91
+msgid "This entry has been superseded by"
+msgstr ""
+
+#: searx/plugins/doai_rewrite.py:7
+msgid "DOAI rewrite"
+msgstr "DOAI改写"
+
+#: searx/plugins/doai_rewrite.py:8
+msgid ""
+"Avoid paywalls by redirecting to open-access versions of publications when "
+"available"
+msgstr ""
+
+#: searx/plugins/https_rewrite.py:29
+msgid "Rewrite HTTP links to HTTPS if possible"
+msgstr "如果可能的话重定向HTTP链接到HTTPS"
+
+#: searx/plugins/infinite_scroll.py:3
+msgid "Infinite scroll"
+msgstr "无限滚动"
+
+#: searx/plugins/infinite_scroll.py:4
+msgid "Automatically load next page when scrolling to bottom of current page"
+msgstr "滚动到当前页面底部时自动加载下一页"
+
+#: searx/plugins/open_results_on_new_tab.py:18
+#: searx/templates/oscar/preferences.html:113
+msgid "Open result links on new browser tabs"
+msgstr "在新标签页打开搜索链接"
+
+#: searx/plugins/open_results_on_new_tab.py:19
+msgid ""
+"Results are opened in the same window by default. This plugin overwrites the"
+" default behaviour to open links on new tabs/windows. (JavaScript required)"
+msgstr "搜索结果默认在原窗口打开。这个插件使其在新标签页/窗口打开。(需要启用JavaScript )"
+
+#: searx/plugins/search_on_category_select.py:18
+msgid "Search on category select"
+msgstr "搜索类别选择"
+
+#: searx/plugins/search_on_category_select.py:19
+msgid ""
+"Perform search immediately if a category selected. Disable to select "
+"multiple categories. (JavaScript required)"
+msgstr "选中一个类别立即搜索。禁用可以选择多个类别搜索。(JavaScript 启用)"
+
+#: searx/plugins/self_info.py:20
+msgid ""
+"Displays your IP if the query is \"ip\" and your user agent if the query "
+"contains \"user agent\"."
+msgstr "搜索“ip”显示你的ip以及搜索内容含有“user agent”显示你的user agent。"
+
+#: searx/plugins/tracker_url_remover.py:26
+msgid "Tracker URL remover"
+msgstr "移除跟踪链接"
+
+#: searx/plugins/tracker_url_remover.py:27
+msgid "Remove trackers arguments from the returned URL"
+msgstr "从返回的链接中移除跟踪参数"
+
+#: searx/plugins/vim_hotkeys.py:3
+msgid "Vim-like hotkeys"
+msgstr "类vim快捷键"
+
+#: searx/plugins/vim_hotkeys.py:4
+msgid ""
+"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
+"\"h\" key on main or result page to get help."
+msgstr "使用类vim快捷键浏览搜索结果(JavaScript启用)。按“h”键获取帮助。"
+
+#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
+#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
+msgid "Page not found"
+msgstr "未找到网页"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+#, python-format
+msgid "Go to %(search_page)s."
+msgstr "返回%(search_page)s。"
+
+#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
+#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
+msgid "search page"
+msgstr "搜索页面"
+
+#: searx/templates/courgette/index.html:9
+#: searx/templates/courgette/index.html:13
+#: searx/templates/courgette/results.html:5
+#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
+#: searx/templates/oscar/navbar.html:12
+#: searx/templates/oscar/preferences.html:3
+#: searx/templates/pix-art/index.html:8
+msgid "preferences"
+msgstr "首选项"
+
+#: searx/templates/courgette/index.html:11
+#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
+#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
+msgid "about"
+msgstr "关于"
+
+#: searx/templates/courgette/preferences.html:5
+#: searx/templates/legacy/preferences.html:5
+#: searx/templates/oscar/preferences.html:7
+#: searx/templates/pix-art/preferences.html:5
+msgid "Preferences"
+msgstr "首选项"
+
+#: searx/templates/courgette/preferences.html:9
+#: searx/templates/legacy/preferences.html:9
+#: searx/templates/oscar/preferences.html:32
+#: searx/templates/oscar/preferences.html:34
+msgid "Default categories"
+msgstr "默认类别"
+
+#: searx/templates/courgette/preferences.html:13
+#: searx/templates/legacy/preferences.html:14
+#: searx/templates/oscar/preferences.html:40
+#: searx/templates/pix-art/preferences.html:9
+msgid "Search language"
+msgstr "搜索语言"
+
+#: searx/templates/courgette/preferences.html:16
+#: searx/templates/legacy/preferences.html:17
+#: searx/templates/oscar/languages.html:6
+#: searx/templates/pix-art/preferences.html:12
+msgid "Default language"
+msgstr ""
+
+#: searx/templates/courgette/preferences.html:24
+#: searx/templates/legacy/preferences.html:25
+#: searx/templates/oscar/preferences.html:46
+#: searx/templates/pix-art/preferences.html:20
+msgid "Interface language"
+msgstr "界面语言"
+
+#: searx/templates/courgette/preferences.html:34
+#: searx/templates/legacy/preferences.html:35
+#: searx/templates/oscar/preferences.html:56
+msgid "Autocomplete"
+msgstr "自动补全"
+
+#: searx/templates/courgette/preferences.html:45
+#: searx/templates/legacy/preferences.html:46
+#: searx/templates/oscar/preferences.html:67
+msgid "Image proxy"
+msgstr "代理图片"
+
+#: searx/templates/courgette/preferences.html:48
+#: searx/templates/legacy/preferences.html:49
+#: searx/templates/oscar/preferences.html:71
+msgid "Enabled"
+msgstr "启用"
+
+#: searx/templates/courgette/preferences.html:49
+#: searx/templates/legacy/preferences.html:50
+#: searx/templates/oscar/preferences.html:72
+msgid "Disabled"
+msgstr "禁用"
+
+#: searx/templates/courgette/preferences.html:54
+#: searx/templates/legacy/preferences.html:55
+#: searx/templates/oscar/preferences.html:76
+#: searx/templates/pix-art/preferences.html:30
+msgid "Method"
+msgstr "方法"
+
+#: searx/templates/courgette/preferences.html:63
+#: searx/templates/legacy/preferences.html:64
+#: searx/templates/oscar/preferences.html:85
+#: searx/templates/oscar/preferences.html:152
+#: searx/templates/oscar/preferences.html:159
+msgid "SafeSearch"
+msgstr "安全搜索"
+
+#: searx/templates/courgette/preferences.html:66
+#: searx/templates/legacy/preferences.html:67
+#: searx/templates/oscar/preferences.html:89
+msgid "Strict"
+msgstr "严格模式"
+
+#: searx/templates/courgette/preferences.html:67
+#: searx/templates/legacy/preferences.html:68
+#: searx/templates/oscar/preferences.html:90
+msgid "Moderate"
+msgstr "中等"
+
+#: searx/templates/courgette/preferences.html:68
+#: searx/templates/legacy/preferences.html:69
+#: searx/templates/oscar/preferences.html:91
+msgid "None"
+msgstr "不过滤"
+
+#: searx/templates/courgette/preferences.html:73
+#: searx/templates/legacy/preferences.html:74
+#: searx/templates/oscar/preferences.html:95
+#: searx/templates/pix-art/preferences.html:39
+msgid "Themes"
+msgstr "主题"
+
+#: searx/templates/courgette/preferences.html:83
+msgid "Color"
+msgstr "颜色"
+
+#: searx/templates/courgette/preferences.html:86
+msgid "Blue (default)"
+msgstr "蓝色(默认)"
+
+#: searx/templates/courgette/preferences.html:87
+msgid "Violet"
+msgstr "紫色"
+
+#: searx/templates/courgette/preferences.html:88
+msgid "Green"
+msgstr "绿色"
+
+#: searx/templates/courgette/preferences.html:89
+msgid "Cyan"
+msgstr "青色"
+
+#: searx/templates/courgette/preferences.html:90
+msgid "Orange"
+msgstr "橙色"
+
+#: searx/templates/courgette/preferences.html:91
+msgid "Red"
+msgstr "红色"
+
+#: searx/templates/courgette/preferences.html:96
+#: searx/templates/legacy/preferences.html:93
+#: searx/templates/pix-art/preferences.html:49
+msgid "Currently used search engines"
+msgstr "目前使用的搜索引擎"
+
+#: searx/templates/courgette/preferences.html:100
+#: searx/templates/legacy/preferences.html:97
+#: searx/templates/oscar/preferences.html:149
+#: searx/templates/oscar/preferences.html:162
+#: searx/templates/pix-art/preferences.html:53
+msgid "Engine name"
+msgstr "搜索引擎名称"
+
+#: searx/templates/courgette/preferences.html:101
+#: searx/templates/legacy/preferences.html:98
+msgid "Category"
+msgstr "类别"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:113
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:110
+#: searx/templates/oscar/preferences.html:148
+#: searx/templates/oscar/preferences.html:163
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:64
+msgid "Allow"
+msgstr "允许"
+
+#: searx/templates/courgette/preferences.html:102
+#: searx/templates/courgette/preferences.html:114
+#: searx/templates/legacy/preferences.html:99
+#: searx/templates/legacy/preferences.html:111
+#: searx/templates/pix-art/preferences.html:54
+#: searx/templates/pix-art/preferences.html:65
+msgid "Block"
+msgstr "阻止"
+
+#: searx/templates/courgette/preferences.html:122
+#: searx/templates/legacy/preferences.html:119
+#: searx/templates/oscar/preferences.html:282
+#: searx/templates/pix-art/preferences.html:73
+msgid ""
+"These settings are stored in your cookies, this allows us not to store this "
+"data about you."
+msgstr "这些设置保存在你的cookie,我们不能保存你的数据。"
+
+#: searx/templates/courgette/preferences.html:124
+#: searx/templates/legacy/preferences.html:121
+#: searx/templates/oscar/preferences.html:284
+#: searx/templates/pix-art/preferences.html:75
+msgid ""
+"These cookies serve your sole convenience, we don't use these cookies to "
+"track you."
+msgstr "这些cookie是为了让你更加方便,我们不会使用这些cookie追踪你。"
+
+#: searx/templates/courgette/preferences.html:127
+#: searx/templates/legacy/preferences.html:124
+#: searx/templates/oscar/preferences.html:287
+#: searx/templates/pix-art/preferences.html:78
+msgid "save"
+msgstr "保存"
+
+#: searx/templates/courgette/preferences.html:128
+#: searx/templates/legacy/preferences.html:125
+#: searx/templates/oscar/preferences.html:289
+msgid "Reset defaults"
+msgstr "恢复默认"
+
+#: searx/templates/courgette/preferences.html:129
+#: searx/templates/legacy/preferences.html:126
+#: searx/templates/oscar/preferences.html:288
+#: searx/templates/pix-art/preferences.html:79
+msgid "back"
+msgstr "返回"
+
+#: searx/templates/courgette/results.html:12
+#: searx/templates/legacy/results.html:13
+#: searx/templates/oscar/results.html:124
+msgid "Search URL"
+msgstr "搜索链接"
+
+#: searx/templates/courgette/results.html:16
+#: searx/templates/legacy/results.html:17
+#: searx/templates/oscar/results.html:129
+msgid "Download results"
+msgstr "下载结果"
+
+#: searx/templates/courgette/results.html:34
+#: searx/templates/legacy/results.html:35
+msgid "Answers"
+msgstr "回答"
+
+#: searx/templates/courgette/results.html:42
+#: searx/templates/legacy/results.html:43
+#: searx/templates/oscar/results.html:104
+msgid "Suggestions"
+msgstr "搜索建议"
+
+#: searx/templates/courgette/results.html:70
+#: searx/templates/legacy/results.html:81
+#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
+msgid "previous page"
+msgstr "上一页"
+
+#: searx/templates/courgette/results.html:81
+#: searx/templates/legacy/results.html:92
+#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
+msgid "next page"
+msgstr "下一页"
+
+#: searx/templates/courgette/search.html:3
+#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
+#: searx/templates/oscar/search_full.html:9
+#: searx/templates/pix-art/search.html:3
+msgid "Search for..."
+msgstr "搜索..."
+
+#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
+#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
+msgid "Engine stats"
+msgstr "搜索引擎统计"
+
+#: searx/templates/courgette/result_templates/images.html:4
+#: searx/templates/legacy/result_templates/images.html:4
+#: searx/templates/pix-art/result_templates/images.html:4
+msgid "original context"
+msgstr "原始上下文"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Seeder"
+msgstr "Seeder"
+
+#: searx/templates/courgette/result_templates/torrent.html:7
+#: searx/templates/legacy/result_templates/torrent.html:11
+#: searx/templates/oscar/result_templates/torrent.html:6
+msgid "Leecher"
+msgstr "Leecher"
+
+#: searx/templates/courgette/result_templates/torrent.html:9
+#: searx/templates/legacy/result_templates/torrent.html:9
+#: searx/templates/oscar/macros.html:24
+msgid "magnet link"
+msgstr "磁力链接"
+
+#: searx/templates/courgette/result_templates/torrent.html:10
+#: searx/templates/legacy/result_templates/torrent.html:10
+#: searx/templates/oscar/macros.html:25
+msgid "torrent file"
+msgstr "种子文件"
+
+#: searx/templates/legacy/categories.html:8
+msgid "Click on the magnifier to perform search"
+msgstr "点击放大镜执行搜索"
+
+#: searx/templates/legacy/preferences.html:84
+#: searx/templates/oscar/preferences.html:112
+msgid "Results on new tabs"
+msgstr "在新标签页打开搜索结果"
+
+#: searx/templates/legacy/preferences.html:87
+#: searx/templates/oscar/preferences.html:116
+msgid "On"
+msgstr "开"
+
+#: searx/templates/legacy/preferences.html:88
+#: searx/templates/oscar/preferences.html:117
+msgid "Off"
+msgstr "关"
+
+#: searx/templates/legacy/result_templates/code.html:3
+#: searx/templates/legacy/result_templates/default.html:3
+#: searx/templates/legacy/result_templates/map.html:9
+#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
+msgid "cached"
+msgstr "缓存"
+
+#: searx/templates/oscar/advanced.html:4
+msgid "Advanced settings"
+msgstr "高级设置"
+
+#: searx/templates/oscar/base.html:62
+#: searx/templates/oscar/messages/first_time.html:4
+#: searx/templates/oscar/messages/no_results.html:5
+#: searx/templates/oscar/messages/save_settings_successfull.html:5
+#: searx/templates/oscar/messages/unknow_error.html:5
+msgid "Close"
+msgstr "关闭"
+
+#: searx/templates/oscar/base.html:64
+msgid "Error!"
+msgstr ""
+
+#: searx/templates/oscar/base.html:90
+msgid "Powered by"
+msgstr "Powered by"
+
+#: searx/templates/oscar/base.html:90
+msgid "a privacy-respecting, hackable metasearch engine"
+msgstr "一个尊重隐私,可再开发的元搜索引擎"
+
+#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
+msgid "proxied"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:12
+#: searx/templates/oscar/preferences.html:21
+msgid "General"
+msgstr "常规"
+
+#: searx/templates/oscar/preferences.html:13
+#: searx/templates/oscar/preferences.html:133
+msgid "Engines"
+msgstr "搜索引擎"
+
+#: searx/templates/oscar/preferences.html:14
+#: searx/templates/oscar/preferences.html:204
+msgid "Plugins"
+msgstr "插件"
+
+#: searx/templates/oscar/preferences.html:15
+#: searx/templates/oscar/preferences.html:230
+msgid "Answerers"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:16
+#: searx/templates/oscar/preferences.html:257
+msgid "Cookies"
+msgstr "Cookie"
+
+#: searx/templates/oscar/preferences.html:41
+msgid "What language do you prefer for search?"
+msgstr "你更喜欢搜索什么语言?"
+
+#: searx/templates/oscar/preferences.html:47
+msgid "Change the language of the layout"
+msgstr "改变界面语言"
+
+#: searx/templates/oscar/preferences.html:57
+msgid "Find stuff as you type"
+msgstr "自动补全搜索字词"
+
+#: searx/templates/oscar/preferences.html:68
+msgid "Proxying image results through searx"
+msgstr "通过searx代理图片"
+
+#: searx/templates/oscar/preferences.html:77
+msgid ""
+"Change how forms are submited, <a "
+"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
+" rel=\"external\">learn more about request methods</a>"
+msgstr "更改请求方法,<a href=\"http://zh.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\" rel=\"external\">深入了解请求方法</a>"
+
+#: searx/templates/oscar/preferences.html:86
+msgid "Filter content"
+msgstr "过滤内容"
+
+#: searx/templates/oscar/preferences.html:96
+msgid "Change searx layout"
+msgstr "改变searx布局"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Choose style for this theme"
+msgstr "选择这个主题的样式"
+
+#: searx/templates/oscar/preferences.html:105
+#: searx/templates/oscar/preferences.html:110
+msgid "Style"
+msgstr "样式"
+
+#: searx/templates/oscar/preferences.html:150
+#: searx/templates/oscar/preferences.html:161
+msgid "Shortcut"
+msgstr "快捷键"
+
+#: searx/templates/oscar/preferences.html:151
+#: searx/templates/oscar/preferences.html:160
+msgid "Supports selected language"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:153
+msgid "Time range"
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:154
+#: searx/templates/oscar/preferences.html:158
+msgid "Avg. time"
+msgstr "平均时间"
+
+#: searx/templates/oscar/preferences.html:155
+#: searx/templates/oscar/preferences.html:157
+msgid "Max time"
+msgstr "最大时间"
+
+#: searx/templates/oscar/preferences.html:233
+msgid "This is the list of searx's instant answering modules."
+msgstr ""
+
+#: searx/templates/oscar/preferences.html:237
+msgid "Name"
+msgstr "名称"
+
+#: searx/templates/oscar/preferences.html:238
+msgid "Keywords"
+msgstr "关键词"
+
+#: searx/templates/oscar/preferences.html:239
+msgid "Description"
+msgstr "描述"
+
+#: searx/templates/oscar/preferences.html:240
+msgid "Examples"
+msgstr "例子"
+
+#: searx/templates/oscar/preferences.html:260
+msgid ""
+"This is the list of cookies and their values searx is storing on your "
+"computer."
+msgstr "这里展示了searx保存在你的电脑上的cookie。"
+
+#: searx/templates/oscar/preferences.html:261
+msgid "With that list, you can assess searx transparency."
+msgstr "有了这个列表,你可以评估searx透明度。"
+
+#: searx/templates/oscar/preferences.html:266
+msgid "Cookie name"
+msgstr "cookie名称"
+
+#: searx/templates/oscar/preferences.html:267
+msgid "Value"
+msgstr "值"
+
+#: searx/templates/oscar/results.html:7
+msgid "Search results"
+msgstr "搜索结果"
+
+#: searx/templates/oscar/results.html:119
+msgid "Links"
+msgstr "链接"
+
+#: searx/templates/oscar/search.html:6
+#: searx/templates/oscar/search_full.html:11
+msgid "Start search"
+msgstr "开始搜索"
+
+#: searx/templates/oscar/stats.html:2
+msgid "stats"
+msgstr "统计"
+
+#: searx/templates/oscar/time-range.html:3
+msgid "Anytime"
+msgstr "时间不限"
+
+#: searx/templates/oscar/time-range.html:6
+msgid "Last day"
+msgstr "过去一天内"
+
+#: searx/templates/oscar/time-range.html:9
+msgid "Last week"
+msgstr "过去一周内"
+
+#: searx/templates/oscar/time-range.html:12
+msgid "Last month"
+msgstr "过去一个月内"
+
+#: searx/templates/oscar/time-range.html:15
+msgid "Last year"
+msgstr ""
+
+#: searx/templates/oscar/messages/first_time.html:6
+#: searx/templates/oscar/messages/no_data_available.html:3
+msgid "Heads up!"
+msgstr "小心!"
+
+#: searx/templates/oscar/messages/first_time.html:7
+msgid "It look like you are using searx first time."
+msgstr "看起来你是第一次使用searx。"
+
+#: searx/templates/oscar/messages/no_cookies.html:3
+msgid "Information!"
+msgstr "信息!"
+
+#: searx/templates/oscar/messages/no_cookies.html:4
+msgid "currently, there are no cookies defined."
+msgstr "目前还未保存cookie。"
+
+#: searx/templates/oscar/messages/no_data_available.html:4
+msgid "There is currently no data available. "
+msgstr "目前没有数据可用。"
+
+#: searx/templates/oscar/messages/no_results.html:7
+msgid "Sorry!"
+msgstr "抱歉!"
+
+#: searx/templates/oscar/messages/no_results.html:8
+msgid ""
+"we didn't find any results. Please use another query or search in more "
+"categories."
+msgstr "我们没有找到任何结果。请换用其他词语或在更多类别中搜索。"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:7
+msgid "Well done!"
+msgstr "做得好!"
+
+#: searx/templates/oscar/messages/save_settings_successfull.html:8
+msgid "Settings saved successfully."
+msgstr "设置保存成功。"
+
+#: searx/templates/oscar/messages/unknow_error.html:7
+msgid "Oh snap!"
+msgstr "哦,糟糕!"
+
+#: searx/templates/oscar/messages/unknow_error.html:8
+msgid "Something went wrong."
+msgstr "出了些问题。"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "show media"
+msgstr "显示媒体"
+
+#: searx/templates/oscar/result_templates/default.html:7
+msgid "hide media"
+msgstr "隐藏媒体"
+
+#: searx/templates/oscar/result_templates/images.html:30
+msgid "Get image"
+msgstr "获取图片"
+
+#: searx/templates/oscar/result_templates/images.html:33
+msgid "View source"
+msgstr "查看来源"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "show map"
+msgstr "显示地图"
+
+#: searx/templates/oscar/result_templates/map.html:7
+msgid "hide map"
+msgstr "隐藏地图"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "show details"
+msgstr "显示详细信息"
+
+#: searx/templates/oscar/result_templates/map.html:11
+msgid "hide details"
+msgstr "隐藏详细信息"
+
+#: searx/templates/oscar/result_templates/torrent.html:7
+msgid "Filesize"
+msgstr "文件大小"
+
+#: searx/templates/oscar/result_templates/torrent.html:9
+msgid "Bytes"
+msgstr "B"
+
+#: searx/templates/oscar/result_templates/torrent.html:10
+msgid "kiB"
+msgstr "KB"
+
+#: searx/templates/oscar/result_templates/torrent.html:11
+msgid "MiB"
+msgstr "MB"
+
+#: searx/templates/oscar/result_templates/torrent.html:12
+msgid "GiB"
+msgstr "GB"
+
+#: searx/templates/oscar/result_templates/torrent.html:13
+msgid "TiB"
+msgstr "TB"
+
+#: searx/templates/oscar/result_templates/torrent.html:15
+msgid "Number of Files"
+msgstr "文件数"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "show video"
+msgstr "显示视频"
+
+#: searx/templates/oscar/result_templates/videos.html:7
+msgid "hide video"
+msgstr "隐藏视频"
+
+#: searx/templates/pix-art/results.html:28
+msgid "Load more..."
+msgstr "载入更多..."
diff --git a/searx/url_utils.py b/searx/url_utils.py
new file mode 100644
index 0000000..e9919ab
--- /dev/null
+++ b/searx/url_utils.py
@@ -0,0 +1,28 @@
+from sys import version_info
+
+if version_info[0] == 2:
+ from urllib import quote, quote_plus, unquote, urlencode
+ from urlparse import parse_qsl, urljoin, urlparse, urlunparse, ParseResult
+else:
+ from urllib.parse import (
+ parse_qsl,
+ quote,
+ quote_plus,
+ unquote,
+ urlencode,
+ urljoin,
+ urlparse,
+ urlunparse,
+ ParseResult
+ )
+
+
+__export__ = (parse_qsl,
+ quote,
+ quote_plus,
+ unquote,
+ urlencode,
+ urljoin,
+ urlparse,
+ urlunparse,
+ ParseResult)
diff --git a/searx/utils.py b/searx/utils.py
new file mode 100644
index 0000000..498f8d0
--- /dev/null
+++ b/searx/utils.py
@@ -0,0 +1,314 @@
+import csv
+import os
+import re
+
+from babel.dates import format_date
+from codecs import getincrementalencoder
+from imp import load_source
+from os.path import splitext, join
+from random import choice
+import sys
+
+from searx.version import VERSION_STRING
+from searx.languages import language_codes
+from searx import settings
+from searx import logger
+
+try:
+ from cStringIO import StringIO
+except:
+ from io import StringIO
+
+try:
+ from HTMLParser import HTMLParser
+except:
+ from html.parser import HTMLParser
+
+if sys.version_info[0] == 3:
+ unichr = chr
+ unicode = str
+
+logger = logger.getChild('utils')
+
+ua_versions = ('40.0',
+ '41.0',
+ '42.0',
+ '43.0',
+ '44.0',
+ '45.0',
+ '46.0',
+ '47.0')
+
+ua_os = ('Windows NT 6.3; WOW64',
+ 'X11; Linux x86_64',
+ 'X11; Linux x86')
+
+ua = "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}"
+
+blocked_tags = ('script',
+ 'style')
+
+
+def gen_useragent():
+ # TODO
+ return ua.format(os=choice(ua_os), version=choice(ua_versions))
+
+
+def searx_useragent():
+ return 'searx/{searx_version} {suffix}'.format(
+ searx_version=VERSION_STRING,
+ suffix=settings['outgoing'].get('useragent_suffix', ''))
+
+
+def highlight_content(content, query):
+
+ if not content:
+ return None
+ # ignoring html contents
+ # TODO better html content detection
+ if content.find('<') != -1:
+ return content
+
+ query = query.decode('utf-8')
+ if content.lower().find(query.lower()) > -1:
+ query_regex = u'({0})'.format(re.escape(query))
+ content = re.sub(query_regex, '<span class="highlight">\\1</span>',
+ content, flags=re.I | re.U)
+ else:
+ regex_parts = []
+ for chunk in query.split():
+ if len(chunk) == 1:
+ regex_parts.append(u'\\W+{0}\\W+'.format(re.escape(chunk)))
+ else:
+ regex_parts.append(u'{0}'.format(re.escape(chunk)))
+ query_regex = u'({0})'.format('|'.join(regex_parts))
+ content = re.sub(query_regex, '<span class="highlight">\\1</span>',
+ content, flags=re.I | re.U)
+
+ return content
+
+
+class HTMLTextExtractor(HTMLParser):
+
+ def __init__(self):
+ HTMLParser.__init__(self)
+ self.result = []
+ self.tags = []
+
+ def handle_starttag(self, tag, attrs):
+ self.tags.append(tag)
+
+ def handle_endtag(self, tag):
+ if not self.tags:
+ return
+
+ if tag != self.tags[-1]:
+ raise Exception("invalid html")
+
+ self.tags.pop()
+
+ def is_valid_tag(self):
+ return not self.tags or self.tags[-1] not in blocked_tags
+
+ def handle_data(self, d):
+ if not self.is_valid_tag():
+ return
+ self.result.append(d)
+
+ def handle_charref(self, number):
+ if not self.is_valid_tag():
+ return
+ if number[0] in (u'x', u'X'):
+ codepoint = int(number[1:], 16)
+ else:
+ codepoint = int(number)
+ self.result.append(unichr(codepoint))
+
+ def handle_entityref(self, name):
+ if not self.is_valid_tag():
+ return
+ # codepoint = htmlentitydefs.name2codepoint[name]
+ # self.result.append(unichr(codepoint))
+ self.result.append(name)
+
+ def get_text(self):
+ return u''.join(self.result).strip()
+
+
+def html_to_text(html):
+ html = html.replace('\n', ' ')
+ html = ' '.join(html.split())
+ s = HTMLTextExtractor()
+ s.feed(html)
+ return s.get_text()
+
+
+class UnicodeWriter:
+ """
+ A CSV writer which will write rows to CSV file "f",
+ which is encoded in the given encoding.
+ """
+
+ def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
+ # Redirect output to a queue
+ self.queue = StringIO()
+ self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
+ self.stream = f
+ self.encoder = getincrementalencoder(encoding)()
+
+ def writerow(self, row):
+ unicode_row = []
+ for col in row:
+ if type(col) == str or type(col) == unicode:
+ unicode_row.append(col.encode('utf-8').strip())
+ else:
+ unicode_row.append(col)
+ self.writer.writerow([x.decode('utf-8') if hasattr(x, 'decode') else x for x in unicode_row])
+ # Fetch UTF-8 output from the queue ...
+ data = self.queue.getvalue().strip('\x00')
+ # ... and reencode it into the target encoding
+ data = self.encoder.encode(data)
+ # write to the target stream
+ self.stream.write(data.decode('utf-8'))
+ # empty queue
+ self.queue.truncate(0)
+
+ def writerows(self, rows):
+ for row in rows:
+ self.writerow(row)
+
+
+def get_resources_directory(searx_directory, subdirectory, resources_directory):
+ if not resources_directory:
+ resources_directory = os.path.join(searx_directory, subdirectory)
+ if not os.path.isdir(resources_directory):
+ raise Exception(directory + " is not a directory")
+ return resources_directory
+
+
+def get_themes(templates_path):
+ """Returns available themes list."""
+ themes = os.listdir(templates_path)
+ if '__common__' in themes:
+ themes.remove('__common__')
+ return themes
+
+
+def get_static_files(static_path):
+ static_files = set()
+ static_path_length = len(static_path) + 1
+ for directory, _, files in os.walk(static_path):
+ for filename in files:
+ f = os.path.join(directory[static_path_length:], filename)
+ static_files.add(f)
+ return static_files
+
+
+def get_result_templates(templates_path):
+ result_templates = set()
+ templates_path_length = len(templates_path) + 1
+ for directory, _, files in os.walk(templates_path):
+ if directory.endswith('result_templates'):
+ for filename in files:
+ f = os.path.join(directory[templates_path_length:], filename)
+ result_templates.add(f)
+ return result_templates
+
+
+def format_date_by_locale(date, locale_string):
+ # strftime works only on dates after 1900
+
+ if date.year <= 1900:
+ return date.isoformat().split('T')[0]
+
+ if locale_string == 'all':
+ locale_string = settings['ui']['default_locale'] or 'en_US'
+
+ # to avoid crashing if locale is not supported by babel
+ try:
+ formatted_date = format_date(date, locale=locale_string)
+ except:
+ formatted_date = format_date(date, "YYYY-MM-dd")
+
+ return formatted_date
+
+
+def dict_subset(d, properties):
+ result = {}
+ for k in properties:
+ if k in d:
+ result[k] = d[k]
+ return result
+
+
+def prettify_url(url, max_length=74):
+ if len(url) > max_length:
+ chunk_len = int(max_length / 2 + 1)
+ return u'{0}[...]{1}'.format(url[:chunk_len], url[-chunk_len:])
+ else:
+ return url
+
+
+# get element in list or default value
+def list_get(a_list, index, default=None):
+ if len(a_list) > index:
+ return a_list[index]
+ else:
+ return default
+
+
+def get_torrent_size(filesize, filesize_multiplier):
+ try:
+ filesize = float(filesize)
+
+ if filesize_multiplier == 'TB':
+ filesize = int(filesize * 1024 * 1024 * 1024 * 1024)
+ elif filesize_multiplier == 'GB':
+ filesize = int(filesize * 1024 * 1024 * 1024)
+ elif filesize_multiplier == 'MB':
+ filesize = int(filesize * 1024 * 1024)
+ elif filesize_multiplier == 'KB':
+ filesize = int(filesize * 1024)
+ elif filesize_multiplier == 'TiB':
+ filesize = int(filesize * 1000 * 1000 * 1000 * 1000)
+ elif filesize_multiplier == 'GiB':
+ filesize = int(filesize * 1000 * 1000 * 1000)
+ elif filesize_multiplier == 'MiB':
+ filesize = int(filesize * 1000 * 1000)
+ elif filesize_multiplier == 'KiB':
+ filesize = int(filesize * 1000)
+ except:
+ filesize = None
+
+ return filesize
+
+
+def convert_str_to_int(number_str):
+ if number_str.isdigit():
+ return int(number_str)
+ else:
+ return 0
+
+
+def is_valid_lang(lang):
+ is_abbr = (len(lang) == 2)
+ if is_abbr:
+ for l in language_codes:
+ if l[0][:2] == lang.lower():
+ return (True, l[0][:2], l[1].lower())
+ return False
+ else:
+ for l in language_codes:
+ if l[1].lower() == lang.lower():
+ return (True, l[0][:2], l[1].lower())
+ return False
+
+
+def load_module(filename, module_dir):
+ modname = splitext(filename)[0]
+ if modname in sys.modules:
+ del sys.modules[modname]
+ filepath = join(module_dir, filename)
+ module = load_source(modname, filepath)
+ module.name = modname
+ return module
diff --git a/searx/version.py b/searx/version.py
new file mode 100644
index 0000000..198f9d2
--- /dev/null
+++ b/searx/version.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
+'''
+
+# version of searx
+VERSION_MAJOR = 0
+VERSION_MINOR = 12
+VERSION_BUILD = 0
+
+VERSION_STRING = "{0}.{1}.{2}".format(VERSION_MAJOR,
+ VERSION_MINOR,
+ VERSION_BUILD)
diff --git a/searx/webapp.py b/searx/webapp.py
new file mode 100644
index 0000000..fcea5f2
--- /dev/null
+++ b/searx/webapp.py
@@ -0,0 +1,886 @@
+#!/usr/bin/env python
+
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
+'''
+
+if __name__ == '__main__':
+ from sys import path
+ from os.path import realpath, dirname
+ path.append(realpath(dirname(realpath(__file__)) + '/../'))
+
+import hashlib
+import hmac
+import json
+import os
+import sys
+
+import requests
+
+from searx import logger
+logger = logger.getChild('webapp')
+
+try:
+ from pygments import highlight
+ from pygments.lexers import get_lexer_by_name
+ from pygments.formatters import HtmlFormatter
+except:
+ logger.critical("cannot import dependency: pygments")
+ from sys import exit
+ exit(1)
+from cgi import escape
+from datetime import datetime, timedelta
+from werkzeug.contrib.fixers import ProxyFix
+from flask import (
+ Flask, request, render_template, url_for, Response, make_response,
+ redirect, send_from_directory
+)
+from flask_babel import Babel, gettext, format_date, format_decimal
+from flask.json import jsonify
+from searx import settings, searx_dir, searx_debug
+from searx.exceptions import SearxParameterException
+from searx.engines import (
+ categories, engines, engine_shortcuts, get_engines_stats, initialize_engines
+)
+from searx.utils import (
+ UnicodeWriter, highlight_content, html_to_text, get_resources_directory,
+ get_static_files, get_result_templates, get_themes, gen_useragent,
+ dict_subset, prettify_url
+)
+from searx.version import VERSION_STRING
+from searx.languages import language_codes
+from searx.search import SearchWithPlugins, get_search_query_from_webapp
+from searx.query import RawTextQuery
+from searx.autocomplete import searx_bang, backends as autocomplete_backends
+from searx.plugins import plugins
+from searx.preferences import Preferences, ValidationException
+from searx.answerers import answerers
+from searx.url_utils import urlencode, urlparse, urljoin
+
+# check if the pyopenssl package is installed.
+# It is needed for SSL connection without trouble, see #298
+try:
+ import OpenSSL.SSL # NOQA
+except ImportError:
+ logger.critical("The pyopenssl package has to be installed.\n"
+ "Some HTTPS connections will fail")
+
+try:
+ from cStringIO import StringIO
+except:
+ from io import StringIO
+
+
+if sys.version_info[0] == 3:
+ unicode = str
+
+# serve pages with HTTP/1.1
+from werkzeug.serving import WSGIRequestHandler
+WSGIRequestHandler.protocol_version = "HTTP/{}".format(settings['server'].get('http_protocol_version', '1.0'))
+
+# about static
+static_path = get_resources_directory(searx_dir, 'static', settings['ui']['static_path'])
+logger.debug('static directory is %s', static_path)
+static_files = get_static_files(static_path)
+
+# about templates
+default_theme = settings['ui']['default_theme']
+templates_path = get_resources_directory(searx_dir, 'templates', settings['ui']['templates_path'])
+logger.debug('templates directory is %s', templates_path)
+themes = get_themes(templates_path)
+result_templates = get_result_templates(templates_path)
+global_favicons = []
+for indice, theme in enumerate(themes):
+ global_favicons.append([])
+ theme_img_path = os.path.join(static_path, 'themes', theme, 'img', 'icons')
+ for (dirpath, dirnames, filenames) in os.walk(theme_img_path):
+ global_favicons[indice].extend(filenames)
+
+# Flask app
+app = Flask(
+ __name__,
+ static_folder=static_path,
+ template_folder=templates_path
+)
+
+app.jinja_env.trim_blocks = True
+app.jinja_env.lstrip_blocks = True
+app.secret_key = settings['server']['secret_key']
+
+if not searx_debug or os.environ.get("WERKZEUG_RUN_MAIN") == "true":
+ initialize_engines(settings['engines'])
+
+babel = Babel(app)
+
+rtl_locales = ['ar', 'arc', 'bcc', 'bqi', 'ckb', 'dv', 'fa', 'glk', 'he',
+ 'ku', 'mzn', 'pnb'', ''ps', 'sd', 'ug', 'ur', 'yi']
+
+# used when translating category names
+_category_names = (gettext('files'),
+ gettext('general'),
+ gettext('music'),
+ gettext('social media'),
+ gettext('images'),
+ gettext('videos'),
+ gettext('it'),
+ gettext('news'),
+ gettext('map'),
+ gettext('science'))
+
+outgoing_proxies = settings['outgoing'].get('proxies', None)
+
+
+@babel.localeselector
+def get_locale():
+ locale = request.accept_languages.best_match(settings['locales'].keys())
+
+ if request.preferences.get_value('locale') != '':
+ locale = request.preferences.get_value('locale')
+
+ if 'locale' in request.args\
+ and request.args['locale'] in settings['locales']:
+ locale = request.args['locale']
+
+ if 'locale' in request.form\
+ and request.form['locale'] in settings['locales']:
+ locale = request.form['locale']
+
+ return locale
+
+
+# code-highlighter
+@app.template_filter('code_highlighter')
+def code_highlighter(codelines, language=None):
+ if not language:
+ language = 'text'
+
+ try:
+ # find lexer by programing language
+ lexer = get_lexer_by_name(language, stripall=True)
+ except:
+ # if lexer is not found, using default one
+ logger.debug('highlighter cannot find lexer for {0}'.format(language))
+ lexer = get_lexer_by_name('text', stripall=True)
+
+ html_code = ''
+ tmp_code = ''
+ last_line = None
+
+ # parse lines
+ for line, code in codelines:
+ if not last_line:
+ line_code_start = line
+
+ # new codeblock is detected
+ if last_line is not None and\
+ last_line + 1 != line:
+
+ # highlight last codepart
+ formatter = HtmlFormatter(linenos='inline',
+ linenostart=line_code_start)
+ html_code = html_code + highlight(tmp_code, lexer, formatter)
+
+ # reset conditions for next codepart
+ tmp_code = ''
+ line_code_start = line
+
+ # add codepart
+ tmp_code += code + '\n'
+
+ # update line
+ last_line = line
+
+ # highlight last codepart
+ formatter = HtmlFormatter(linenos='inline', linenostart=line_code_start)
+ html_code = html_code + highlight(tmp_code, lexer, formatter)
+
+ return html_code
+
+
+# Extract domain from url
+@app.template_filter('extract_domain')
+def extract_domain(url):
+ return urlparse(url)[1]
+
+
+def get_base_url():
+ if settings['server']['base_url']:
+ hostname = settings['server']['base_url']
+ else:
+ scheme = 'http'
+ if request.is_secure:
+ scheme = 'https'
+ hostname = url_for('index', _external=True, _scheme=scheme)
+ return hostname
+
+
+def get_current_theme_name(override=None):
+ """Returns theme name.
+
+ Checks in this order:
+ 1. override
+ 2. cookies
+ 3. settings"""
+
+ if override and (override in themes or override == '__common__'):
+ return override
+ theme_name = request.args.get('theme', request.preferences.get_value('theme'))
+ if theme_name not in themes:
+ theme_name = default_theme
+ return theme_name
+
+
+def get_result_template(theme, template_name):
+ themed_path = theme + '/result_templates/' + template_name
+ if themed_path in result_templates:
+ return themed_path
+ return 'result_templates/' + template_name
+
+
+def url_for_theme(endpoint, override_theme=None, **values):
+ if endpoint == 'static' and values.get('filename'):
+ theme_name = get_current_theme_name(override=override_theme)
+ filename_with_theme = "themes/{}/{}".format(theme_name, values['filename'])
+ if filename_with_theme in static_files:
+ values['filename'] = filename_with_theme
+ return url_for(endpoint, **values)
+
+
+def proxify(url):
+ if url.startswith('//'):
+ url = 'https:' + url
+
+ if not settings.get('result_proxy'):
+ return url
+
+ url_params = dict(mortyurl=url.encode('utf-8'))
+
+ if settings['result_proxy'].get('key'):
+ url_params['mortyhash'] = hmac.new(settings['result_proxy']['key'],
+ url.encode('utf-8'),
+ hashlib.sha256).hexdigest()
+
+ return '{0}?{1}'.format(settings['result_proxy']['url'],
+ urlencode(url_params))
+
+
+def image_proxify(url):
+
+ if url.startswith('//'):
+ url = 'https:' + url
+
+ if not request.preferences.get_value('image_proxy'):
+ return url
+
+ if settings.get('result_proxy'):
+ return proxify(url)
+
+ h = hmac.new(settings['server']['secret_key'], url.encode('utf-8'), hashlib.sha256).hexdigest()
+
+ return '{0}?{1}'.format(url_for('image_proxy'),
+ urlencode(dict(url=url.encode('utf-8'), h=h)))
+
+
+def render(template_name, override_theme=None, **kwargs):
+ disabled_engines = request.preferences.engines.get_disabled()
+
+ enabled_categories = set(category for engine_name in engines
+ for category in engines[engine_name].categories
+ if (engine_name, category) not in disabled_engines)
+
+ if 'categories' not in kwargs:
+ kwargs['categories'] = ['general']
+ kwargs['categories'].extend(x for x in
+ sorted(categories.keys())
+ if x != 'general'
+ and x in enabled_categories)
+
+ if 'all_categories' not in kwargs:
+ kwargs['all_categories'] = ['general']
+ kwargs['all_categories'].extend(x for x in
+ sorted(categories.keys())
+ if x != 'general')
+
+ if 'selected_categories' not in kwargs:
+ kwargs['selected_categories'] = []
+ for arg in request.args:
+ if arg.startswith('category_'):
+ c = arg.split('_', 1)[1]
+ if c in categories:
+ kwargs['selected_categories'].append(c)
+
+ if not kwargs['selected_categories']:
+ cookie_categories = request.preferences.get_value('categories')
+ for ccateg in cookie_categories:
+ kwargs['selected_categories'].append(ccateg)
+
+ if not kwargs['selected_categories']:
+ kwargs['selected_categories'] = ['general']
+
+ if 'autocomplete' not in kwargs:
+ kwargs['autocomplete'] = request.preferences.get_value('autocomplete')
+
+ if get_locale() in rtl_locales and 'rtl' not in kwargs:
+ kwargs['rtl'] = True
+
+ kwargs['searx_version'] = VERSION_STRING
+
+ kwargs['method'] = request.preferences.get_value('method')
+
+ kwargs['safesearch'] = str(request.preferences.get_value('safesearch'))
+
+ kwargs['language_codes'] = language_codes
+ if 'current_language' not in kwargs:
+ kwargs['current_language'] = request.preferences.get_value('language')
+
+ # override url_for function in templates
+ kwargs['url_for'] = url_for_theme
+
+ kwargs['image_proxify'] = image_proxify
+
+ kwargs['proxify'] = proxify if settings.get('result_proxy') else None
+
+ kwargs['get_result_template'] = get_result_template
+
+ kwargs['theme'] = get_current_theme_name(override=override_theme)
+
+ kwargs['template_name'] = template_name
+
+ kwargs['cookies'] = request.cookies
+
+ kwargs['errors'] = request.errors
+
+ kwargs['instance_name'] = settings['general']['instance_name']
+
+ kwargs['results_on_new_tab'] = request.preferences.get_value('results_on_new_tab')
+
+ kwargs['unicode'] = unicode
+
+ kwargs['scripts'] = set()
+ for plugin in request.user_plugins:
+ for script in plugin.js_dependencies:
+ kwargs['scripts'].add(script)
+
+ kwargs['styles'] = set()
+ for plugin in request.user_plugins:
+ for css in plugin.css_dependencies:
+ kwargs['styles'].add(css)
+
+ return render_template(
+ '{}/{}'.format(kwargs['theme'], template_name), **kwargs)
+
+
+@app.before_request
+def pre_request():
+ request.errors = []
+
+ preferences = Preferences(themes, list(categories.keys()), engines, plugins)
+ request.preferences = preferences
+ try:
+ preferences.parse_cookies(request.cookies)
+ except:
+ request.errors.append(gettext('Invalid settings, please edit your preferences'))
+
+ # merge GET, POST vars
+ # request.form
+ request.form = dict(request.form.items())
+ for k, v in request.args.items():
+ if k not in request.form:
+ request.form[k] = v
+
+ # request.user_plugins
+ request.user_plugins = []
+ allowed_plugins = preferences.plugins.get_enabled()
+ disabled_plugins = preferences.plugins.get_disabled()
+ for plugin in plugins:
+ if ((plugin.default_on and plugin.id not in disabled_plugins)
+ or plugin.id in allowed_plugins):
+ request.user_plugins.append(plugin)
+
+
+def index_error(output_format, error_message):
+ if output_format == 'json':
+ return Response(json.dumps({'error': error_message}),
+ mimetype='application/json')
+ elif output_format == 'csv':
+ response = Response('', mimetype='application/csv')
+ cont_disp = 'attachment;Filename=searx.csv'
+ response.headers.add('Content-Disposition', cont_disp)
+ return response
+ elif output_format == 'rss':
+ response_rss = render(
+ 'opensearch_response_rss.xml',
+ results=[],
+ q=request.form['q'] if 'q' in request.form else '',
+ number_of_results=0,
+ base_url=get_base_url(),
+ error_message=error_message,
+ override_theme='__common__',
+ )
+ return Response(response_rss, mimetype='text/xml')
+ else:
+ # html
+ request.errors.append(gettext('search error'))
+ return render(
+ 'index.html',
+ )
+
+
+@app.route('/search', methods=['GET', 'POST'])
+@app.route('/', methods=['GET', 'POST'])
+def index():
+ """Render index page.
+
+ Supported outputs: html, json, csv, rss.
+ """
+
+ # output_format
+ output_format = request.form.get('format', 'html')
+ if output_format not in ['html', 'csv', 'json', 'rss']:
+ output_format = 'html'
+
+ # check if there is query
+ if request.form.get('q') is None:
+ if output_format == 'html':
+ return render(
+ 'index.html',
+ )
+ else:
+ return index_error(output_format, 'No query'), 400
+
+ # search
+ search_query = None
+ result_container = None
+ try:
+ search_query = get_search_query_from_webapp(request.preferences, request.form)
+ # search = Search(search_query) # without plugins
+ search = SearchWithPlugins(search_query, request.user_plugins, request)
+ result_container = search.search()
+ except Exception as e:
+ # log exception
+ logger.exception('search error')
+
+ # is it an invalid input parameter or something else ?
+ if (issubclass(e.__class__, SearxParameterException)):
+ return index_error(output_format, e.message), 400
+ else:
+ return index_error(output_format, gettext('search error')), 500
+
+ # results
+ results = result_container.get_ordered_results()
+ number_of_results = result_container.results_number()
+ if number_of_results < result_container.results_length():
+ number_of_results = 0
+
+ # UI
+ advanced_search = request.form.get('advanced_search', None)
+
+ # output
+ for result in results:
+ if output_format == 'html':
+ if 'content' in result and result['content']:
+ result['content'] = highlight_content(escape(result['content'][:1024]), search_query.query)
+ result['title'] = highlight_content(escape(result['title'] or u''), search_query.query)
+ else:
+ if result.get('content'):
+ result['content'] = html_to_text(result['content']).strip()
+ # removing html content and whitespace duplications
+ result['title'] = ' '.join(html_to_text(result['title']).strip().split())
+
+ result['pretty_url'] = prettify_url(result['url'])
+
+ # TODO, check if timezone is calculated right
+ if 'publishedDate' in result:
+ try: # test if publishedDate >= 1900 (datetime module bug)
+ result['pubdate'] = result['publishedDate'].strftime('%Y-%m-%d %H:%M:%S%z')
+ except ValueError:
+ result['publishedDate'] = None
+ else:
+ if result['publishedDate'].replace(tzinfo=None) >= datetime.now() - timedelta(days=1):
+ timedifference = datetime.now() - result['publishedDate'].replace(tzinfo=None)
+ minutes = int((timedifference.seconds / 60) % 60)
+ hours = int(timedifference.seconds / 60 / 60)
+ if hours == 0:
+ result['publishedDate'] = gettext(u'{minutes} minute(s) ago').format(minutes=minutes)
+ else:
+ result['publishedDate'] = gettext(u'{hours} hour(s), {minutes} minute(s) ago').format(hours=hours, minutes=minutes) # noqa
+ else:
+ result['publishedDate'] = format_date(result['publishedDate'])
+
+ if output_format == 'json':
+ return Response(json.dumps({'query': search_query.query.decode('utf-8'),
+ 'number_of_results': number_of_results,
+ 'results': results,
+ 'answers': list(result_container.answers),
+ 'corrections': list(result_container.corrections),
+ 'infoboxes': result_container.infoboxes,
+ 'suggestions': list(result_container.suggestions)}),
+ mimetype='application/json')
+ elif output_format == 'csv':
+ csv = UnicodeWriter(StringIO())
+ keys = ('title', 'url', 'content', 'host', 'engine', 'score')
+ csv.writerow(keys)
+ for row in results:
+ row['host'] = row['parsed_url'].netloc
+ csv.writerow([row.get(key, '') for key in keys])
+ csv.stream.seek(0)
+ response = Response(csv.stream.read(), mimetype='application/csv')
+ cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search_query.query)
+ response.headers.add('Content-Disposition', cont_disp)
+ return response
+ elif output_format == 'rss':
+ response_rss = render(
+ 'opensearch_response_rss.xml',
+ results=results,
+ q=request.form['q'],
+ number_of_results=number_of_results,
+ base_url=get_base_url(),
+ override_theme='__common__',
+ )
+ return Response(response_rss, mimetype='text/xml')
+
+ return render(
+ 'results.html',
+ results=results,
+ q=request.form['q'],
+ selected_categories=search_query.categories,
+ pageno=search_query.pageno,
+ time_range=search_query.time_range,
+ number_of_results=format_decimal(number_of_results),
+ advanced_search=advanced_search,
+ suggestions=result_container.suggestions,
+ answers=result_container.answers,
+ corrections=result_container.corrections,
+ infoboxes=result_container.infoboxes,
+ paging=result_container.paging,
+ current_language=search_query.lang,
+ base_url=get_base_url(),
+ theme=get_current_theme_name(),
+ favicons=global_favicons[themes.index(get_current_theme_name())]
+ )
+
+
+@app.route('/about', methods=['GET'])
+def about():
+ """Render about page"""
+ return render(
+ 'about.html',
+ )
+
+
+@app.route('/autocompleter', methods=['GET', 'POST'])
+def autocompleter():
+ """Return autocompleter results"""
+
+ # set blocked engines
+ disabled_engines = request.preferences.engines.get_disabled()
+
+ # parse query
+ raw_text_query = RawTextQuery(request.form.get('q', u'').encode('utf-8'), disabled_engines)
+ raw_text_query.parse_query()
+
+ # check if search query is set
+ if not raw_text_query.getSearchQuery():
+ return '', 400
+
+ # run autocompleter
+ completer = autocomplete_backends.get(request.preferences.get_value('autocomplete'))
+
+ # parse searx specific autocompleter results like !bang
+ raw_results = searx_bang(raw_text_query)
+
+ # normal autocompletion results only appear if max 3 inner results returned
+ if len(raw_results) <= 3 and completer:
+ # get language from cookie
+ language = request.preferences.get_value('language')
+ if not language or language == 'all':
+ language = 'en'
+ else:
+ language = language.split('-')[0]
+ # run autocompletion
+ raw_results.extend(completer(raw_text_query.getSearchQuery(), language))
+
+ # parse results (write :language and !engine back to result string)
+ results = []
+ for result in raw_results:
+ raw_text_query.changeSearchQuery(result)
+
+ # add parsed result
+ results.append(raw_text_query.getFullQuery())
+
+ # return autocompleter results
+ if request.form.get('format') == 'x-suggestions':
+ return Response(json.dumps([raw_text_query.query, results]),
+ mimetype='application/json')
+
+ return Response(json.dumps(results),
+ mimetype='application/json')
+
+
+@app.route('/preferences', methods=['GET', 'POST'])
+def preferences():
+ """Render preferences page && save user preferences"""
+
+ # save preferences
+ if request.method == 'POST':
+ resp = make_response(redirect(urljoin(settings['server']['base_url'], url_for('index'))))
+ try:
+ request.preferences.parse_form(request.form)
+ except ValidationException:
+ request.errors.append(gettext('Invalid settings, please edit your preferences'))
+ return resp
+ return request.preferences.save(resp)
+
+ # render preferences
+ image_proxy = request.preferences.get_value('image_proxy')
+ lang = request.preferences.get_value('language')
+ disabled_engines = request.preferences.engines.get_disabled()
+ allowed_plugins = request.preferences.plugins.get_enabled()
+
+ # stats for preferences page
+ stats = {}
+
+ for c in categories:
+ for e in categories[c]:
+ stats[e.name] = {'time': None,
+ 'warn_timeout': False,
+ 'warn_time': False}
+ if e.timeout > settings['outgoing']['request_timeout']:
+ stats[e.name]['warn_timeout'] = True
+
+ # get first element [0], the engine time,
+ # and then the second element [1] : the time (the first one is the label)
+ for engine_stat in get_engines_stats()[0][1]:
+ stats[engine_stat.get('name')]['time'] = round(engine_stat.get('avg'), 3)
+ if engine_stat.get('avg') > settings['outgoing']['request_timeout']:
+ stats[engine_stat.get('name')]['warn_time'] = True
+ # end of stats
+
+ return render('preferences.html',
+ locales=settings['locales'],
+ current_locale=get_locale(),
+ image_proxy=image_proxy,
+ engines_by_category=categories,
+ stats=stats,
+ answerers=[{'info': a.self_info(), 'keywords': a.keywords} for a in answerers],
+ disabled_engines=disabled_engines,
+ autocomplete_backends=autocomplete_backends,
+ shortcuts={y: x for x, y in engine_shortcuts.items()},
+ themes=themes,
+ plugins=plugins,
+ allowed_plugins=allowed_plugins,
+ theme=get_current_theme_name(),
+ preferences=True)
+
+
+@app.route('/image_proxy', methods=['GET'])
+def image_proxy():
+ url = request.args.get('url').encode('utf-8')
+
+ if not url:
+ return '', 400
+
+ h = hmac.new(settings['server']['secret_key'], url, hashlib.sha256).hexdigest()
+
+ if h != request.args.get('h'):
+ return '', 400
+
+ headers = dict_subset(request.headers, {'If-Modified-Since', 'If-None-Match'})
+ headers['User-Agent'] = gen_useragent()
+
+ resp = requests.get(url,
+ stream=True,
+ timeout=settings['outgoing']['request_timeout'],
+ headers=headers,
+ proxies=outgoing_proxies)
+
+ if resp.status_code == 304:
+ return '', resp.status_code
+
+ if resp.status_code != 200:
+ logger.debug('image-proxy: wrong response code: {0}'.format(resp.status_code))
+ if resp.status_code >= 400:
+ return '', resp.status_code
+ return '', 400
+
+ if not resp.headers.get('content-type', '').startswith('image/'):
+ logger.debug('image-proxy: wrong content-type: {0}'.format(resp.headers.get('content-type')))
+ return '', 400
+
+ img = ''
+ chunk_counter = 0
+
+ for chunk in resp.iter_content(1024 * 1024):
+ chunk_counter += 1
+ if chunk_counter > 5:
+ return '', 502 # Bad gateway - file is too big (>5M)
+ img += chunk
+
+ headers = dict_subset(resp.headers, {'Content-Length', 'Length', 'Date', 'Last-Modified', 'Expires', 'Etag'})
+
+ return Response(img, mimetype=resp.headers['content-type'], headers=headers)
+
+
+@app.route('/stats', methods=['GET'])
+def stats():
+ """Render engine statistics page."""
+ stats = get_engines_stats()
+ return render(
+ 'stats.html',
+ stats=stats,
+ )
+
+
+@app.route('/robots.txt', methods=['GET'])
+def robots():
+ return Response("""User-agent: *
+Allow: /
+Allow: /about
+Disallow: /stats
+Disallow: /preferences
+Disallow: /*?*q=*
+""", mimetype='text/plain')
+
+
+@app.route('/opensearch.xml', methods=['GET'])
+def opensearch():
+ method = 'post'
+
+ if request.preferences.get_value('method') == 'GET':
+ method = 'get'
+
+ # chrome/chromium only supports HTTP GET....
+ if request.headers.get('User-Agent', '').lower().find('webkit') >= 0:
+ method = 'get'
+
+ ret = render('opensearch.xml',
+ opensearch_method=method,
+ host=get_base_url(),
+ urljoin=urljoin,
+ override_theme='__common__')
+
+ resp = Response(response=ret,
+ status=200,
+ mimetype="text/xml")
+ return resp
+
+
+@app.route('/favicon.ico')
+def favicon():
+ return send_from_directory(os.path.join(app.root_path,
+ 'static/themes',
+ get_current_theme_name(),
+ 'img'),
+ 'favicon.png',
+ mimetype='image/vnd.microsoft.icon')
+
+
+@app.route('/clear_cookies')
+def clear_cookies():
+ resp = make_response(redirect(urljoin(settings['server']['base_url'], url_for('index'))))
+ for cookie_name in request.cookies:
+ resp.delete_cookie(cookie_name)
+ return resp
+
+
+@app.route('/config')
+def config():
+ return jsonify({'categories': categories.keys(),
+ 'engines': [{'name': engine_name,
+ 'categories': engine.categories,
+ 'shortcut': engine.shortcut,
+ 'enabled': not engine.disabled,
+ 'paging': engine.paging,
+ 'language_support': engine.language_support,
+ 'supported_languages':
+ engine.supported_languages.keys()
+ if isinstance(engine.supported_languages, dict)
+ else engine.supported_languages,
+ 'safesearch': engine.safesearch,
+ 'time_range_support': engine.time_range_support,
+ 'timeout': engine.timeout}
+ for engine_name, engine in engines.items()],
+ 'plugins': [{'name': plugin.name,
+ 'enabled': plugin.default_on}
+ for plugin in plugins],
+ 'instance_name': settings['general']['instance_name'],
+ 'locales': settings['locales'],
+ 'default_locale': settings['ui']['default_locale'],
+ 'autocomplete': settings['search']['autocomplete'],
+ 'safe_search': settings['search']['safe_search'],
+ 'default_theme': settings['ui']['default_theme'],
+ 'version': VERSION_STRING})
+
+
+@app.errorhandler(404)
+def page_not_found(e):
+ return render('404.html'), 404
+
+
+def run():
+ logger.debug('starting webserver on %s:%s', settings['server']['port'], settings['server']['bind_address'])
+ app.run(
+ debug=searx_debug,
+ use_debugger=searx_debug,
+ port=settings['server']['port'],
+ host=settings['server']['bind_address'],
+ threaded=True
+ )
+
+
+class ReverseProxyPathFix(object):
+ '''Wrap the application in this middleware and configure the
+ front-end server to add these headers, to let you quietly bind
+ this to a URL other than / and to an HTTP scheme that is
+ different than what is used locally.
+
+ http://flask.pocoo.org/snippets/35/
+
+ In nginx:
+ location /myprefix {
+ proxy_pass http://127.0.0.1:8000;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Scheme $scheme;
+ proxy_set_header X-Script-Name /myprefix;
+ }
+
+ :param app: the WSGI application
+ '''
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
+ if script_name:
+ environ['SCRIPT_NAME'] = script_name
+ path_info = environ['PATH_INFO']
+ if path_info.startswith(script_name):
+ environ['PATH_INFO'] = path_info[len(script_name):]
+
+ scheme = environ.get('HTTP_X_SCHEME', '')
+ if scheme:
+ environ['wsgi.url_scheme'] = scheme
+ return self.app(environ, start_response)
+
+
+application = app
+# patch app to handle non root url-s behind proxy & wsgi
+app.wsgi_app = ReverseProxyPathFix(ProxyFix(application.wsgi_app))
+
+if __name__ == "__main__":
+ run()
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..6466d1f
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+"""Installer for Searx package."""
+
+from setuptools import setup
+from setuptools import find_packages
+
+import os
+import sys
+
+# required to load VERSION_STRING constant
+sys.path.insert(0, './searx')
+from version import VERSION_STRING
+
+
+def read(*rnames):
+ return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+
+long_description = read('README.rst')
+requirements = map(str.strip, open('requirements.txt').readlines())
+dev_requirements = map(str.strip, open('requirements-dev.txt').readlines())
+
+setup(
+ name='searx',
+ version=VERSION_STRING,
+ description="A privacy-respecting, hackable metasearch engine",
+ long_description=long_description,
+ classifiers=[
+ "Development Status :: 4 - Beta",
+ "Programming Language :: Python",
+ "Topic :: Internet",
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+ 'License :: OSI Approved :: GNU Affero General Public License v3'
+ ],
+ keywords='metasearch searchengine search web http',
+ author='Adam Tauber',
+ author_email='asciimoo@gmail.com',
+ url='https://github.com/asciimoo/searx',
+ license='GNU Affero General Public License',
+ packages=find_packages('.'),
+ zip_safe=False,
+ install_requires=requirements,
+ extras_require={
+ 'test': dev_requirements
+ },
+ entry_points={
+ 'console_scripts': [
+ 'searx-run = searx.webapp:run'
+ ]
+ },
+ package_data={
+ 'searx': [
+ 'settings.yml',
+ '../README.rst',
+ '../requirements.txt',
+ '../requirements-dev.txt',
+ 'data/*',
+ 'plugins/*/*',
+ 'static/*.*',
+ 'static/*/*.*',
+ 'static/*/*/*.*',
+ 'static/*/*/*/*.*',
+ 'static/*/*/*/*/*.*',
+ 'templates/*/*.*',
+ 'templates/*/*/*.*',
+ 'tests/*',
+ 'tests/*/*',
+ 'tests/*/*/*',
+ 'translations/*/*/*'
+ ],
+ },
+
+)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/__init__.py
diff --git a/tests/robot/__init__.py b/tests/robot/__init__.py
new file mode 100644
index 0000000..038a319
--- /dev/null
+++ b/tests/robot/__init__.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+
+from time import sleep
+
+url = "http://localhost:11111/"
+
+
+def test_index(browser):
+ # Visit URL
+ browser.visit(url)
+ assert browser.is_text_present('about')
+
+
+def test_404(browser):
+ # Visit URL
+ browser.visit(url + 'missing_link')
+ assert browser.is_text_present('Page not found')
+
+
+def test_about(browser):
+ browser.visit(url)
+ browser.click_link_by_text('about')
+ assert browser.is_text_present('Why use searx?')
+
+
+def test_preferences(browser):
+ browser.visit(url)
+ browser.click_link_by_text('preferences')
+ assert browser.is_text_present('Preferences')
+ assert browser.is_text_present('Cookies')
+
+ assert browser.is_element_present_by_xpath('//label[@for="checkbox_dummy"]')
+
+
+def test_preferences_engine_select(browser):
+ browser.visit(url)
+ browser.click_link_by_text('preferences')
+
+ assert browser.is_element_present_by_xpath('//a[@href="#tab_engine"]')
+ browser.find_by_xpath('//a[@href="#tab_engine"]').first.click()
+
+ assert not browser.find_by_xpath('//input[@id="engine_general_dummy__general"]').first.checked
+ browser.find_by_xpath('//label[@for="engine_general_dummy__general"]').first.check()
+ browser.find_by_xpath('//input[@value="save"]').first.click()
+
+ # waiting for the redirect - without this the test is flaky..
+ sleep(1)
+
+ browser.visit(url)
+ browser.click_link_by_text('preferences')
+ browser.find_by_xpath('//a[@href="#tab_engine"]').first.click()
+
+ assert browser.find_by_xpath('//input[@id="engine_general_dummy__general"]').first.checked
+
+
+def test_preferences_locale(browser):
+ browser.visit(url)
+ browser.click_link_by_text('preferences')
+
+ browser.select('locale', 'hu')
+ browser.find_by_xpath('//input[@value="save"]').first.click()
+
+ # waiting for the redirect - without this the test is flaky..
+ sleep(1)
+
+ browser.visit(url)
+ browser.click_link_by_text('beállítások')
+ browser.is_text_present('Beállítások')
+
+
+def test_search(browser):
+ browser.visit(url)
+ browser.fill('q', 'test search query')
+ browser.find_by_xpath('//button[@type="submit"]').first.click()
+ assert browser.is_text_present('didn\'t find any results')
diff --git a/tests/test_robot.py b/tests/test_robot.py
new file mode 100644
index 0000000..b48153f
--- /dev/null
+++ b/tests/test_robot.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+import os
+import unittest2 as unittest
+from plone.testing import layered
+from robotsuite import RobotTestSuite
+from searx.testing import SEARXROBOTLAYER
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ current_dir = os.path.abspath(os.path.dirname(__file__))
+ robot_dir = os.path.join(current_dir, 'robot')
+ tests = [
+ os.path.join('robot', f) for f in
+ os.listdir(robot_dir) if f.endswith('.robot') and
+ f.startswith('test_')
+ ]
+ for test in tests:
+ suite.addTests([
+ layered(RobotTestSuite(test), layer=SEARXROBOTLAYER),
+ ])
+ return suite
diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/unit/__init__.py
diff --git a/tests/unit/engines/__init__.py b/tests/unit/engines/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/unit/engines/__init__.py
diff --git a/tests/unit/engines/seedpeer_fixture.html b/tests/unit/engines/seedpeer_fixture.html
new file mode 100644
index 0000000..28207bf
--- /dev/null
+++ b/tests/unit/engines/seedpeer_fixture.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ </head>
+ <body>
+ <div id="header">
+ <div id="whoIsYou">
+ <a href="/lang.php"><small>SeedPeer in your own language?</small></a>&nbsp;&nbsp;&nbsp;<a href="http://www.seedpeer.eu"><img src="/images/flags/uk.gif" width="16px" alt="Torrents EN" /></a> <a href="http://spanish.seedpeer.eu"><img src="/images/flags/es.gif" width="16px" alt="Torrents ES" /></a> <a href="http://german.seedpeer.eu"><img src="/images/flags/de.gif" width="16px" alt="Torrents DE" /></a> <a href="http://french.seedpeer.eu"><img src="/images/flags/fr.gif" width="16px" alt="Torrents FR" /></a> <a href="http://portuguese.seedpeer.eu"><img src="/images/flags/pt.gif" width="16px" alt="Torrents Portuguese" /></a> <a href="http://swedish.seedpeer.eu"><img src="/images/flags/se.gif" width="16px" alt="Torrents Sweden" /></a>
+ </div>
+
+ <script type="text/javascript">
+ whoIsYou();
+ </script>
+ <div id="search">
+ <form action="/search.php" method="get">
+ <input id="topsearchbar" name="search" value="narcos season 2" />
+ <input type="submit" class="searchbutton" value="Torrents" />
+ <input style="color:#000" type="submit" class="searchbutton" name="usenet" value="Usenet Binaries" />
+ </form>
+ <div id="suggestion"></div>
+ </div>
+ <div id="logo"><a href="/"><img src="/images/logo2.gif" alt="Seedpeer homepage" width="415" height="143" /></a></div>
+ <div id="subtext"><a href="/">Home</a> &gt; <a href="/search.html">Torrent search</a> &gt; Narcos season 2 | page 1</div>
+ </div>
+ <div id="nav">
+ <ul>
+ <!--
+ <li><font style="color:red;font-size:9px;font-weight:bold;">NEW</font><a title="Download TOP Games for FREE" rel="nofollow" href="http://www.bigrebelads.com/affiliate/index?ref=9301" target="_blank">FREE Games</a></li>
+
+ -->
+ <li style="border-left:none" id="categories"><a title="Browse Torrent Categories" href="/browse.html">Categories</a>
+ <ul>
+ <li><a title="Browse Anime Torrents" href="/browse.html#6">Anime</a></li>
+ <li><a title="Browse Game Torrents" href="/browse.html#4">Games</a></li>
+ <li><a title="Browse Movie Torrents" href="/browse.html#1">Movies</a></li>
+ <li><a title="Browse Music Torrents" href="/browse.html#3">Music</a></li>
+ <li><a title="Browse Software Torrents" href="/browse.html#5">Software</a></li>
+ <li><a title="Browse TV Torrents" href="/browse.html#2">TV Shows</a></li>
+ <li><a title="Browse Other Torrents" href="/browse.html#7">Others</a></li>
+ </ul>
+ </li>
+ <li><a title="Upload A Torrents" href="/upload.html">Upload torrent</a></li>
+ <li id="verified"><a title="Verified Torrents" href="/verified.html">Verified</a></li>
+ <li id="searchoptions"><a title="Search Torrents" href="/search.html">Torrent search</a></li>
+ <li id="newsgroups"><a style="color:#212b3e" title="News Groups" href="/usenet.html">Usenet Binaries</a></li>
+ <li id="about" style="border-right:none"><a rel="nofollow" href="/faq.html">About Us</a>
+ <ul>
+ <li><a title="SeedPeer Statistics" href="/stats.html">Statistics</a></li>
+ <li><a title="Contact Us" href="/contact.html">Contact</a></li>
+ <li><a title="Frequently Asked Questions" href="/faq.html">FAQ</a></li>
+ <li><a title="SeedPeer API" href="http://api.seedpeer.eu">Our API</a></li>
+ <li><a title="SeedPeer Blog" href="/blog">Blog</a></li>
+ </ul>
+ </li>
+ <!--<li><a href="/toolbar.php">Our Toolbar</a></li>-->
+ </ul>
+ <div class="clear"></div>
+ </div>
+ <div id="body"><div id="pageTop"></div>
+ <div id="headerbox"><h1>Verified <font class="colored">Narcos season 2</font> torrents</h1></div><table width="100%"><tr><th>
+ <span style="float:right">
+ <a href="/search/narcos-season-2/8/1.html"><img style="vertical-align:middle" src="/images/comments.gif" alt="comments" /></a> |
+ <a href="/search/narcos-season-2/7/1.html"><img style="vertical-align:middle" src="/images/ver.gif" alt="verified" /></a>
+ </span>
+ <a href="/search/narcos-season-2/1/1.html">Torrent name</a></th><th class="right"><a href="/search/narcos-season-2/2/1.html">Age</a></th><th class="right"><a href="/search/narcos-season-2/3/1.html">Size</a></th><th class="right"><a href="/search/narcos-season-2/4/1.html">Seeds</a></th><th class="right"><a href="/search/narcos-season-2/5/1.html">Peers</a></th><th class="center"><a href="/search/narcos-season-2/6/1.html">Health</a></th><td class="tableAd" rowspan="6"><iframe src="http://creative.wwwpromoter.com/13689?d=300x250" width="300" height="250" style="border: none;" frameborder="0" scrolling="no"></iframe></td></tr><tr class=""><td><a class="pblink" id="pblink_table_item_1" href="" data-tad="431726" data-last-search="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Narcos season 2</strong> Full Version</a></td><td class="right">20 hours</td><td class="right">681.3 MB</td><td class="right"><font color="green">28</font> </td><td class="right"><font color="navy">654</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr><tr class="tdark"><td><a class="pblink" id="pblink_table_item_2" href="" data-tad="431727" data-url="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Narcos season 2</strong> Trusted Source</a></td><td class="right">12 hours</td><td class="right">787.1 MB</td><td class="right"><font color="green">64</font> </td><td class="right"><font color="navy">220</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr><tr class=""><td><a class="pblink" id="pblink_table_item_3" href="" data-tad="431729" data-last-search="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Full Narcos season 2 Download</strong></a> <small><a class="pblink" id="pblink_table_item_4" href="" data-tad="431729" data-last-search="narcos+season+2" target="_blank" rel="nofollow">Usenet</a></small></td><td class="right">24 hours</td><td class="right">775.5 MB</td><td class="right"><font color="green">60</font> </td><td class="right"><font color="navy">236</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr><tr class="tdark"><td><a class="pblink" id="pblink_table_item_5" href="" data-tad="431730" data-last-search="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Narcos season 2</strong> 2014 - DIRECT STREAMING</a> <small><a class="pblink" id="pblink_table_item_6" href="" data-tad="431729" data-last-search="narcos+season+2" target="_blank" rel="nofollow">Movies</a></small></td><td class="right">17 hours</td><td class="right">654.1 MB</td><td class="right"><font color="green">2</font> </td><td class="right"><font color="navy">391</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr><tr class=""><td><a class="pblink" id="pblink_table_item_7" href="" data-tad="431731" data-last-search="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Narcos season 2</strong> 2014</a> <small><a class="pblink" id="pblink_table_item_8" href="" data-tad="431729" data-last-search="narcos+season+2" target="_blank" rel="nofollow">Movies</a></small></td><td class="right">20 hours</td><td class="right">754.5 MB</td><td class="right"><font color="green">21</font> </td><td class="right"><font color="navy">919</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr></table><br /><br /><center><iframe src='http://creative.wwwpromoter.com/13689?d=728x90' width='728' height='90' style='border: none;' frameborder='0' scrolling='no'></iframe><center><span style="float:right;margin:1em .2em 0 0"><a title="Download at the speed of your connection" href="/usenet.php?search=narcos+season+2"><img src="/images/dlf.gif" alt="Search Binaries" /></a></span><div style="margin-bottom:1em;margin-right:290px" id="headerbox"><h1><a href="/searchfeed/narcos+season+2.xml" target="_blank" title="SeedPeer RSS Torrent Search Feed fornarcos season 2"><img src="/images/feedIcon.png" border="0" /></a>&nbsp;2 <font class="colored">Narcos season 2</font> Torrents were found</h1></div><table width="100%"><tr><th>
+ <span style="float:right">
+ <a href="/search/narcos-season-2/8/1.html"><img style="vertical-align:middle" src="/images/comments.gif" alt="comments" /></a> |
+ <a href="/search/narcos-season-2/7/1.html"><img style="vertical-align:middle" src="/images/ver.gif" alt="verified" /></a>
+ </span>
+ <a href="/search/narcos-season-2/1/1.html">Torrent name</a></th><th class="right"><a href="/search/narcos-season-2/2/1.html">Age</a></th><th class="right"><a href="/search/narcos-season-2/3/1.html">Size</a></th><th class="right"><a href="/search/narcos-season-2/4/1.html">Seeds</a></th><th class="right"><a href="/search/narcos-season-2/5/1.html">Peers</a></th><th class="center"><a href="/search/narcos-season-2/6/1.html">Health</a></th></tr><tr class=""><td><small class="comments"><a href="http://www.facebook.com/sharer.php?t=Download%20<strong class='colored'>Narcos</strong> <strong class='colored'>Season</strong> <strong class='colored'>2</strong> Complete 7<strong class='colored'>2</strong>0p WebRip EN-SUB x<strong class='colored'>2</strong>64-[MULVAcoded] S0<strong class='colored'>2</strong>%20 torrent&u=http://seedpeer.seedpeer.eu/details/11686840/Narcos-Season-2-Complete-720p-WebRip-EN-SUB-x264-[MULVAcoded]-S02.html"><img src="/images/facebook.png" alt="Add to Facebook" width="14" height="14" /></a></small><a href="/details/11686840/Narcos-Season-2-Complete-720p-WebRip-EN-SUB-x264-[MULVAcoded]-S02.html"><strong class='colored'>Narcos</strong> <strong class='colored'>Season</strong> <strong class='colored'>2</strong> Complete 7<strong class='colored'>2</strong>0p WebRip EN-SUB x<strong class='colored'>2</strong>64-[MULVAcoded] S0<strong class='colored'>2</strong> <small><a href="/browse.html#11686840"></a></small></a></td><td class="right">19 hours</td><td class="right">4.39 GB</td><td class="right"><font color="green">715</font> </td><td class="right"><font color="navy">183</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" width="40" height="11" /></td></tr><tr class="tdark"><td><small class="comments"><a href="http://www.facebook.com/sharer.php?t=Download%20<strong class='colored'>Narcos</strong> - <strong class='colored'>Season</strong> <strong class='colored'>2</strong> - 7<strong class='colored'>2</strong>0p WEBRiP - x<strong class='colored'>2</strong>65 HEVC - ShAaNiG%20 torrent&u=http://seedpeer.seedpeer.eu/details/11685972/Narcos---Season-2---720p-WEBRiP---x265-HEVC---ShAaNiG.html"><img src="/images/facebook.png" alt="Add to Facebook" width="14" height="14" /></a></small><a href="/details/11685972/Narcos---Season-2---720p-WEBRiP---x265-HEVC---ShAaNiG.html"><strong class='colored'>Narcos</strong> - <strong class='colored'>Season</strong> <strong class='colored'>2</strong> - 7<strong class='colored'>2</strong>0p WEBRiP - x<strong class='colored'>2</strong>65 HEVC - ShAaNiG <small><a href="/browse.html#11685972"></a></small></a></td><td class="right">1 day</td><td class="right">2.48 GB</td><td class="right"><font color="green">861</font> </td><td class="right"><font color="navy">332</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" width="40" height="11" /></td></tr></table><div id="headerbox"><h1>Related searches for: <font class="colored">Narcos season 2</font></h1></div><div id="search_suggestions"><br />Other suggested searches: </div><br /><a href="http://torrentz2.eu/search?f=narcos-season-2">Search for "narcos-season-2" on Torrentz2.eu</a><br /><a href="http://torrent-finder.info/show.php?q=narcos-season-2">Search for "narcos-season-2" on Torrent-Finder</a><br /><center><iframe src='http://creative.wwwpromoter.com/13689?d=300x250' width='300' height='250' style='border: none;' frameborder='0' scrolling='no'></iframe>&nbsp;<iframe src='http://creative.wwwpromoter.com/13689?d=300x250' width='300' height='250' style='border: none;' frameborder='0' scrolling='no'></iframe>&nbsp;<iframe src='http://creative.wwwpromoter.com/13689?d=300x250' width='300' height='250' style='border: none;' frameborder='0' scrolling='no'></iframe></center><div id="footer">
+ <table width="100%">
+ <tr>
+ <td width="30%">
+ <h2>Torrents Download</h2>
+ <a href="/">Torrent search</a><br />
+ <a href="/browse.html">Browse categories</a><br />
+ <a href="/verified.html">Verified Torrents</a><br />
+ <a href="/order-date.html">Today's torrents</a><br />
+ <a href="/yesterday.html">Yesterday's torrents</a><br />
+ <a href="/stats.html">Statistics</a><br />
+ <br />
+ <a href="/faq.html#copyright"><strong>Copyright & Removal</strong></a>
+ </td>
+ <td width="30%"><h2>Cool Stuff</h2>
+ <a href="/promotional.php">Promotional</a><br />
+ <a href="/contact.html">Advertising Information</a><br />
+ <strong><a href="/plugins.php" title="Add a search plugin to Firefox or Internet Explorer">Search Plugin <span style="color:red">*</span></a></strong><br />
+ <a href="http://www.utorrent.com">&micro;Torrent Client</a><br />
+ <a href="/blog">Seedpeer Blog</a><br />
+ </td>
+ <td width="30%"><h2>Links</h2>
+ <a href="http://www.sumotorrent.com" target="_blank"><strong>SumoTorrent</strong></a><br />
+ <a href="http://www.torrent-finder.info" target="_blank"><strong>Torrent Finder</strong></a><br />
+ <a href="http://www.torrentpond.com" target="_blank"><strong>TorrentPond</strong></a><br />
+ <a href="https://www.limetorrents.cc" target="_blank">LimeTorrents.cc</a><br />
+ <a href="http://www.torrents.to/" target="_blank">Torrents.to</a><br />
+ <a href="http://www.torrentfunk.com" target="_blank">TorrentFunk</a><br />
+ <a href="https://monova.org" target="_blank">Monova</a><br />
+ <a href="http://www.torrentroom.com" target="_blank">TorrentRoom</a><br />
+ <a href="http://www.katcr.co/" target="_blank">Kickass Torrents Community</a><br />
+ </td>
+ <td width="10%"><div id="bottomlogo"></div></td>
+ </tr>
+ </table>
+ <br />
+ <br />
+ </div>
+ </div>
+ </body>
+ </html> \ No newline at end of file
diff --git a/tests/unit/engines/test_archlinux.py b/tests/unit/engines/test_archlinux.py
new file mode 100644
index 0000000..e4ee033
--- /dev/null
+++ b/tests/unit/engines/test_archlinux.py
@@ -0,0 +1,106 @@
+from collections import defaultdict
+import mock
+from searx.engines import archlinux
+from searx.testing import SearxTestCase
+
+domains = {
+ 'all': 'https://wiki.archlinux.org',
+ 'de': 'https://wiki.archlinux.de',
+ 'fr': 'https://wiki.archlinux.fr',
+ 'ja': 'https://wiki.archlinuxjp.org',
+ 'ro': 'http://wiki.archlinux.ro',
+ 'tr': 'http://archtr.org/wiki'
+}
+
+
+class TestArchLinuxEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dic = defaultdict(dict)
+ dic['pageno'] = 1
+ dic['language'] = 'en_US'
+ params = archlinux.request(query, dic)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('wiki.archlinux.org' in params['url'])
+
+ for lang, domain in domains.items():
+ dic['language'] = lang
+ params = archlinux.request(query, dic)
+ self.assertTrue(domain in params['url'])
+
+ def test_response(self):
+ response = mock.Mock(text='<html></html>',
+ search_params={'language': 'en_US'})
+ self.assertEqual(archlinux.response(response), [])
+
+ html = """
+ <ul class="mw-search-results">
+ <li>
+ <div class="mw-search-result-heading">
+ <a href="/index.php/ATI" title="ATI">ATI</a>
+ </div>
+ <div class="searchresult">
+ Lorem ipsum dolor sit amet
+ </div>
+ <div class="mw-search-result-data">
+ 30 KB (4,630 words) - 19:04, 17 March 2016</div>
+ </li>
+ <li>
+ <div class="mw-search-result-heading">
+ <a href="/index.php/Frequently_asked_questions" title="Frequently asked questions">
+ Frequently asked questions
+ </a>
+ </div>
+ <div class="searchresult">
+ CPUs with AMDs instruction set "AMD64"
+ </div>
+ <div class="mw-search-result-data">
+ 17 KB (2,722 words) - 20:13, 21 March 2016
+ </div>
+ </li>
+ <li>
+ <div class="mw-search-result-heading">
+ <a href="/index.php/CPU_frequency_scaling" title="CPU frequency scaling">CPU frequency scaling</a>
+ </div>
+ <div class="searchresult">
+ ondemand for AMD and older Intel CPU
+ </div>
+ <div class="mw-search-result-data">
+ 15 KB (2,319 words) - 23:46, 16 March 2016
+ </div>
+ </li>
+ </ul>
+ """
+
+ expected = [
+ {
+ 'title': 'ATI',
+ 'url': 'https://wiki.archlinux.org/index.php/ATI'
+ },
+ {
+ 'title': 'Frequently asked questions',
+ 'url': 'https://wiki.archlinux.org/index.php/Frequently_asked_questions'
+ },
+ {
+ 'title': 'CPU frequency scaling',
+ 'url': 'https://wiki.archlinux.org/index.php/CPU_frequency_scaling'
+ }
+ ]
+
+ response = mock.Mock(text=html)
+ response.search_params = {
+ 'language': 'en_US'
+ }
+ results = archlinux.response(response)
+
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), len(expected))
+
+ i = 0
+ for exp in expected:
+ res = results[i]
+ i += 1
+ for key, value in exp.items():
+ self.assertEqual(res[key], value)
diff --git a/tests/unit/engines/test_bing.py b/tests/unit/engines/test_bing.py
new file mode 100644
index 0000000..523ec57
--- /dev/null
+++ b/tests/unit/engines/test_bing.py
@@ -0,0 +1,120 @@
+from collections import defaultdict
+import mock
+from searx.engines import bing
+from searx.testing import SearxTestCase
+
+
+class TestBingEngine(SearxTestCase):
+
+ def test_request(self):
+ query = u'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ dicto['language'] = 'fr_FR'
+ params = bing.request(query.encode('utf-8'), dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('language%3AFR' in params['url'])
+ self.assertTrue('bing.com' in params['url'])
+
+ dicto['language'] = 'all'
+ params = bing.request(query.encode('utf-8'), dicto)
+ self.assertTrue('language' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, bing.response, None)
+ self.assertRaises(AttributeError, bing.response, [])
+ self.assertRaises(AttributeError, bing.response, '')
+ self.assertRaises(AttributeError, bing.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(bing.response(response), [])
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(bing.response(response), [])
+
+ html = """
+ <div class="sa_cc" u="0|5109|4755453613245655|UAGjXgIrPH5yh-o5oNHRx_3Zta87f_QO">
+ <div Class="sa_mc">
+ <div class="sb_tlst">
+ <h3>
+ <a href="http://this.should.be.the.link/" h="ID=SERP,5124.1">
+ <strong>This</strong> should be the title</a>
+ </h3>
+ </div>
+ <div class="sb_meta"><cite><strong>this</strong>.meta.com</cite>
+ <span class="c_tlbxTrg">
+ <span class="c_tlbxH" H="BASE:CACHEDPAGEDEFAULT" K="SERP,5125.1">
+ </span>
+ </span>
+ </div>
+ <p><strong>This</strong> should be the content.</p>
+ </div>
+ </div>
+ """
+ response = mock.Mock(text=html)
+ results = bing.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This should be the title')
+ self.assertEqual(results[0]['url'], 'http://this.should.be.the.link/')
+ self.assertEqual(results[0]['content'], 'This should be the content.')
+
+ html = """
+ <li class="b_algo" u="0|5109|4755453613245655|UAGjXgIrPH5yh-o5oNHRx_3Zta87f_QO">
+ <div Class="sa_mc">
+ <div class="sb_tlst">
+ <h2>
+ <a href="http://this.should.be.the.link/" h="ID=SERP,5124.1">
+ <strong>This</strong> should be the title</a>
+ </h2>
+ </div>
+ <div class="sb_meta"><cite><strong>this</strong>.meta.com</cite>
+ <span class="c_tlbxTrg">
+ <span class="c_tlbxH" H="BASE:CACHEDPAGEDEFAULT" K="SERP,5125.1">
+ </span>
+ </span>
+ </div>
+ <p><strong>This</strong> should be the content.</p>
+ </div>
+ </li>
+ """
+ response = mock.Mock(text=html)
+ results = bing.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This should be the title')
+ self.assertEqual(results[0]['url'], 'http://this.should.be.the.link/')
+ self.assertEqual(results[0]['content'], 'This should be the content.')
+
+ def test_fetch_supported_languages(self):
+ html = """<html></html>"""
+ response = mock.Mock(text=html)
+ results = bing._fetch_supported_languages(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ html = """
+ <html>
+ <body>
+ <form>
+ <div id="limit-languages">
+ <div>
+ <div><input id="es" value="es"></input></div>
+ </div>
+ <div>
+ <div><input id="pt_BR" value="pt_BR"></input></div>
+ <div><input id="pt_PT" value="pt_PT"></input></div>
+ </div>
+ </div>
+ </form>
+ </body>
+ </html>
+ """
+ response = mock.Mock(text=html)
+ languages = bing._fetch_supported_languages(response)
+ self.assertEqual(type(languages), list)
+ self.assertEqual(len(languages), 3)
+ self.assertIn('es', languages)
+ self.assertIn('pt-BR', languages)
+ self.assertIn('pt-PT', languages)
diff --git a/tests/unit/engines/test_bing_images.py b/tests/unit/engines/test_bing_images.py
new file mode 100644
index 0000000..287f134
--- /dev/null
+++ b/tests/unit/engines/test_bing_images.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import bing_images
+from searx.testing import SearxTestCase
+
+
+class TestBingImagesEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr_FR'
+ dicto['safesearch'] = 1
+ dicto['time_range'] = ''
+ params = bing_images.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('bing.com' in params['url'])
+ self.assertTrue('SRCHHPGUSR' in params['cookies'])
+ self.assertTrue('fr' in params['cookies']['SRCHHPGUSR'])
+
+ dicto['language'] = 'all'
+ params = bing_images.request(query, dicto)
+ self.assertIn('SRCHHPGUSR', params['cookies'])
+ self.assertIn('en', params['cookies']['SRCHHPGUSR'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, bing_images.response, None)
+ self.assertRaises(AttributeError, bing_images.response, [])
+ self.assertRaises(AttributeError, bing_images.response, '')
+ self.assertRaises(AttributeError, bing_images.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(bing_images.response(response), [])
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(bing_images.response(response), [])
+
+ html = """
+ <div id="mmComponent_images_1">
+ <ul>
+ <li>
+ <div>
+ <div class="imgpt">
+ <a m='{"purl":"page_url","murl":"img_url"}' mad='{"turl":"thumb_url"}'>
+ <img src="" alt="alt text" />
+ </a>
+ </div>
+ <div></div>
+ </div>
+ <div>
+ <div class="imgpt">
+ <a m='{"purl":"page_url2","murl":"img_url2"}' mad='{"turl":"thumb_url2"}'>
+ <img src="" alt="alt text 2" />
+ </a>
+ </div>
+ </div>
+ </li>
+ </ul>
+ <ul>
+ <li>
+ <div>
+ <div class="imgpt">
+ <a m='{"purl":"page_url3","murl":"img_url3"}' mad='{"turl":"thumb_url3"}'>
+ <img src="" alt="alt text 3" />
+ </a>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+ """
+ html = html.replace('\r\n', '').replace('\n', '').replace('\r', '')
+ response = mock.Mock(text=html)
+ results = bing_images.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 3)
+ self.assertEqual(results[0]['title'], 'alt text')
+ self.assertEqual(results[0]['url'], 'page_url')
+ self.assertEqual(results[0]['content'], '')
+ self.assertEqual(results[0]['thumbnail_src'], 'thumb_url')
+ self.assertEqual(results[0]['img_src'], 'img_url')
diff --git a/tests/unit/engines/test_bing_news.py b/tests/unit/engines/test_bing_news.py
new file mode 100644
index 0000000..1f1aeca
--- /dev/null
+++ b/tests/unit/engines/test_bing_news.py
@@ -0,0 +1,146 @@
+from collections import defaultdict
+import mock
+from searx.engines import bing_news
+from searx.testing import SearxTestCase
+import lxml
+
+
+class TestBingNewsEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr_FR'
+ dicto['time_range'] = ''
+ params = bing_news.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('bing.com', params['url'])
+ self.assertIn('fr', params['url'])
+
+ dicto['language'] = 'all'
+ params = bing_news.request(query, dicto)
+ self.assertIn('en', params['url'])
+
+ def test_no_url_in_request_year_time_range(self):
+ dicto = defaultdict(dict)
+ query = 'test_query'
+ dicto['time_range'] = 'year'
+ params = bing_news.request(query, dicto)
+ self.assertEqual({}, params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, bing_news.response, None)
+ self.assertRaises(AttributeError, bing_news.response, [])
+ self.assertRaises(AttributeError, bing_news.response, '')
+ self.assertRaises(AttributeError, bing_news.response, '[]')
+
+ response = mock.Mock(content='<html></html>')
+ self.assertEqual(bing_news.response(response), [])
+
+ response = mock.Mock(content='<html></html>')
+ self.assertEqual(bing_news.response(response), [])
+
+ html = """<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0" xmlns:News="https://www.bing.com:443/news/search?q=python&amp;setmkt=en-US&amp;first=1&amp;format=RSS">
+ <channel>
+ <title>python - Bing News</title>
+ <link>https://www.bing.com:443/news/search?q=python&amp;setmkt=en-US&amp;first=1&amp;format=RSS</link>
+ <description>Search results</description>
+ <image>
+ <url>http://10.53.64.9/rsslogo.gif</url>
+ <title>test</title>
+ <link>https://www.bing.com:443/news/search?q=test&amp;setmkt=en-US&amp;first=1&amp;format=RSS</link>
+ </image>
+ <copyright>Copyright</copyright>
+ <item>
+ <title>Title</title>
+ <link>https://www.bing.com/news/apiclick.aspx?ref=FexRss&amp;aid=&amp;tid=c237eccc50bd4758b106a5e3c94fce09&amp;url=http%3a%2f%2furl.of.article%2f&amp;c=xxxxxxxxx&amp;mkt=en-us</link>
+ <description>Article Content</description>
+ <pubDate>Tue, 02 Jun 2015 13:37:00 GMT</pubDate>
+ <News:Source>Infoworld</News:Source>
+ <News:Image>http://a1.bing4.com/th?id=ON.13371337133713371337133713371337&amp;pid=News</News:Image>
+ <News:ImageSize>w={0}&amp;h={1}&amp;c=7</News:ImageSize>
+ <News:ImageKeepOriginalRatio></News:ImageKeepOriginalRatio>
+ <News:ImageMaxWidth>620</News:ImageMaxWidth>
+ <News:ImageMaxHeight>413</News:ImageMaxHeight>
+ </item>
+ <item>
+ <title>Another Title</title>
+ <link>https://www.bing.com/news/apiclick.aspx?ref=FexRss&amp;aid=&amp;tid=c237eccc50bd4758b106a5e3c94fce09&amp;url=http%3a%2f%2fanother.url.of.article%2f&amp;c=xxxxxxxxx&amp;mkt=en-us</link>
+ <description>Another Article Content</description>
+ <pubDate>Tue, 02 Jun 2015 13:37:00 GMT</pubDate>
+ </item>
+ </channel>
+</rss>""" # noqa
+ response = mock.Mock(content=html.encode('utf-8'))
+ results = bing_news.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], 'Title')
+ self.assertEqual(results[0]['url'], 'http://url.of.article/')
+ self.assertEqual(results[0]['content'], 'Article Content')
+ self.assertEqual(results[0]['img_src'], 'https://www.bing.com/th?id=ON.13371337133713371337133713371337')
+ self.assertEqual(results[1]['title'], 'Another Title')
+ self.assertEqual(results[1]['url'], 'http://another.url.of.article/')
+ self.assertEqual(results[1]['content'], 'Another Article Content')
+ self.assertNotIn('img_src', results[1])
+
+ html = """<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0" xmlns:News="https://www.bing.com:443/news/search?q=python&amp;setmkt=en-US&amp;first=1&amp;format=RSS">
+ <channel>
+ <title>python - Bing News</title>
+ <link>https://www.bing.com:443/news/search?q=python&amp;setmkt=en-US&amp;first=1&amp;format=RSS</link>
+ <description>Search results</description>
+ <image>
+ <url>http://10.53.64.9/rsslogo.gif</url>
+ <title>test</title>
+ <link>https://www.bing.com:443/news/search?q=test&amp;setmkt=en-US&amp;first=1&amp;format=RSS</link>
+ </image>
+ <copyright>Copyright</copyright>
+ <item>
+ <title>Title</title>
+ <link>http://another.url.of.article/</link>
+ <description>Article Content</description>
+ <pubDate>garbage</pubDate>
+ <News:Source>Infoworld</News:Source>
+ <News:Image>http://another.bing.com/image</News:Image>
+ <News:ImageSize>w={0}&amp;h={1}&amp;c=7</News:ImageSize>
+ <News:ImageKeepOriginalRatio></News:ImageKeepOriginalRatio>
+ <News:ImageMaxWidth>620</News:ImageMaxWidth>
+ <News:ImageMaxHeight>413</News:ImageMaxHeight>
+ </item>
+ </channel>
+</rss>""" # noqa
+ response = mock.Mock(content=html.encode('utf-8'))
+ results = bing_news.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title')
+ self.assertEqual(results[0]['url'], 'http://another.url.of.article/')
+ self.assertEqual(results[0]['content'], 'Article Content')
+ self.assertEqual(results[0]['img_src'], 'http://another.bing.com/image')
+
+ html = """<?xml version="1.0" encoding="utf-8" ?>
+<rss version="2.0" xmlns:News="https://www.bing.com:443/news/search?q=python&amp;setmkt=en-US&amp;first=1&amp;format=RSS">
+ <channel>
+ <title>python - Bing News</title>
+ <link>https://www.bing.com:443/news/search?q=python&amp;setmkt=en-US&amp;first=1&amp;format=RSS</link>
+ <description>Search results</description>
+ <image>
+ <url>http://10.53.64.9/rsslogo.gif</url>
+ <title>test</title>
+ <link>https://www.bing.com:443/news/search?q=test&amp;setmkt=en-US&amp;first=1&amp;format=RSS</link>
+ </image>
+ </channel>
+</rss>""" # noqa
+
+ response = mock.Mock(content=html.encode('utf-8'))
+ results = bing_news.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ html = """<?xml version="1.0" encoding="utf-8" ?>gabarge"""
+ response = mock.Mock(content=html.encode('utf-8'))
+ self.assertRaises(lxml.etree.XMLSyntaxError, bing_news.response, response)
diff --git a/tests/unit/engines/test_blekko_images.py b/tests/unit/engines/test_blekko_images.py
new file mode 100644
index 0000000..beb0853
--- /dev/null
+++ b/tests/unit/engines/test_blekko_images.py
@@ -0,0 +1,71 @@
+from collections import defaultdict
+import mock
+from searx.engines import blekko_images
+from searx.testing import SearxTestCase
+
+
+class TestBlekkoImagesEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ dicto['safesearch'] = 1
+ params = blekko_images.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('blekko.com', params['url'])
+ self.assertIn('page', params['url'])
+
+ dicto['pageno'] = 1
+ params = blekko_images.request(query, dicto)
+ self.assertNotIn('page', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, blekko_images.response, None)
+ self.assertRaises(AttributeError, blekko_images.response, [])
+ self.assertRaises(AttributeError, blekko_images.response, '')
+ self.assertRaises(AttributeError, blekko_images.response, '[]')
+
+ response = mock.Mock(text='[]')
+ self.assertEqual(blekko_images.response(response), [])
+
+ json = """
+ [
+ {
+ "c": 1,
+ "page_url": "http://result_url.html",
+ "title": "Photo title",
+ "tn_url": "http://ts1.mm.bing.net/th?id=HN.608050619474382748&pid=15.1",
+ "url": "http://result_image.jpg"
+ },
+ {
+ "c": 2,
+ "page_url": "http://companyorange.simpsite.nl/OSM",
+ "title": "OSM",
+ "tn_url": "http://ts2.mm.bing.net/th?id=HN.608048068264919461&pid=15.1",
+ "url": "http://simpsite.nl/userdata2/58985/Home/OSM.bmp"
+ },
+ {
+ "c": 3,
+ "page_url": "http://invincible.webklik.nl/page/osm",
+ "title": "OSM",
+ "tn_url": "http://ts1.mm.bing.net/th?id=HN.608024514657649476&pid=15.1",
+ "url": "http://www.webklik.nl/user_files/2009_09/65324/osm.gif"
+ },
+ {
+ "c": 4,
+ "page_url": "http://www.offshorenorway.no/event/companyDetail/id/12492",
+ "title": "Go to OSM Offshore AS homepage",
+ "tn_url": "http://ts2.mm.bing.net/th?id=HN.608054265899847285&pid=15.1",
+ "url": "http://www.offshorenorway.no/firmalogo/OSM-logo.png"
+ }
+ ]
+ """
+ response = mock.Mock(text=json)
+ results = blekko_images.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 4)
+ self.assertEqual(results[0]['title'], 'Photo title')
+ self.assertEqual(results[0]['url'], 'http://result_url.html')
+ self.assertEqual(results[0]['img_src'], 'http://result_image.jpg')
diff --git a/tests/unit/engines/test_btdigg.py b/tests/unit/engines/test_btdigg.py
new file mode 100644
index 0000000..6a88e3f
--- /dev/null
+++ b/tests/unit/engines/test_btdigg.py
@@ -0,0 +1,384 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import btdigg
+from searx.testing import SearxTestCase
+
+
+class TestBtdiggEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = btdigg.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('btdigg.org', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, btdigg.response, None)
+ self.assertRaises(AttributeError, btdigg.response, [])
+ self.assertRaises(AttributeError, btdigg.response, '')
+ self.assertRaises(AttributeError, btdigg.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(btdigg.response(response), [])
+
+ html = u"""
+ <div id="search_res">
+ <table>
+ <tr>
+ <td class="idx">1</td>
+ <td>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="torrent_name">
+ <a href="/url">Should be the title</a>
+ </td>
+ </tr>
+ </table>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="ttth">
+ <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
+ title="Télécharger des liens Magnet">[magnet]</a>
+ </td>
+ <td class="ttth">
+ <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
+ target="_blank" title="Ajouter à BTCloud">[cloud]</a>
+ </td>
+ <td>
+ <span class="attr_name">Taille:</span>
+ <span class="attr_val">8 B</span>
+ </td>
+ <td>
+ <span class="attr_name">Fichiers:</span>
+ <span class="attr_val">710</span>
+ </td>
+ <td>
+ <span class="attr_name">Téléchargements:</span>
+ <span class="attr_val">5</span>
+ </td>
+ <td>
+ <span class="attr_name">Temps:</span>
+ <span class="attr_val">417.8&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
+ <span class="attr_val">5.3&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Faux:</span>
+ <span class="attr_val">Aucun</span>
+ </td>
+ </tr>
+ </table>
+ <pre class="snippet">
+ Content
+ </pre>
+ </td>
+ </tr>
+ </table>
+ </div>
+ """
+ response = mock.Mock(text=html.encode('utf-8'))
+ results = btdigg.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Should be the title')
+ self.assertEqual(results[0]['url'], 'https://btdigg.org/url')
+ self.assertEqual(results[0]['content'], 'Content')
+ self.assertEqual(results[0]['seed'], 5)
+ self.assertEqual(results[0]['leech'], 0)
+ self.assertEqual(results[0]['filesize'], 8)
+ self.assertEqual(results[0]['files'], 710)
+ self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:magnet&dn=Test')
+
+ html = """
+ <div id="search_res">
+ <table>
+ </table>
+ </div>
+ """
+ response = mock.Mock(text=html.encode('utf-8'))
+ results = btdigg.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ html = u"""
+ <div id="search_res">
+ <table>
+ <tr>
+ <td class="idx">1</td>
+ <td>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="torrent_name">
+ <a href="/url">Should be the title</a>
+ </td>
+ </tr>
+ </table>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="ttth">
+ <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
+ title="Télécharger des liens Magnet">[magnet]</a>
+ </td>
+ <td class="ttth">
+ <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
+ target="_blank" title="Ajouter à BTCloud">[cloud]</a>
+ </td>
+ <td>
+ <span class="attr_name">Taille:</span>
+ <span class="attr_val">1 KB</span>
+ </td>
+ <td>
+ <span class="attr_name">Fichiers:</span>
+ <span class="attr_val">710</span>
+ </td>
+ <td>
+ <span class="attr_name">Téléchargements:</span>
+ <span class="attr_val">5</span>
+ </td>
+ <td>
+ <span class="attr_name">Temps:</span>
+ <span class="attr_val">417.8&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
+ <span class="attr_val">5.3&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Faux:</span>
+ <span class="attr_val">Aucun</span>
+ </td>
+ </tr>
+ </table>
+ <pre class="snippet">
+ Content
+ </pre>
+ </td>
+ </tr>
+ <tr>
+ <td class="idx">1</td>
+ <td>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="torrent_name">
+ <a href="/url">Should be the title</a>
+ </td>
+ </tr>
+ </table>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="ttth">
+ <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
+ title="Télécharger des liens Magnet">[magnet]</a>
+ </td>
+ <td class="ttth">
+ <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
+ target="_blank" title="Ajouter à BTCloud">[cloud]</a>
+ </td>
+ <td>
+ <span class="attr_name">Taille:</span>
+ <span class="attr_val">1 MB</span>
+ </td>
+ <td>
+ <span class="attr_name">Fichiers:</span>
+ <span class="attr_val">a</span>
+ </td>
+ <td>
+ <span class="attr_name">Téléchargements:</span>
+ <span class="attr_val">4</span>
+ </td>
+ <td>
+ <span class="attr_name">Temps:</span>
+ <span class="attr_val">417.8&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
+ <span class="attr_val">5.3&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Faux:</span>
+ <span class="attr_val">Aucun</span>
+ </td>
+ </tr>
+ </table>
+ <pre class="snippet">
+ Content
+ </pre>
+ </td>
+ </tr>
+ <tr>
+ <td class="idx">1</td>
+ <td>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="torrent_name">
+ <a href="/url">Should be the title</a>
+ </td>
+ </tr>
+ </table>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="ttth">
+ <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
+ title="Télécharger des liens Magnet">[magnet]</a>
+ </td>
+ <td class="ttth">
+ <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
+ target="_blank" title="Ajouter à BTCloud">[cloud]</a>
+ </td>
+ <td>
+ <span class="attr_name">Taille:</span>
+ <span class="attr_val">1 GB</span>
+ </td>
+ <td>
+ <span class="attr_name">Fichiers:</span>
+ <span class="attr_val">710</span>
+ </td>
+ <td>
+ <span class="attr_name">Téléchargements:</span>
+ <span class="attr_val">3</span>
+ </td>
+ <td>
+ <span class="attr_name">Temps:</span>
+ <span class="attr_val">417.8&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
+ <span class="attr_val">5.3&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Faux:</span>
+ <span class="attr_val">Aucun</span>
+ </td>
+ </tr>
+ </table>
+ <pre class="snippet">
+ Content
+ </pre>
+ </td>
+ </tr>
+ <tr>
+ <td class="idx">1</td>
+ <td>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="torrent_name">
+ <a href="/url">Should be the title</a>
+ </td>
+ </tr>
+ </table>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="ttth">
+ <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
+ title="Télécharger des liens Magnet">[magnet]</a>
+ </td>
+ <td class="ttth">
+ <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
+ target="_blank" title="Ajouter à BTCloud">[cloud]</a>
+ </td>
+ <td>
+ <span class="attr_name">Taille:</span>
+ <span class="attr_val">1 TB</span>
+ </td>
+ <td>
+ <span class="attr_name">Fichiers:</span>
+ <span class="attr_val">710</span>
+ </td>
+ <td>
+ <span class="attr_name">Téléchargements:</span>
+ <span class="attr_val">2</span>
+ </td>
+ <td>
+ <span class="attr_name">Temps:</span>
+ <span class="attr_val">417.8&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
+ <span class="attr_val">5.3&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Faux:</span>
+ <span class="attr_val">Aucun</span>
+ </td>
+ </tr>
+ </table>
+ <pre class="snippet">
+ Content
+ </pre>
+ </td>
+ </tr>
+ <tr>
+ <td class="idx">1</td>
+ <td>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="torrent_name">
+ <a href="/url">Should be the title</a>
+ </td>
+ </tr>
+ </table>
+ <table class="torrent_name_tbl">
+ <tr>
+ <td class="ttth">
+ <a onclick="fclck(this.href)" href="magnet:?xt=urn:btih:magnet&amp;dn=Test"
+ title="Télécharger des liens Magnet">[magnet]</a>
+ </td>
+ <td class="ttth">
+ <a href="https://btcloud.io/manager?cmd=add&amp;info_hash=hash"
+ target="_blank" title="Ajouter à BTCloud">[cloud]</a>
+ </td>
+ <td>
+ <span class="attr_name">Taille:</span>
+ <span class="attr_val">a TB</span>
+ </td>
+ <td>
+ <span class="attr_name">Fichiers:</span>
+ <span class="attr_val">710</span>
+ </td>
+ <td>
+ <span class="attr_name">Téléchargements:</span>
+ <span class="attr_val">z</span>
+ </td>
+ <td>
+ <span class="attr_name">Temps:</span>
+ <span class="attr_val">417.8&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Dernière&nbsp;mise&nbsp;à&nbsp;jour:</span>
+ <span class="attr_val">5.3&nbsp;jours</span>
+ </td>
+ <td>
+ <span class="attr_name">Faux:</span>
+ <span class="attr_val">Aucun</span>
+ </td>
+ </tr>
+ </table>
+ <pre class="snippet">
+ Content
+ </pre>
+ </td>
+ </tr>
+ </table>
+ </div>
+ """
+ response = mock.Mock(text=html.encode('utf-8'))
+ results = btdigg.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 5)
+ self.assertEqual(results[0]['title'], 'Should be the title')
+ self.assertEqual(results[0]['url'], 'https://btdigg.org/url')
+ self.assertEqual(results[0]['content'], 'Content')
+ self.assertEqual(results[0]['seed'], 5)
+ self.assertEqual(results[0]['leech'], 0)
+ self.assertEqual(results[0]['files'], 710)
+ self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:magnet&dn=Test')
+ self.assertEqual(results[0]['filesize'], 1024)
+ self.assertEqual(results[1]['filesize'], 1048576)
+ self.assertEqual(results[2]['filesize'], 1073741824)
+ self.assertEqual(results[3]['filesize'], 1099511627776)
diff --git a/tests/unit/engines/test_currency_convert.py b/tests/unit/engines/test_currency_convert.py
new file mode 100644
index 0000000..2814d79
--- /dev/null
+++ b/tests/unit/engines/test_currency_convert.py
@@ -0,0 +1,43 @@
+from collections import defaultdict
+from datetime import datetime
+import mock
+from searx.engines import currency_convert
+from searx.testing import SearxTestCase
+
+
+class TestCurrencyConvertEngine(SearxTestCase):
+
+ def test_request(self):
+ query = b'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = currency_convert.request(query, dicto)
+ self.assertNotIn('url', params)
+
+ query = b'convert 10 Pound Sterlings to United States Dollars'
+ params = currency_convert.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn('finance.yahoo.com', params['url'])
+ self.assertIn('GBP', params['url'])
+ self.assertIn('USD', params['url'])
+
+ def test_response(self):
+ dicto = defaultdict(dict)
+ dicto['ammount'] = float(10)
+ dicto['from'] = "GBP"
+ dicto['to'] = "USD"
+ dicto['from_name'] = "pound sterling"
+ dicto['to_name'] = "United States dollar"
+ response = mock.Mock(text='a,b,c,d', search_params=dicto)
+ self.assertEqual(currency_convert.response(response), [])
+
+ csv = "2,0.5,1"
+ response = mock.Mock(text=csv, search_params=dicto)
+ results = currency_convert.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['answer'], '10.0 GBP = 5.0 USD, 1 GBP (pound sterling)' +
+ ' = 0.5 USD (United States dollar)')
+ now_date = datetime.now().strftime('%Y%m%d')
+ self.assertEqual(results[0]['url'], 'https://finance.yahoo.com/currency/converter-results/' +
+ now_date + '/10.0-gbp-to-usd.html')
diff --git a/tests/unit/engines/test_dailymotion.py b/tests/unit/engines/test_dailymotion.py
new file mode 100644
index 0000000..72071af
--- /dev/null
+++ b/tests/unit/engines/test_dailymotion.py
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import dailymotion
+from searx.testing import SearxTestCase
+
+
+class TestDailymotionEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ dicto['language'] = 'fr_FR'
+ params = dailymotion.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('dailymotion.com' in params['url'])
+ self.assertTrue('fr' in params['url'])
+
+ dicto['language'] = 'all'
+ params = dailymotion.request(query, dicto)
+ self.assertTrue('en' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, dailymotion.response, None)
+ self.assertRaises(AttributeError, dailymotion.response, [])
+ self.assertRaises(AttributeError, dailymotion.response, '')
+ self.assertRaises(AttributeError, dailymotion.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(dailymotion.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(dailymotion.response(response), [])
+
+ json = """
+ {
+ "page": 1,
+ "limit": 5,
+ "explicit": false,
+ "total": 289487,
+ "has_more": true,
+ "list": [
+ {
+ "created_time": 1422173451,
+ "title": "Title",
+ "description": "Description",
+ "duration": 81,
+ "url": "http://www.url",
+ "thumbnail_360_url": "http://thumbnail",
+ "id": "x2fit7q"
+ }
+ ]
+ }
+ """
+ response = mock.Mock(text=json)
+ results = dailymotion.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title')
+ self.assertEqual(results[0]['url'], 'http://www.url')
+ self.assertEqual(results[0]['content'], 'Description')
+ self.assertIn('x2fit7q', results[0]['embedded'])
+
+ json = r"""
+ {"toto":[
+ {"id":200,"name":"Artist Name",
+ "link":"http:\/\/www.dailymotion.com\/artist\/1217","type":"artist"}
+ ]}
+ """
+ response = mock.Mock(text=json)
+ results = dailymotion.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ def test_fetch_supported_languages(self):
+ json = r"""
+ {"list":[{"code":"af","name":"Afrikaans","native_name":"Afrikaans",
+ "localized_name":"Afrikaans","display_name":"Afrikaans"},
+ {"code":"ar","name":"Arabic","native_name":"\u0627\u0644\u0639\u0631\u0628\u064a\u0629",
+ "localized_name":"Arabic","display_name":"Arabic"},
+ {"code":"la","name":"Latin","native_name":null,
+ "localized_name":"Latin","display_name":"Latin"}
+ ]}
+ """
+ response = mock.Mock(text=json)
+ languages = dailymotion._fetch_supported_languages(response)
+ self.assertEqual(type(languages), dict)
+ self.assertEqual(len(languages), 3)
+ self.assertIn('af', languages)
+ self.assertIn('ar', languages)
+ self.assertIn('la', languages)
+
+ self.assertEqual(type(languages['af']), dict)
+ self.assertEqual(type(languages['ar']), dict)
+ self.assertEqual(type(languages['la']), dict)
+
+ self.assertIn('name', languages['af'])
+ self.assertIn('name', languages['ar'])
+ self.assertNotIn('name', languages['la'])
+
+ self.assertIn('english_name', languages['af'])
+ self.assertIn('english_name', languages['ar'])
+ self.assertIn('english_name', languages['la'])
+
+ self.assertEqual(languages['af']['name'], 'Afrikaans')
+ self.assertEqual(languages['af']['english_name'], 'Afrikaans')
+ self.assertEqual(languages['ar']['name'], u'العربية')
+ self.assertEqual(languages['ar']['english_name'], 'Arabic')
+ self.assertEqual(languages['la']['english_name'], 'Latin')
diff --git a/tests/unit/engines/test_deezer.py b/tests/unit/engines/test_deezer.py
new file mode 100644
index 0000000..5b9f55c
--- /dev/null
+++ b/tests/unit/engines/test_deezer.py
@@ -0,0 +1,57 @@
+from collections import defaultdict
+import mock
+from searx.engines import deezer
+from searx.testing import SearxTestCase
+
+
+class TestDeezerEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = deezer.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('deezer.com' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, deezer.response, None)
+ self.assertRaises(AttributeError, deezer.response, [])
+ self.assertRaises(AttributeError, deezer.response, '')
+ self.assertRaises(AttributeError, deezer.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(deezer.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(deezer.response(response), [])
+
+ json = r"""
+ {"data":[
+ {"id":100, "title":"Title of track",
+ "link":"https:\/\/www.deezer.com\/track\/1094042","duration":232,
+ "artist":{"id":200,"name":"Artist Name",
+ "link":"https:\/\/www.deezer.com\/artist\/1217","type":"artist"},
+ "album":{"id":118106,"title":"Album Title","type":"album"},"type":"track"}
+ ]}
+ """
+ response = mock.Mock(text=json)
+ results = deezer.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title of track')
+ self.assertEqual(results[0]['url'], 'https://www.deezer.com/track/1094042')
+ self.assertEqual(results[0]['content'], 'Artist Name - Album Title - Title of track')
+ self.assertTrue('100' in results[0]['embedded'])
+
+ json = r"""
+ {"data":[
+ {"id":200,"name":"Artist Name",
+ "link":"https:\/\/www.deezer.com\/artist\/1217","type":"artist"}
+ ]}
+ """
+ response = mock.Mock(text=json)
+ results = deezer.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_deviantart.py b/tests/unit/engines/test_deviantart.py
new file mode 100644
index 0000000..bd2cf18
--- /dev/null
+++ b/tests/unit/engines/test_deviantart.py
@@ -0,0 +1,95 @@
+from collections import defaultdict
+import mock
+from searx.engines import deviantart
+from searx.testing import SearxTestCase
+
+
+class TestDeviantartEngine(SearxTestCase):
+
+ def test_request(self):
+ dicto = defaultdict(dict)
+ query = 'test_query'
+ dicto['pageno'] = 0
+ dicto['time_range'] = ''
+ params = deviantart.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('deviantart.com' in params['url'])
+
+ def test_no_url_in_request_year_time_range(self):
+ dicto = defaultdict(dict)
+ query = 'test_query'
+ dicto['time_range'] = 'year'
+ params = deviantart.request(query, dicto)
+ self.assertEqual({}, params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, deviantart.response, None)
+ self.assertRaises(AttributeError, deviantart.response, [])
+ self.assertRaises(AttributeError, deviantart.response, '')
+ self.assertRaises(AttributeError, deviantart.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(deviantart.response(response), [])
+
+ response = mock.Mock(status_code=302)
+ self.assertEqual(deviantart.response(response), [])
+
+ html = """
+ <div id="page-1-results" class="page-results results-page-thumb torpedo-container">
+ <span class="thumb wide" href="http://amai911.deviantart.com/art/Horse-195212845"
+ data-super-full-width="900" data-super-full-height="600">
+ <a class="torpedo-thumb-link" href="https://url.of.image">
+ <img data-sigil="torpedo-img" src="https://url.of.thumbnail" />
+ </a>
+ <span class="info"><span class="title-wrap"><span class="title">Title of image</span></span>
+ </div>
+ """
+ response = mock.Mock(text=html)
+ results = deviantart.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title of image')
+ self.assertEqual(results[0]['url'], 'https://url.of.image')
+ self.assertNotIn('content', results[0])
+ self.assertEqual(results[0]['thumbnail_src'], 'https://url.of.thumbnail')
+
+ html = """
+ <span class="tt-fh-tc" style="width: 202px;">
+ <span class="tt-bb" style="width: 202px;">
+ </span>
+ <span class="shadow">
+ <a class="thumb" href="http://url.of.result/2nd.part.of.url"
+ title="Behoimi BE Animation Test by test-0, Jan 4,
+ 2010 in Digital Art &gt; Animation"> <i></i>
+ <img width="200" height="200" alt="Test"
+ src="http://url.of.thumbnail" data-src="http://th08.deviantart.net/test.jpg">
+ </a>
+ </span>
+ <!-- ^TTT -->
+ </span>
+ <span class="details">
+ <a href="http://test-0.deviantart.com/art/Test" class="t"
+ title="Behoimi BE Animation Test by test-0, Jan 4, 2010">
+ <span class="tt-fh-oe">Title of image</span> </a>
+ <small>
+ <span class="category">
+ <span class="age">
+ 5 years ago
+ </span>
+ in <a title="Behoimi BE Animation Test by test-0, Jan 4, 2010"
+ href="http://www.deviantart.com/browse/all/digitalart/animation/">Animation</a>
+ </span>
+ <div class="commentcount">
+ <a href="http://test-0.deviantart.com/art/Test#comments">
+ <span class="iconcommentsstats"></span>9 Comments</a>
+ </div>
+ <a class="mlt-link" href="http://www.deviantart.com/morelikethis/149167425">
+ <span class="mlt-icon"></span> <span class="mlt-text">More Like This</span> </a>
+ </span>
+ </small> <!-- TTT$ -->
+ """
+ response = mock.Mock(text=html)
+ results = deviantart.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_digbt.py b/tests/unit/engines/test_digbt.py
new file mode 100644
index 0000000..31c2eca
--- /dev/null
+++ b/tests/unit/engines/test_digbt.py
@@ -0,0 +1,61 @@
+from collections import defaultdict
+import mock
+from searx.engines import digbt
+from searx.testing import SearxTestCase
+
+
+class TestDigBTEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = digbt.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('digbt.org', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, digbt.response, None)
+ self.assertRaises(AttributeError, digbt.response, [])
+ self.assertRaises(AttributeError, digbt.response, '')
+ self.assertRaises(AttributeError, digbt.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(digbt.response(response), [])
+
+ html = """
+ <table class="table">
+ <tr><td class="x-item">
+ <div>
+ <a title="The Big Bang Theory" class="title" href="/The-Big-Bang-Theory-d2.html">
+ The Big <span class="highlight">Bang</span> Theory
+ </a>
+ <span class="ctime"><span style="color:red;">4 hours ago</span></span>
+ </div>
+ <div class="files">
+ <ul>
+ <li>The Big Bang Theory 2.9 GB</li>
+ <li>....</li>
+ </ul>
+ </div>
+ <div class="tail">
+ Files: 1 Size: 2.9 GB Downloads: 1 Updated: <span style="color:red;">4 hours ago</span>
+ &nbsp; &nbsp;
+ <a class="title" href="magnet:?xt=urn:btih:a&amp;dn=The+Big+Bang+Theory">
+ <span class="glyphicon glyphicon-magnet"></span> magnet-link
+ </a>
+ &nbsp; &nbsp;
+ </div>
+ </td></tr>
+ </table>
+ """
+ response = mock.Mock(text=html.encode('utf-8'))
+ results = digbt.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'The Big Bang Theory')
+ self.assertEqual(results[0]['url'], 'https://digbt.org/The-Big-Bang-Theory-d2.html')
+ self.assertEqual(results[0]['content'], 'The Big Bang Theory 2.9 GB ....')
+ self.assertEqual(results[0]['filesize'], 3113851289)
+ self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:a&dn=The+Big+Bang+Theory')
diff --git a/tests/unit/engines/test_digg.py b/tests/unit/engines/test_digg.py
new file mode 100644
index 0000000..6e7c9cc
--- /dev/null
+++ b/tests/unit/engines/test_digg.py
@@ -0,0 +1,101 @@
+from collections import defaultdict
+import mock
+from searx.engines import digg
+from searx.testing import SearxTestCase
+
+
+class TestDiggEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = digg.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('digg.com', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, digg.response, None)
+ self.assertRaises(AttributeError, digg.response, [])
+ self.assertRaises(AttributeError, digg.response, '')
+ self.assertRaises(AttributeError, digg.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(digg.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(digg.response(response), [])
+
+ json = """
+ {
+ "status": "ok",
+ "num": 10,
+ "next_position": 20,
+ "html": "<article itemscope itemtype=\\"http://schema.org/Article\\"
+ class=\\"story-container digg-story-el hentry entry story-1sRANah col-1\\"
+ data-content-id=\\"1sRANah\\" data-contenturl=\\"http://url.of.link\\"
+ data-position=\\"0\\" data-diggs=\\"24\\" data-tweets=\\"69\\"
+ data-digg-score=\\"1190\\"> <div class=\\"story-image story-image-thumb\\">
+ <a data-position=\\"0\\" data-content-id=\\"1sRANah\\"
+ class=\\"story-link\\" href=\\"http://www.thedailybeast.com/\\"
+ target=\\"_blank\\"><img class=\\"story-image-img\\"
+ src=\\"http://url.of.image.jpeg\\" width=\\"312\\" height=\\"170\\"
+ alt=\\"\\" /> </a> </div> <div class=\\"story-content\\"><header
+ class=\\"story-header\\"> <div itemprop=\\"alternativeHeadline\\"
+ class=\\"story-kicker\\" >Kicker</div> <h2 itemprop=\\"headline\\"
+ class=\\"story-title entry-title\\"><a class=\\"story-title-link story-link\\"
+ rel=\\"bookmark\\" itemprop=\\"url\\" href=\\"http://www.thedailybeast.com/\\"
+ target=\\"_blank\\">Title of article</h2> <div class=\\"story-meta\\">
+ <div class=\\"story-score \\">
+ <div class=\\"story-score-diggscore diggscore-1sRANah\\">1190</div>
+ <div class=\\"story-score-details\\"> <div class=\\"arrow\\"></div>
+ <ul class=\\"story-score-details-list\\"> <li
+ class=\\"story-score-detail story-score-diggs\\"><span
+ class=\\"label\\">Diggs:</span> <span class=\\"count diggs-1sRANah\\">24</span>
+ </li> <li class=\\"story-score-detail story-score-twitter\\"><span
+ class=\\"label\\">Tweets:</span> <span class=\\"count tweets-1sRANah\\">69</span>
+ </li> <li class=\\"story-score-detail story-score-facebook\\"><span
+ class=\\"label\\">Facebook Shares:</span> <span
+ class=\\"count fb_shares-1sRANah\\">1097</span></li> </ul> </div> </div>
+ <span class=\\"story-meta-item story-source\\"> <a
+ itemprop=\\"publisher copyrightHolder sourceOrganization provider\\"
+ class=\\"story-meta-item-link story-source-link\\"
+ href=\\"/source/thedailybeast.com\\">The Daily Beast </a> </span>
+ <span class=\\"story-meta-item story-tag first-tag\\"> <a
+ itemprop=\\"keywords\\" rel=\\"tag\\"
+ class=\\"story-meta-item-link story-tag-link\\" href=\\"/tag/news\\">News</a>
+ </span> <abbr class=\\"published story-meta-item story-timestamp\\"
+ title=\\"2014-10-18 14:53:45\\"> <time datetime=\\"2014-10-18 14:53:45\\">18 Oct 2014</time>
+ </abbr> </div> </header> </div> <ul class=\\"story-actions\\"> <li
+ class=\\"story-action story-action-digg btn-story-action-container\\">
+ <a class=\\"target digg-1sRANah\\" href=\\"#\\">Digg</a></li> <li
+ class=\\"story-action story-action-save btn-story-action-container\\">
+ <a class=\\"target save-1sRANah\\" href=\\"#\\">Save</a></li> <li
+ class=\\"story-action story-action-share\\"><a
+ class=\\"target share-facebook\\" href=\\"https://www.facebook.com/\\">Facebook</a></li>
+ <li class=\\"story-action story-action-share\\"><a class=\\"target share-twitter\\"
+ href=\\"https://twitter.com/\\">Twitter</a></li> </ul> </article>"
+ }
+ """
+ json = json.replace('\r\n', '').replace('\n', '').replace('\r', '')
+ response = mock.Mock(text=json)
+ results = digg.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title of article')
+ self.assertEqual(results[0]['url'], 'http://url.of.link')
+ self.assertEqual(results[0]['thumbnail'], 'http://url.of.image.jpeg')
+ self.assertEqual(results[0]['content'], '')
+
+ json = """
+ {
+ "status": "error",
+ "num": 10,
+ "next_position": 20
+ }
+ """
+ response = mock.Mock(text=json)
+ results = digg.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_doku.py b/tests/unit/engines/test_doku.py
new file mode 100644
index 0000000..22ddb7a
--- /dev/null
+++ b/tests/unit/engines/test_doku.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import doku
+from searx.testing import SearxTestCase
+
+
+class TestDokuEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ params = doku.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, doku.response, None)
+ self.assertRaises(AttributeError, doku.response, [])
+ self.assertRaises(AttributeError, doku.response, '')
+ self.assertRaises(AttributeError, doku.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(doku.response(response), [])
+
+ html = u"""
+ <div class="search_quickresult">
+ <h3>Pages trouvées :</h3>
+ <ul class="search_quickhits">
+ <li> <a href="/xfconf-query" class="wikilink1" title="xfconf-query">xfconf-query</a></li>
+ </ul>
+ <div class="clearer"></div>
+ </div>
+ """
+ response = mock.Mock(text=html)
+ results = doku.response(response)
+ expected = [{'content': '', 'title': 'xfconf-query', 'url': 'http://localhost:8090/xfconf-query'}]
+ self.assertEqual(doku.response(response), expected)
+
+ html = u"""
+ <dl class="search_results">
+ <dt><a href="/xvnc?s[]=query" class="wikilink1" title="xvnc">xvnc</a>: 40 Occurrences trouvées</dt>
+ <dd>er = /usr/bin/Xvnc
+ server_args = -inetd -<strong class="search_hit">query</strong> localhost -geometry 640x480 ... er = /usr/bin/Xvnc
+ server_args = -inetd -<strong class="search_hit">query</strong> localhost -geometry 800x600 ... er = /usr/bin/Xvnc
+ server_args = -inetd -<strong class="search_hit">query</strong> localhost -geometry 1024x768 ... er = /usr/bin/Xvnc
+ server_args = -inetd -<strong class="search_hit">query</strong> localhost -geometry 1280x1024 -depth 8 -Sec</dd>
+ <dt><a href="/postfix_mysql_tls_sasl_1404?s[]=query"
+ class="wikilink1"
+ title="postfix_mysql_tls_sasl_1404">postfix_mysql_tls_sasl_1404</a>: 14 Occurrences trouvées</dt>
+ <dd>tdepasse
+ hosts = 127.0.0.1
+ dbname = postfix
+ <strong class="search_hit">query</strong> = SELECT goto FROM alias WHERE address='%s' AND a... tdepasse
+ hosts = 127.0.0.1
+ dbname = postfix
+ <strong class="search_hit">query</strong> = SELECT domain FROM domain WHERE domain='%s'
+ #optional <strong class="search_hit">query</strong> to use when relaying for backup MX
+ #<strong class="search_hit">query</strong> = SELECT domain FROM domain WHERE domain='%s' and backupmx =</dd>
+ <dt><a href="/bind9?s[]=query" class="wikilink1" title="bind9">bind9</a>: 12 Occurrences trouvées</dt>
+ <dd> printcmd
+;; Got answer:
+;; -&gt;&gt;HEADER&lt;&lt;- opcode: <strong class="search_hit">QUERY</strong>, status: NOERROR, id: 13427
+;; flags: qr aa rd ra; <strong class="search_hit">QUERY</strong>: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
+
+[...]
+
+;; <strong class="search_hit">Query</strong> time: 1 msec
+;; SERVER: 127.0.0.1#53(127.0.0.1)
+;... par la requête (<strong class="search_hit">Query</strong> time) , entre la première et la deuxième requête.</dd>
+ </dl>
+ """
+ response = mock.Mock(text=html)
+ results = doku.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 3)
+ self.assertEqual(results[0]['title'], 'xvnc')
+# FIXME self.assertEqual(results[0]['url'], u'http://this.should.be.the.link/ű')
+# FIXME self.assertEqual(results[0]['content'], 'This should be the content.')
diff --git a/tests/unit/engines/test_duckduckgo.py b/tests/unit/engines/test_duckduckgo.py
new file mode 100644
index 0000000..eea4789
--- /dev/null
+++ b/tests/unit/engines/test_duckduckgo.py
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import duckduckgo
+from searx.testing import SearxTestCase
+
+
+class TestDuckduckgoEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'de-CH'
+ dicto['time_range'] = ''
+ params = duckduckgo.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('duckduckgo.com', params['url'])
+ self.assertIn('ch-de', params['url'])
+ self.assertIn('s=0', params['url'])
+
+ # when ddg uses non standard code
+ dicto['language'] = 'en-GB'
+ params = duckduckgo.request(query, dicto)
+ self.assertIn('uk-en', params['url'])
+
+ # no country given
+ duckduckgo.supported_languages = ['de-CH', 'en-US']
+ dicto['language'] = 'de'
+ params = duckduckgo.request(query, dicto)
+ self.assertIn('ch-de', params['url'])
+
+ def test_no_url_in_request_year_time_range(self):
+ dicto = defaultdict(dict)
+ query = 'test_query'
+ dicto['time_range'] = 'year'
+ params = duckduckgo.request(query, dicto)
+ self.assertEqual({}, params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, duckduckgo.response, None)
+ self.assertRaises(AttributeError, duckduckgo.response, [])
+ self.assertRaises(AttributeError, duckduckgo.response, '')
+ self.assertRaises(AttributeError, duckduckgo.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(duckduckgo.response(response), [])
+
+ html = u"""
+ <div class="result results_links results_links_deep web-result result--no-result">
+ <div class="links_main links_deep result__body">
+ <h2 class="result__title">
+ </h2>
+ <div class="no-results">No results</div>
+ <div class="result__extras">
+ </div>
+ </div>
+ </div>
+ """
+ response = mock.Mock(text=html)
+ results = duckduckgo.response(response)
+ self.assertEqual(duckduckgo.response(response), [])
+
+ html = u"""
+ <div class="result results_links results_links_deep web-result ">
+ <div class="links_main links_deep result__body">
+ <h2 class="result__title">
+ <a rel="nofollow" class="result__a" href="http://this.should.be.the.link/ű">
+ This <b>is</b> <b>the</b> title
+ </a>
+ </h2>
+ <a class="result__snippet" href="http://this.should.be.the.link/ű">
+ <b>This</b> should be the content.
+ </a>
+ <div class="result__extras">
+ </div>
+ </div>
+ </div>
+ """
+ response = mock.Mock(text=html)
+ results = duckduckgo.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], u'http://this.should.be.the.link/ű')
+ self.assertEqual(results[0]['content'], 'This should be the content.')
+
+ def test_fetch_supported_languages(self):
+ js = """some code...regions:{
+ "wt-wt":"All Results","ar-es":"Argentina","au-en":"Australia","at-de":"Austria","be-fr":"Belgium (fr)"
+ }some more code..."""
+ response = mock.Mock(text=js)
+ languages = list(duckduckgo._fetch_supported_languages(response))
+ self.assertEqual(len(languages), 5)
+ self.assertIn('wt-WT', languages)
+ self.assertIn('es-AR', languages)
+ self.assertIn('en-AU', languages)
+ self.assertIn('de-AT', languages)
+ self.assertIn('fr-BE', languages)
diff --git a/tests/unit/engines/test_duckduckgo_definitions.py b/tests/unit/engines/test_duckduckgo_definitions.py
new file mode 100644
index 0000000..feafe47
--- /dev/null
+++ b/tests/unit/engines/test_duckduckgo_definitions.py
@@ -0,0 +1,254 @@
+from collections import defaultdict
+import mock
+from searx.engines import duckduckgo_definitions
+from searx.testing import SearxTestCase
+
+
+class TestDDGDefinitionsEngine(SearxTestCase):
+
+ def test_result_to_text(self):
+ url = ''
+ text = 'Text'
+ html_result = 'Html'
+ result = duckduckgo_definitions.result_to_text(url, text, html_result)
+ self.assertEqual(result, text)
+
+ html_result = '<a href="url">Text in link</a>'
+ result = duckduckgo_definitions.result_to_text(url, text, html_result)
+ self.assertEqual(result, 'Text in link')
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'es'
+ params = duckduckgo_definitions.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('duckduckgo.com', params['url'])
+ self.assertIn('headers', params)
+ self.assertIn('Accept-Language', params['headers'])
+ self.assertIn('es', params['headers']['Accept-Language'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, duckduckgo_definitions.response, None)
+ self.assertRaises(AttributeError, duckduckgo_definitions.response, [])
+ self.assertRaises(AttributeError, duckduckgo_definitions.response, '')
+ self.assertRaises(AttributeError, duckduckgo_definitions.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(duckduckgo_definitions.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(duckduckgo_definitions.response(response), [])
+
+ json = """
+ {
+ "DefinitionSource": "definition source",
+ "Heading": "heading",
+ "ImageWidth": 0,
+ "RelatedTopics": [
+ {
+ "Result": "Top-level domains",
+ "Icon": {
+ "URL": "",
+ "Height": "",
+ "Width": ""
+ },
+ "FirstURL": "https://first.url",
+ "Text": "text"
+ },
+ {
+ "Topics": [
+ {
+ "Result": "result topic",
+ "Icon": {
+ "URL": "",
+ "Height": "",
+ "Width": ""
+ },
+ "FirstURL": "https://duckduckgo.com/?q=2%2F2",
+ "Text": "result topic text"
+ }
+ ],
+ "Name": "name"
+ }
+ ],
+ "Entity": "Entity",
+ "Type": "A",
+ "Redirect": "",
+ "DefinitionURL": "http://definition.url",
+ "AbstractURL": "https://abstract.url",
+ "Definition": "this is the definition",
+ "AbstractSource": "abstract source",
+ "Infobox": {
+ "content": [
+ {
+ "data_type": "string",
+ "value": "1999",
+ "label": "Introduced",
+ "wiki_order": 0
+ }
+ ],
+ "meta": [
+ {
+ "data_type": "string",
+ "value": ".test",
+ "label": "article_title"
+ }
+ ]
+ },
+ "Image": "image.png",
+ "ImageIsLogo": 0,
+ "Abstract": "abstract",
+ "AbstractText": "abstract text",
+ "AnswerType": "",
+ "ImageHeight": 0,
+ "Results": [{
+ "Result" : "result title",
+ "Icon" : {
+ "URL" : "result url",
+ "Height" : 16,
+ "Width" : 16
+ },
+ "FirstURL" : "result first url",
+ "Text" : "result text"
+ }
+ ],
+ "Answer": "answer"
+ }
+ """
+ response = mock.Mock(text=json)
+ results = duckduckgo_definitions.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 4)
+ self.assertEqual(results[0]['answer'], 'answer')
+ self.assertEqual(results[1]['title'], 'heading')
+ self.assertEqual(results[1]['url'], 'result first url')
+ self.assertEqual(results[2]['suggestion'], 'text')
+ self.assertEqual(results[3]['infobox'], 'heading')
+ self.assertEqual(results[3]['id'], 'https://definition.url')
+ self.assertEqual(results[3]['entity'], 'Entity')
+ self.assertIn('abstract', results[3]['content'])
+ self.assertIn('this is the definition', results[3]['content'])
+ self.assertEqual(results[3]['img_src'], 'image.png')
+ self.assertIn('Introduced', results[3]['attributes'][0]['label'])
+ self.assertIn('1999', results[3]['attributes'][0]['value'])
+ self.assertIn({'url': 'https://abstract.url', 'title': 'abstract source'}, results[3]['urls'])
+ self.assertIn({'url': 'http://definition.url', 'title': 'definition source'}, results[3]['urls'])
+ self.assertIn({'name': 'name', 'suggestions': ['result topic text']}, results[3]['relatedTopics'])
+
+ json = """
+ {
+ "DefinitionSource": "definition source",
+ "Heading": "heading",
+ "ImageWidth": 0,
+ "RelatedTopics": [],
+ "Entity": "Entity",
+ "Type": "A",
+ "Redirect": "",
+ "DefinitionURL": "",
+ "AbstractURL": "https://abstract.url",
+ "Definition": "",
+ "AbstractSource": "abstract source",
+ "Image": "",
+ "ImageIsLogo": 0,
+ "Abstract": "",
+ "AbstractText": "abstract text",
+ "AnswerType": "",
+ "ImageHeight": 0,
+ "Results": [],
+ "Answer": ""
+ }
+ """
+ response = mock.Mock(text=json)
+ results = duckduckgo_definitions.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['url'], 'https://abstract.url')
+ self.assertEqual(results[0]['title'], 'heading')
+ self.assertEqual(results[0]['content'], '')
+
+ json = """
+ {
+ "DefinitionSource": "definition source",
+ "Heading": "heading",
+ "ImageWidth": 0,
+ "RelatedTopics": [
+ {
+ "Result": "Top-level domains",
+ "Icon": {
+ "URL": "",
+ "Height": "",
+ "Width": ""
+ },
+ "FirstURL": "https://first.url",
+ "Text": "heading"
+ },
+ {
+ "Name": "name"
+ },
+ {
+ "Topics": [
+ {
+ "Result": "result topic",
+ "Icon": {
+ "URL": "",
+ "Height": "",
+ "Width": ""
+ },
+ "FirstURL": "https://duckduckgo.com/?q=2%2F2",
+ "Text": "heading"
+ }
+ ],
+ "Name": "name"
+ }
+ ],
+ "Entity": "Entity",
+ "Type": "A",
+ "Redirect": "",
+ "DefinitionURL": "http://definition.url",
+ "AbstractURL": "https://abstract.url",
+ "Definition": "this is the definition",
+ "AbstractSource": "abstract source",
+ "Infobox": {
+ "meta": [
+ {
+ "data_type": "string",
+ "value": ".test",
+ "label": "article_title"
+ }
+ ]
+ },
+ "Image": "image.png",
+ "ImageIsLogo": 0,
+ "Abstract": "abstract",
+ "AbstractText": "abstract text",
+ "AnswerType": "",
+ "ImageHeight": 0,
+ "Results": [{
+ "Result" : "result title",
+ "Icon" : {
+ "URL" : "result url",
+ "Height" : 16,
+ "Width" : 16
+ },
+ "Text" : "result text"
+ }
+ ],
+ "Answer": ""
+ }
+ """
+ response = mock.Mock(text=json)
+ results = duckduckgo_definitions.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['infobox'], 'heading')
+ self.assertEqual(results[0]['id'], 'https://definition.url')
+ self.assertEqual(results[0]['entity'], 'Entity')
+ self.assertIn('abstract', results[0]['content'])
+ self.assertIn('this is the definition', results[0]['content'])
+ self.assertEqual(results[0]['img_src'], 'image.png')
+ self.assertIn({'url': 'https://abstract.url', 'title': 'abstract source'}, results[0]['urls'])
+ self.assertIn({'url': 'http://definition.url', 'title': 'definition source'}, results[0]['urls'])
+ self.assertIn({'name': 'name', 'suggestions': []}, results[0]['relatedTopics'])
diff --git a/tests/unit/engines/test_duckduckgo_images.py b/tests/unit/engines/test_duckduckgo_images.py
new file mode 100644
index 0000000..5f94e3c
--- /dev/null
+++ b/tests/unit/engines/test_duckduckgo_images.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import duckduckgo_images
+from searx.testing import SearxTestCase
+
+
+class TestDuckduckgoImagesEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['is_test'] = True
+ dicto['pageno'] = 1
+ dicto['safesearch'] = 0
+ dicto['language'] = 'all'
+ params = duckduckgo_images.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('duckduckgo.com', params['url'])
+ self.assertIn('s=0', params['url'])
+ self.assertIn('p=-1', params['url'])
+ self.assertIn('vqd=12345', params['url'])
+
+ # test paging and safe search
+ dicto['pageno'] = 2
+ dicto['safesearch'] = 2
+ params = duckduckgo_images.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('s=50', params['url'])
+ self.assertIn('p=1', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, duckduckgo_images.response, None)
+ self.assertRaises(AttributeError, duckduckgo_images.response, [])
+ self.assertRaises(AttributeError, duckduckgo_images.response, '')
+ self.assertRaises(AttributeError, duckduckgo_images.response, '[]')
+
+ response = mock.Mock(text='If this error persists, please let us know: ops@duckduckgo.com')
+ self.assertEqual(duckduckgo_images.response(response), [])
+
+ json = u"""
+ {
+ "query": "test_query",
+ "results": [
+ {
+ "title": "Result 1",
+ "url": "https://site1.url",
+ "thumbnail": "https://thumb1.nail",
+ "image": "https://image1"
+ },
+ {
+ "title": "Result 2",
+ "url": "https://site2.url",
+ "thumbnail": "https://thumb2.nail",
+ "image": "https://image2"
+ }
+ ]
+ }
+ """
+ response = mock.Mock(text=json)
+ results = duckduckgo_images.response(response)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], 'Result 1')
+ self.assertEqual(results[0]['url'], 'https://site1.url')
+ self.assertEqual(results[0]['thumbnail_src'], 'https://thumb1.nail')
+ self.assertEqual(results[0]['img_src'], 'https://image1')
+ self.assertEqual(results[1]['title'], 'Result 2')
+ self.assertEqual(results[1]['url'], 'https://site2.url')
+ self.assertEqual(results[1]['thumbnail_src'], 'https://thumb2.nail')
+ self.assertEqual(results[1]['img_src'], 'https://image2')
diff --git a/tests/unit/engines/test_dummy.py b/tests/unit/engines/test_dummy.py
new file mode 100644
index 0000000..9399bea
--- /dev/null
+++ b/tests/unit/engines/test_dummy.py
@@ -0,0 +1,26 @@
+from searx.engines import dummy
+from searx.testing import SearxTestCase
+
+
+class TestDummyEngine(SearxTestCase):
+
+ def test_request(self):
+ test_params = [
+ [1, 2, 3],
+ ['a'],
+ [],
+ 1
+ ]
+ for params in test_params:
+ self.assertEqual(dummy.request(None, params), params)
+
+ def test_response(self):
+ responses = [
+ None,
+ [],
+ True,
+ dict(),
+ tuple()
+ ]
+ for response in responses:
+ self.assertEqual(dummy.response(response), [])
diff --git a/tests/unit/engines/test_faroo.py b/tests/unit/engines/test_faroo.py
new file mode 100644
index 0000000..acebdda
--- /dev/null
+++ b/tests/unit/engines/test_faroo.py
@@ -0,0 +1,116 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import faroo
+from searx.testing import SearxTestCase
+
+
+class TestFarooEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr_FR'
+ dicto['category'] = 'general'
+ params = faroo.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('faroo.com', params['url'])
+ self.assertIn('en', params['url'])
+ self.assertIn('web', params['url'])
+
+ dicto['language'] = 'all'
+ params = faroo.request(query, dicto)
+ self.assertIn('en', params['url'])
+
+ dicto['language'] = 'de_DE'
+ params = faroo.request(query, dicto)
+ self.assertIn('de', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, faroo.response, None)
+ self.assertRaises(AttributeError, faroo.response, [])
+ self.assertRaises(AttributeError, faroo.response, '')
+ self.assertRaises(AttributeError, faroo.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(faroo.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(faroo.response(response), [])
+
+ response = mock.Mock(text='{"data": []}', status_code=401)
+ self.assertRaises(Exception, faroo.response, response)
+
+ response = mock.Mock(text='{"data": []}', status_code=429)
+ self.assertRaises(Exception, faroo.response, response)
+
+ json = """
+ {
+ "results": [
+ {
+ "title": "This is the title",
+ "kwic": "This is the content",
+ "content": "",
+ "url": "http://this.is.the.url/",
+ "iurl": "",
+ "domain": "css3test.com",
+ "author": "Jim Dalrymple",
+ "news": true,
+ "votes": "10",
+ "date": 1360622563000,
+ "related": []
+ },
+ {
+ "title": "This is the title2",
+ "kwic": "This is the content2",
+ "content": "",
+ "url": "http://this.is.the.url2/",
+ "iurl": "",
+ "domain": "css3test.com",
+ "author": "Jim Dalrymple",
+ "news": false,
+ "votes": "10",
+ "related": []
+ },
+ {
+ "title": "This is the title3",
+ "kwic": "This is the content3",
+ "content": "",
+ "url": "http://this.is.the.url3/",
+ "iurl": "http://upload.wikimedia.org/optimized.jpg",
+ "domain": "css3test.com",
+ "author": "Jim Dalrymple",
+ "news": false,
+ "votes": "10",
+ "related": []
+ }
+ ],
+ "query": "test",
+ "suggestions": [],
+ "count": 100,
+ "start": 1,
+ "length": 10,
+ "time": "15"
+ }
+ """
+ response = mock.Mock(text=json)
+ results = faroo.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 4)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url/')
+ self.assertEqual(results[0]['content'], 'This is the content')
+ self.assertEqual(results[1]['title'], 'This is the title2')
+ self.assertEqual(results[1]['url'], 'http://this.is.the.url2/')
+ self.assertEqual(results[1]['content'], 'This is the content2')
+ self.assertEqual(results[3]['img_src'], 'http://upload.wikimedia.org/optimized.jpg')
+
+ json = """
+ {}
+ """
+ response = mock.Mock(text=json)
+ results = faroo.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_fdroid.py b/tests/unit/engines/test_fdroid.py
new file mode 100644
index 0000000..d75f4f0
--- /dev/null
+++ b/tests/unit/engines/test_fdroid.py
@@ -0,0 +1,49 @@
+import mock
+from collections import defaultdict
+from searx.engines import fdroid
+from searx.testing import SearxTestCase
+
+
+class TestFdroidEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dic = defaultdict(dict)
+ dic['pageno'] = 1
+ params = fdroid.request(query, dic)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('f-droid.org' in params['url'])
+
+ def test_response(self):
+ resp = mock.Mock(text='<html></html>')
+ self.assertEqual(fdroid.response(resp), [])
+
+ html = """
+ <a href="https://google.com/qwerty">
+ <div id="appheader">
+ <div style="float:left;padding-right:10px;">
+ <img src="http://example.com/image.png"
+ style="width:48px;border:none;">
+ </div>
+ <div style="float:right;">
+ <p>Details...</p>
+ </div>
+ <p style="color:#000000;">
+ <span style="font-size:20px;">Sample title</span>
+ <br>
+ Sample content
+ </p>
+ </div>
+ </a>
+ """
+
+ resp = mock.Mock(text=html)
+ results = fdroid.response(resp)
+
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['url'], 'https://google.com/qwerty')
+ self.assertEqual(results[0]['title'], 'Sample title')
+ self.assertEqual(results[0]['content'], 'Sample content')
+ self.assertEqual(results[0]['img_src'], 'http://example.com/image.png')
diff --git a/tests/unit/engines/test_flickr.py b/tests/unit/engines/test_flickr.py
new file mode 100644
index 0000000..be97647
--- /dev/null
+++ b/tests/unit/engines/test_flickr.py
@@ -0,0 +1,142 @@
+from collections import defaultdict
+import mock
+from searx.engines import flickr
+from searx.testing import SearxTestCase
+
+
+class TestFlickrEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = flickr.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('flickr.com' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, flickr.response, None)
+ self.assertRaises(AttributeError, flickr.response, [])
+ self.assertRaises(AttributeError, flickr.response, '')
+ self.assertRaises(AttributeError, flickr.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(flickr.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(flickr.response(response), [])
+
+ json = r"""
+ { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
+ "photo": [
+ { "id": "15751017054", "owner": "66847915@N08",
+ "secret": "69c22afc40", "server": "7285", "farm": 8,
+ "title": "Photo title", "ispublic": 1,
+ "isfriend": 0, "isfamily": 0,
+ "description": { "_content": "Description" },
+ "ownername": "Owner",
+ "url_o": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_9178e0f963_o.jpg",
+ "height_o": "2100", "width_o": "2653",
+ "url_n": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_69c22afc40_n.jpg",
+ "height_n": "253", "width_n": "320",
+ "url_z": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_69c22afc40_z.jpg",
+ "height_z": "507", "width_z": "640" }
+ ] }, "stat": "ok" }
+ """
+ response = mock.Mock(text=json)
+ results = flickr.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Photo title')
+ self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054')
+ self.assertTrue('o.jpg' in results[0]['img_src'])
+ self.assertTrue('n.jpg' in results[0]['thumbnail_src'])
+ self.assertTrue('Owner' in results[0]['author'])
+ self.assertTrue('Description' in results[0]['content'])
+
+ json = r"""
+ { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
+ "photo": [
+ { "id": "15751017054", "owner": "66847915@N08",
+ "secret": "69c22afc40", "server": "7285", "farm": 8,
+ "title": "Photo title", "ispublic": 1,
+ "isfriend": 0, "isfamily": 0,
+ "description": { "_content": "Description" },
+ "ownername": "Owner",
+ "url_z": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_69c22afc40_z.jpg",
+ "height_z": "507", "width_z": "640" }
+ ] }, "stat": "ok" }
+ """
+ response = mock.Mock(text=json)
+ results = flickr.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Photo title')
+ self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054')
+ self.assertTrue('z.jpg' in results[0]['img_src'])
+ self.assertTrue('z.jpg' in results[0]['thumbnail_src'])
+ self.assertTrue('Owner' in results[0]['author'])
+ self.assertTrue('Description' in results[0]['content'])
+
+ json = r"""
+ { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
+ "photo": [
+ { "id": "15751017054", "owner": "66847915@N08",
+ "secret": "69c22afc40", "server": "7285", "farm": 8,
+ "title": "Photo title", "ispublic": 1,
+ "isfriend": 0, "isfamily": 0,
+ "description": { "_content": "Description" },
+ "ownername": "Owner",
+ "url_o": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_9178e0f963_o.jpg",
+ "height_o": "2100", "width_o": "2653" }
+ ] }, "stat": "ok" }
+ """
+ response = mock.Mock(text=json)
+ results = flickr.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Photo title')
+ self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/66847915@N08/15751017054')
+ self.assertTrue('o.jpg' in results[0]['img_src'])
+ self.assertTrue('o.jpg' in results[0]['thumbnail_src'])
+ self.assertTrue('Owner' in results[0]['author'])
+ self.assertTrue('Description' in results[0]['content'])
+
+ json = r"""
+ { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
+ "photo": [
+ { "id": "15751017054", "owner": "66847915@N08",
+ "secret": "69c22afc40", "server": "7285", "farm": 8,
+ "title": "Photo title", "ispublic": 1,
+ "isfriend": 0, "isfamily": 0,
+ "description": { "_content": "Description" },
+ "ownername": "Owner",
+ "url_n": "https:\/\/farm8.staticflickr.com\/7285\/15751017054_69c22afc40_n.jpg",
+ "height_n": "253", "width_n": "320" }
+ ] }, "stat": "ok" }
+ """
+ response = mock.Mock(text=json)
+ results = flickr.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ json = """
+ { "photos": { "page": 1, "pages": "41001", "perpage": 100, "total": "4100032",
+ "toto": [] }, "stat": "ok" }
+ """
+ response = mock.Mock(text=json)
+ results = flickr.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ json = r"""
+ {"toto":[
+ {"id":200,"name":"Artist Name",
+ "link":"http:\/\/www.flickr.com\/artist\/1217","type":"artist"}
+ ]}
+ """
+ response = mock.Mock(text=json)
+ results = flickr.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_flickr_noapi.py b/tests/unit/engines/test_flickr_noapi.py
new file mode 100644
index 0000000..5f8b069
--- /dev/null
+++ b/tests/unit/engines/test_flickr_noapi.py
@@ -0,0 +1,329 @@
+from collections import defaultdict
+import mock
+from searx.engines import flickr_noapi
+from searx.testing import SearxTestCase
+
+
+class TestFlickrNoapiEngine(SearxTestCase):
+
+ def test_build_flickr_url(self):
+ url = flickr_noapi.build_flickr_url("uid", "pid")
+ self.assertIn("uid", url)
+ self.assertIn("pid", url)
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['time_range'] = ''
+ params = flickr_noapi.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('flickr.com', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, flickr_noapi.response, None)
+ self.assertRaises(AttributeError, flickr_noapi.response, [])
+ self.assertRaises(AttributeError, flickr_noapi.response, '')
+ self.assertRaises(AttributeError, flickr_noapi.response, '[]')
+
+ response = mock.Mock(text='"search-photos-lite-models","photos":{},"totalItems":')
+ self.assertEqual(flickr_noapi.response(response), [])
+
+ response = mock.Mock(text='search-photos-lite-models","photos":{"data": []},"totalItems":')
+ self.assertEqual(flickr_noapi.response(response), [])
+
+ # everthing is ok test
+ json = """
+ "search-photos-lite-models","photos":
+ {
+ "_data": [
+ {
+ "_flickrModelRegistry": "photo-lite-models",
+ "title": "This is the title",
+ "username": "Owner",
+ "pathAlias": "klink692",
+ "realname": "Owner",
+ "license": 0,
+ "ownerNsid": "59729010@N00",
+ "canComment": false,
+ "commentCount": 14,
+ "faveCount": 21,
+ "id": "14001294434",
+ "sizes": {
+ "c": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_c.jpg",
+ "width": 541,
+ "height": 800,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_c.jpg",
+ "key": "c"
+ },
+ "h": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_761d32237a_h.jpg",
+ "width": 1081,
+ "height": 1600,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_761d32237a_h.jpg",
+ "key": "h"
+ },
+ "k": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_f145a2c11a_k.jpg",
+ "width": 1383,
+ "height": 2048,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_f145a2c11a_k.jpg",
+ "key": "k"
+ },
+ "l": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_b.jpg",
+ "width": 692,
+ "height": 1024,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_b.jpg",
+ "key": "l"
+ },
+ "m": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777.jpg",
+ "width": 338,
+ "height": 500,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777.jpg",
+ "key": "m"
+ },
+ "n": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_n.jpg",
+ "width": 216,
+ "height": 320,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_n.jpg",
+ "key": "n"
+ },
+ "q": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_q.jpg",
+ "width": 150,
+ "height": 150,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_q.jpg",
+ "key": "q"
+ },
+ "s": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_m.jpg",
+ "width": 162,
+ "height": 240,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_m.jpg",
+ "key": "s"
+ },
+ "sq": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_s.jpg",
+ "width": 75,
+ "height": 75,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_s.jpg",
+ "key": "sq"
+ },
+ "t": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_t.jpg",
+ "width": 68,
+ "height": 100,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_t.jpg",
+ "key": "t"
+ },
+ "z": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_z.jpg",
+ "width": 433,
+ "height": 640,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_z.jpg",
+ "key": "z"
+ }
+ }
+ }
+ ],
+ "fetchedStart": true,
+ "fetchedEnd": false,
+ "totalItems": "4386039"
+ },"totalItems":
+ """
+ json = json.replace('\r\n', '').replace('\n', '').replace('\r', '')
+ response = mock.Mock(text=json)
+ results = flickr_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434')
+ self.assertIn('k.jpg', results[0]['img_src'])
+ self.assertIn('n.jpg', results[0]['thumbnail_src'])
+ self.assertIn('Owner', results[0]['author'])
+
+ # no n size, only the z size
+ json = """
+ "search-photos-lite-models","photos":
+ {
+ "_data": [
+ {
+ "_flickrModelRegistry": "photo-lite-models",
+ "title": "This is the title",
+ "username": "Owner",
+ "pathAlias": "klink692",
+ "realname": "Owner",
+ "license": 0,
+ "ownerNsid": "59729010@N00",
+ "canComment": false,
+ "commentCount": 14,
+ "faveCount": 21,
+ "id": "14001294434",
+ "sizes": {
+ "z": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_z.jpg",
+ "width": 433,
+ "height": 640,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_z.jpg",
+ "key": "z"
+ }
+ }
+ }
+ ],
+ "fetchedStart": true,
+ "fetchedEnd": false,
+ "totalItems": "4386039"
+ },"totalItems":
+ """
+ response = mock.Mock(text=json)
+ results = flickr_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434')
+ self.assertIn('z.jpg', results[0]['img_src'])
+ self.assertIn('z.jpg', results[0]['thumbnail_src'])
+ self.assertIn('Owner', results[0]['author'])
+
+ # no z or n size
+ json = """
+ "search-photos-lite-models","photos":
+ {
+ "_data": [
+ {
+ "_flickrModelRegistry": "photo-lite-models",
+ "title": "This is the title",
+ "username": "Owner",
+ "pathAlias": "klink692",
+ "realname": "Owner",
+ "license": 0,
+ "ownerNsid": "59729010@N00",
+ "canComment": false,
+ "commentCount": 14,
+ "faveCount": 21,
+ "id": "14001294434",
+ "sizes": {
+ "o": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_o.jpg",
+ "width": 433,
+ "height": 640,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_o.jpg",
+ "key": "o"
+ }
+ }
+ }
+ ],
+ "fetchedStart": true,
+ "fetchedEnd": false,
+ "totalItems": "4386039"
+ },"totalItems":
+ """
+ response = mock.Mock(text=json)
+ results = flickr_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'https://www.flickr.com/photos/59729010@N00/14001294434')
+ self.assertIn('o.jpg', results[0]['img_src'])
+ self.assertIn('o.jpg', results[0]['thumbnail_src'])
+ self.assertIn('Owner', results[0]['author'])
+
+ # no image test
+ json = """
+ "search-photos-lite-models","photos":
+ {
+ "_data": [
+ {
+ "_flickrModelRegistry": "photo-lite-models",
+ "title": "This is the title",
+ "username": "Owner",
+ "pathAlias": "klink692",
+ "realname": "Owner",
+ "license": 0,
+ "ownerNsid": "59729010@N00",
+ "canComment": false,
+ "commentCount": 14,
+ "faveCount": 21,
+ "id": "14001294434",
+ "sizes": {
+ }
+ }
+ ],
+ "fetchedStart": true,
+ "fetchedEnd": false,
+ "totalItems": "4386039"
+ },"totalItems":
+ """
+ response = mock.Mock(text=json)
+ results = flickr_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ # null test
+ json = """
+ "search-photos-models","photos":
+ {
+ "_data": [null],
+ "fetchedStart": true,
+ "fetchedEnd": false,
+ "totalItems": "4386039"
+ },"totalItems":
+ """
+ response = mock.Mock(text=json)
+ results = flickr_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ # no ownerNsid test
+ json = """
+ "search-photos-lite-models","photos":
+ {
+ "_data": [
+ {
+ "_flickrModelRegistry": "photo-lite-models",
+ "title": "This is the title",
+ "username": "Owner",
+ "pathAlias": "klink692",
+ "realname": "Owner",
+ "license": 0,
+ "canComment": false,
+ "commentCount": 14,
+ "faveCount": 21,
+ "id": "14001294434",
+ "sizes": {
+ "o": {
+ "displayUrl": "//farm8.staticflickr.com/7246/14001294434_410f653777_o.jpg",
+ "width": 433,
+ "height": 640,
+ "url": "//c4.staticflickr.com/8/7246/14001294434_410f653777_o.jpg",
+ "key": "o"
+ }
+ }
+ }
+ ],
+ "fetchedStart": true,
+ "fetchedEnd": false,
+ "totalItems": "4386039"
+ },"totalItems":
+ """
+ response = mock.Mock(text=json)
+ results = flickr_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ # garbage test
+ json = r"""
+ {"toto":[
+ {"id":200,"name":"Artist Name",
+ "link":"http:\/\/www.flickr.com\/artist\/1217","type":"artist"}
+ ]}
+ """
+ response = mock.Mock(text=json)
+ results = flickr_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_framalibre.py b/tests/unit/engines/test_framalibre.py
new file mode 100644
index 0000000..8509963
--- /dev/null
+++ b/tests/unit/engines/test_framalibre.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import framalibre
+from searx.testing import SearxTestCase
+
+
+class TestFramalibreEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = framalibre.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('framalibre.org' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, framalibre.response, None)
+ self.assertRaises(AttributeError, framalibre.response, [])
+ self.assertRaises(AttributeError, framalibre.response, '')
+ self.assertRaises(AttributeError, framalibre.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(framalibre.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(framalibre.response(response), [])
+
+ html = u"""
+ <div class="nodes-list-row">
+ <div id="node-431"
+ class="node node-logiciel-annuaires node-promoted node-teaser node-teaser node-sheet clearfix nodes-list"
+ about="/content/gogs" typeof="sioc:Item foaf:Document">
+ <header class="media">
+ <div class="media-left">
+ <div class="field field-name-field-logo field-type-image field-label-hidden">
+ <div class="field-items">
+ <div class="field-item even">
+ <a href="/content/gogs">
+ <img class="media-object img-responsive" typeof="foaf:Image"
+ src="https://framalibre.org/sites/default/files/styles/teaser_logo/public/leslogos/gogs-lg.png?itok=rrCxKKBy"
+ width="70" height="70" alt="" />
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="media-body">
+ <h3 class="node-title"><a href="/content/gogs">Gogs</a></h3>
+ <span property="dc:title" content="Gogs" class="rdf-meta element-hidden"></span>
+ <div class="field field-name-field-annuaires field-type-taxonomy-term-reference field-label-hidden">
+ <div class="field-items">
+ <div class="field-item even">
+ <a href="/annuaires/cloudwebapps"
+ typeof="skos:Concept" property="rdfs:label skos:prefLabel"
+ datatype="" class="label label-primary">Cloud/webApps</a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </header>
+ <div class="content">
+ <div class="field field-name-field-votre-appr-ciation field-type-fivestar field-label-hidden">
+ <div class="field-items">
+ <div class="field-item even">
+ </div>
+ </div>
+ </div>
+ <div class="field field-name-body field-type-text-with-summary field-label-hidden">
+ <div class="field-items">
+ <div class="field-item even" property="content:encoded">
+ <p>Gogs est une interface web basée sur git et une bonne alternative à GitHub.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ <footer>
+ <a href="/content/gogs" class="read-more btn btn-default btn-sm">Voir la notice</a>
+ <div class="field field-name-field-lien-officiel field-type-link-field field-label-hidden">
+ <div class="field-items">
+ <div class="field-item even">
+ <a href="https://gogs.io/" target="_blank" title="Voir le site officiel">
+ <span class="glyphicon glyphicon-globe"></span>
+ <span class="sr-only">Lien officiel</span>
+ </a>
+ </div>
+ </div>
+ </div>
+ </footer>
+ </div>
+ </div>
+ """
+ response = mock.Mock(text=html)
+ results = framalibre.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Gogs')
+ self.assertEqual(results[0]['url'],
+ 'https://framalibre.org/content/gogs')
+ self.assertEqual(results[0]['content'],
+ u"Gogs est une interface web basée sur git et une bonne alternative à GitHub.")
diff --git a/tests/unit/engines/test_frinkiac.py b/tests/unit/engines/test_frinkiac.py
new file mode 100644
index 0000000..5ea220c
--- /dev/null
+++ b/tests/unit/engines/test_frinkiac.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import frinkiac
+from searx.testing import SearxTestCase
+
+
+class TestFrinkiacEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ request_dict = defaultdict(dict)
+ params = frinkiac.request(query, request_dict)
+ self.assertTrue('url' in params)
+
+ def test_response(self):
+ self.assertRaises(AttributeError, frinkiac.response, None)
+ self.assertRaises(AttributeError, frinkiac.response, [])
+ self.assertRaises(AttributeError, frinkiac.response, '')
+ self.assertRaises(AttributeError, frinkiac.response, '[]')
+
+ text = """
+[{"Id":770931,
+ "Episode":"S06E18",
+ "Timestamp":534616,
+ "Filename":""},
+ {"Id":1657080,
+ "Episode":"S12E14",
+ "Timestamp":910868,
+ "Filename":""},
+ {"Id":1943753,
+ "Episode":"S14E21",
+ "Timestamp":773439,
+ "Filename":""},
+ {"Id":107835,
+ "Episode":"S02E03",
+ "Timestamp":531709,
+ "Filename":""}]
+ """
+
+ response = mock.Mock(text=text)
+ results = frinkiac.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 4)
+ self.assertEqual(results[0]['title'], u'S06E18')
+ self.assertIn('p=caption', results[0]['url'])
+ self.assertIn('e=S06E18', results[0]['url'])
+ self.assertIn('t=534616', results[0]['url'])
+ self.assertEqual(results[0]['thumbnail_src'], 'https://frinkiac.com/img/S06E18/534616/medium.jpg')
+ self.assertEqual(results[0]['img_src'], 'https://frinkiac.com/img/S06E18/534616.jpg')
diff --git a/tests/unit/engines/test_gigablast.py b/tests/unit/engines/test_gigablast.py
new file mode 100644
index 0000000..6b2d264
--- /dev/null
+++ b/tests/unit/engines/test_gigablast.py
@@ -0,0 +1,119 @@
+from collections import defaultdict
+import mock
+from searx.engines import gigablast
+from searx.testing import SearxTestCase
+
+
+class TestGigablastEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ dicto['safesearch'] = 0
+ dicto['language'] = 'all'
+ params = gigablast.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('gigablast.com' in params['url'])
+ self.assertTrue('xx' in params['url'])
+
+ dicto['language'] = 'en-US'
+ params = gigablast.request(query, dicto)
+ self.assertTrue('en' in params['url'])
+ self.assertFalse('en-US' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, gigablast.response, None)
+ self.assertRaises(AttributeError, gigablast.response, [])
+ self.assertRaises(AttributeError, gigablast.response, '')
+ self.assertRaises(AttributeError, gigablast.response, '[]')
+
+ response = mock.Mock(text='{"results": []}')
+ self.assertEqual(gigablast.response(response), [])
+
+ json = """{"results": [
+ {
+ "title":"South by Southwest 2016",
+ "dmozEntry":{
+ "dmozCatId":1041152,
+ "directCatId":1,
+ "dmozCatStr":"Top: Regional: North America: United States",
+ "dmozTitle":"South by Southwest (SXSW)",
+ "dmozSum":"Annual music, film, and interactive conference.",
+ "dmozAnchor":""
+ },
+ "dmozEntry":{
+ "dmozCatId":763945,
+ "directCatId":1,
+ "dmozCatStr":"Top: Regional: North America: United States",
+ "dmozTitle":"South by Southwest (SXSW)",
+ "dmozSum":"",
+ "dmozAnchor":"www.sxsw.com"
+ },
+ "dmozEntry":{
+ "dmozCatId":761446,
+ "directCatId":1,
+ "dmozCatStr":"Top: Regional: North America: United States",
+ "dmozTitle":"South by Southwest (SXSW)",
+ "dmozSum":"Music, film, and interactive conference and festival.",
+ "dmozAnchor":""
+ },
+ "indirectDmozCatId":1041152,
+ "indirectDmozCatId":763945,
+ "indirectDmozCatId":761446,
+ "contentType":"html",
+ "sum":"This should be the content.",
+ "url":"www.sxsw.com",
+ "hopCount":0,
+ "size":" 102k",
+ "sizeInBytes":104306,
+ "bytesUsedToComputeSummary":70000,
+ "docId":269411794364,
+ "docScore":586571136.000000,
+ "summaryGenTimeMS":12,
+ "summaryTagdbLookupTimeMS":0,
+ "summaryTitleRecLoadTimeMS":1,
+ "site":"www.sxsw.com",
+ "spidered":1452203608,
+ "firstIndexedDateUTC":1444167123,
+ "contentHash32":2170650347,
+ "language":"English",
+ "langAbbr":"en"
+ }
+]}
+ """
+ response = mock.Mock(text=json)
+ results = gigablast.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'South by Southwest 2016')
+ self.assertEqual(results[0]['url'], 'www.sxsw.com')
+ self.assertEqual(results[0]['content'], 'This should be the content.')
+
+ def test_fetch_supported_languages(self):
+ html = """<html></html>"""
+ response = mock.Mock(text=html)
+ results = gigablast._fetch_supported_languages(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ html = """
+ <html>
+ <body>
+ <span id="menu2">
+ <a href="/search?&rxikd=1&qlang=xx"></a>
+ <a href="/search?&rxikd=1&qlang=en"></a>
+ <a href="/search?&rxikd=1&prepend=gblang%3Aen"></a>
+ <a href="/search?&rxikd=1&qlang=zh_"></a>
+ <a href="/search?&rxikd=1&prepend=gblang%3Azh_tw"></a>
+ </span>
+ </body>
+ </html>
+ """
+ response = mock.Mock(text=html)
+ languages = gigablast._fetch_supported_languages(response)
+ self.assertEqual(type(languages), list)
+ self.assertEqual(len(languages), 2)
+ self.assertIn('en', languages)
+ self.assertIn('zh-TW', languages)
diff --git a/tests/unit/engines/test_github.py b/tests/unit/engines/test_github.py
new file mode 100644
index 0000000..460be8c
--- /dev/null
+++ b/tests/unit/engines/test_github.py
@@ -0,0 +1,61 @@
+from collections import defaultdict
+import mock
+from searx.engines import github
+from searx.testing import SearxTestCase
+
+
+class TestGitHubEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ params = github.request(query, defaultdict(dict))
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('github.com' in params['url'])
+ self.assertEqual(params['headers']['Accept'], github.accept_header)
+
+ def test_response(self):
+ self.assertRaises(AttributeError, github.response, None)
+ self.assertRaises(AttributeError, github.response, [])
+ self.assertRaises(AttributeError, github.response, '')
+ self.assertRaises(AttributeError, github.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(github.response(response), [])
+
+ response = mock.Mock(text='{"items": []}')
+ self.assertEqual(github.response(response), [])
+
+ json = """
+ {
+ "items": [
+ {
+ "name": "title",
+ "html_url": "url",
+ "description": ""
+ }
+ ]
+ }
+ """
+ response = mock.Mock(text=json)
+ results = github.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'title')
+ self.assertEqual(results[0]['url'], 'url')
+ self.assertEqual(results[0]['content'], '')
+
+ json = """
+ {
+ "items": [
+ {
+ "name": "title",
+ "html_url": "url",
+ "description": "desc"
+ }
+ ]
+ }
+ """
+ response = mock.Mock(text=json)
+ results = github.response(response)
+ self.assertEqual(results[0]['content'], "desc")
diff --git a/tests/unit/engines/test_google.py b/tests/unit/engines/test_google.py
new file mode 100644
index 0000000..0d56b1e
--- /dev/null
+++ b/tests/unit/engines/test_google.py
@@ -0,0 +1,236 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+import lxml
+from searx.engines import google
+from searx.testing import SearxTestCase
+
+
+class TestGoogleEngine(SearxTestCase):
+
+ def mock_response(self, text):
+ response = mock.Mock(text=text, url='https://www.google.com/search?q=test&start=0&gbv=1&gws_rd=cr')
+ response.search_params = mock.Mock()
+ response.search_params.get = mock.Mock(return_value='www.google.com')
+ return response
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr-FR'
+ dicto['time_range'] = ''
+ params = google.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('google.fr', params['url'])
+ self.assertIn('fr', params['headers']['Accept-Language'])
+
+ dicto['language'] = 'all'
+ params = google.request(query, dicto)
+ self.assertIn('google.com', params['url'])
+ self.assertIn('en', params['headers']['Accept-Language'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, google.response, None)
+ self.assertRaises(AttributeError, google.response, [])
+ self.assertRaises(AttributeError, google.response, '')
+ self.assertRaises(AttributeError, google.response, '[]')
+
+ response = self.mock_response('<html></html>')
+ self.assertEqual(google.response(response), [])
+
+ html = """
+ <div class="g">
+ <h3 class="r">
+ <a href="http://this.should.be.the.link/">
+ <b>This</b> is <b>the</b> title
+ </a>
+ </h3>
+ <div class="s">
+ <div class="kv" style="margin-bottom:2px">
+ <cite>
+ <b>test</b>.psychologies.com/
+ </cite>
+ <div class="_nBb">‎
+ <div style="display:inline" onclick="google.sham(this);" aria-expanded="false"
+ aria-haspopup="true" tabindex="0" data-ved="0CBUQ7B0wAA">
+ <span class="_O0">
+ </span>
+ </div>
+ <div style="display:none" class="am-dropdown-menu" role="menu" tabindex="-1">
+ <ul>
+ <li class="_Ykb">
+ <a class="_Zkb" href="http://www.google.fr/url?url=http://webcache.googleusercontent
+ .com/search%3Fcache:R1Z_4pGXjuIJ:http://test.psychologies.com/">
+ En cache
+ </a>
+ </li>
+ <li class="_Ykb">
+ <a class="_Zkb" href="/search?safe=off&amp;q=related:test.psy.com/">
+ Pages similaires
+ </a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ <span class="st">
+ This should be the content.
+ </span>
+ <br>
+ <div class="osl">‎
+ <a href="http://www.google.fr/url?url=http://test.psychologies.com/tests/">
+ Test Personnalité
+ </a> - ‎
+ <a href="http://www.google.fr/url?url=http://test.psychologies.com/test/">
+ Tests - Moi
+ </a> - ‎
+ <a href="http://www.google.fr/url?url=http://test.psychologies.com/test/tests-couple">
+ Test Couple
+ </a>
+ - ‎
+ <a href="http://www.google.fr/url?url=http://test.psychologies.com/tests/tests-amour">
+ Test Amour
+ </a>
+ </div>
+ </div>
+ </div>
+ <div class="g">
+ <h3 class="r">
+ <a href="http://www.google.com/images?q=toto">
+ <b>This</b>
+ </a>
+ </h3>
+ </div>
+ <div class="g">
+ <h3 class="r">
+ <a href="http://www.google.com/search?q=toto">
+ <b>This</b> is
+ </a>
+ </h3>
+ </div>
+ <div class="g">
+ <h3 class="r">
+ <a href="€">
+ <b>This</b> is <b>the</b>
+ </a>
+ </h3>
+ </div>
+ <div class="g">
+ <h3 class="r">
+ <a href="/url?q=url">
+ <b>This</b> is <b>the</b>
+ </a>
+ </h3>
+ </div>
+ <p class="_Bmc" style="margin:3px 8px">
+ <a href="/search?num=20&amp;safe=off&amp;q=t&amp;revid=1754833769&amp;sa=X&amp;ei=-&amp;ved=">
+ suggestion <b>title</b>
+ </a>
+ </p>
+ """
+ response = self.mock_response(html)
+ results = google.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'http://this.should.be.the.link/')
+ self.assertEqual(results[0]['content'], 'This should be the content.')
+ self.assertEqual(results[1]['suggestion'], 'suggestion title')
+
+ html = """
+ <li class="b_algo" u="0|5109|4755453613245655|UAGjXgIrPH5yh-o5oNHRx_3Zta87f_QO">
+ </li>
+ """
+ response = self.mock_response(html)
+ results = google.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ response = mock.Mock(text='<html></html>', url='https://sorry.google.com')
+ response.search_params = mock.Mock()
+ response.search_params.get = mock.Mock(return_value='www.google.com')
+ self.assertRaises(RuntimeWarning, google.response, response)
+
+ response = mock.Mock(text='<html></html>', url='https://www.google.com/sorry/IndexRedirect')
+ response.search_params = mock.Mock()
+ response.search_params.get = mock.Mock(return_value='www.google.com')
+ self.assertRaises(RuntimeWarning, google.response, response)
+
+ def test_parse_images(self):
+ html = """
+ <li>
+ <div>
+ <a href="http://www.google.com/url?q=http://this.is.the.url/">
+ <img style="margin:3px 0;margin-right:6px;padding:0" height="90"
+ src="https://this.is.the.image/image.jpg" width="60" align="middle" alt="" border="0">
+ </a>
+ </div>
+ </li>
+ """
+ dom = lxml.html.fromstring(html)
+ results = google.parse_images(dom, 'www.google.com')
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url/')
+ self.assertEqual(results[0]['title'], '')
+ self.assertEqual(results[0]['content'], '')
+ self.assertEqual(results[0]['img_src'], 'https://this.is.the.image/image.jpg')
+
+ def test_fetch_supported_languages(self):
+ html = """<html></html>"""
+ response = mock.Mock(text=html)
+ languages = google._fetch_supported_languages(response)
+ self.assertEqual(type(languages), dict)
+ self.assertEqual(len(languages), 0)
+
+ html = u"""
+ <html>
+ <body>
+ <table>
+ <tbody>
+ <tr>
+ <td>
+ <font>
+ <label>
+ <span id="ten">English</span>
+ </label>
+ </font>
+ </td>
+ <td>
+ <font>
+ <label>
+ <span id="tzh-CN">中文 (简体)</span>
+ </label>
+ <label>
+ <span id="tzh-TW">中文 (繁體)</span>
+ </label>
+ </font>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </body>
+ </html>
+ """
+ response = mock.Mock(text=html)
+ languages = google._fetch_supported_languages(response)
+ self.assertEqual(type(languages), dict)
+ self.assertEqual(len(languages), 3)
+
+ self.assertIn('en', languages)
+ self.assertIn('zh-CN', languages)
+ self.assertIn('zh-TW', languages)
+
+ self.assertEquals(type(languages['en']), dict)
+ self.assertEquals(type(languages['zh-CN']), dict)
+ self.assertEquals(type(languages['zh-TW']), dict)
+
+ self.assertIn('name', languages['en'])
+ self.assertIn('name', languages['zh-CN'])
+ self.assertIn('name', languages['zh-TW'])
+
+ self.assertEquals(languages['en']['name'], 'English')
+ self.assertEquals(languages['zh-CN']['name'], u'中文 (简体)')
+ self.assertEquals(languages['zh-TW']['name'], u'中文 (繁體)')
diff --git a/tests/unit/engines/test_google_images.py b/tests/unit/engines/test_google_images.py
new file mode 100644
index 0000000..493741c
--- /dev/null
+++ b/tests/unit/engines/test_google_images.py
@@ -0,0 +1,42 @@
+from collections import defaultdict
+import mock
+from searx.engines import google_images
+from searx.testing import SearxTestCase
+
+
+class TestGoogleImagesEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['safesearch'] = 1
+ dicto['time_range'] = ''
+ params = google_images.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+
+ dicto['safesearch'] = 0
+ params = google_images.request(query, dicto)
+ self.assertNotIn('safe', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, google_images.response, None)
+ self.assertRaises(AttributeError, google_images.response, [])
+ self.assertRaises(AttributeError, google_images.response, '')
+ self.assertRaises(AttributeError, google_images.response, '[]')
+
+ html = r"""
+["rg_s",["dom","\u003Cstyle\u003E.rg_kn,.rg_s{}.rg_bx{display:-moz-inline-box;display:inline-block;margin-top:0;margin-right:12px;margin-bottom:12px;margin-left:0;overflow:hidden;position:relative;vertical-align:top;z-index:1}.rg_meta{display:none}.rg_l{display:inline-block;height:100%;position:absolute;text-decoration:none;width:100%}.rg_l:focus{outline:0}.rg_i{border:0;color:rgba(0,0,0,0);display:block;-webkit-touch-callout:none;}.rg_an,.rg_anbg,.rg_ilm,.rg_ilmbg{right:0;bottom:0;box-sizing:border-box;-moz-box-sizing:border-box;color:#fff;font:normal 11px arial,sans-serif;line-height:100%;white-space:nowrap;width:100%}.rg_anbg,.rg_ilmbg{background:rgba(51,51,51,0.8);margin-left:0;padding:2px 4px;position:absolute}.rg_ilmn{bottom:0;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rg_ilm{display:none}#rg_s.rg_kn .rg_l:focus .rg_ilm{display:block}.rg_kn .rg_bx:hover .rg_ilm,.rg_bx:hover .rg_anbg{display:none}.rg_bx:hover .rg_ilm,.rg_anbg,.rg_kn .rg_bx:hover .rg_anbg{display:block}\u003C\/style\u003E\u003Cdiv eid=\"qlKuV-T3BoqksAHMnaroAw\" id=\"isr_scm_0\" style=\"display:none\"\u003E\u003C\/div\u003E\u003Cdiv data-cei=\"qlKuV-T3BoqksAHMnaroAw\" class=\"rg_add_chunk\"\u003E\u003C!--m--\u003E\u003Cdiv class=\"rg_di rg_bx rg_el ivg-i\" data-ved=\"0ahUKEwjk9PCm-7zOAhUKEiwKHcyOCj0QMwgCKAAwAA\"\u003E\u003Ca jsaction=\"fire.ivg_o;mouseover:str.hmov;mouseout:str.hmou\" class=\"rg_l\" style=\"background:rgb(170,205,240)\"\u003E\u003Cimg data-sz=\"f\" name=\"5eykIeMjmCk7xM:\" src=\"https:\/\/encrypted-tbn0.gstatic.com\/images?q=tbn\" class=\"rg_i rg_ic\" alt=\"Image result for south\" jsaction=\"load:str.tbn\" onload=\"google.aft\u0026\u0026google.aft(this)\"\u003E\u003Cdiv class=\"_aOd rg_ilm\"\u003E\u003Cdiv class=\"rg_ilmbg\"\u003E\u003Cspan class=\"rg_ilmn\"\u003E 566\u0026nbsp;\u0026#215;\u0026nbsp;365 - en.wikipedia.org \u003C\/span\u003E\u003C\/div\u003E\u003C\/div\u003E\u003C\/a\u003E\u003Cdiv class=\"rg_meta\"\u003E{\"id\":\"5eykIeMjmCk7xM:\",\"isu\":\"en.wikipedia.org\",\"itg\":false,\"ity\":\"png\",\"oh\":365,\"ou\":\"https:\/\/upload.wikimedia.org\/wikipedia\/commons\/e\/e4\/Us_south_census.png\",\"ow\":566,\"pt\":\"Southern United States - Wikipedia, the free encyclopedia\",\"rid\":\"cErfE02-v-VcAM\",\"ru\":\"https:\/\/en.wikipedia.org\/wiki\/Southern_United_States\",\"s\":\"The Southern United States as defined by the United States Census Bureau.\",\"sc\":1,\"th\":180,\"tu\":\"https:\/\/encrypted-tbn0.gstatic.com\/images?q\\u003dtbn\",\"tw\":280}\u003C\/div\u003E\u003C\/div\u003E\u003C!--n--\u003E\u003C!--m--\u003E\u003Cdiv class=\"rg_di rg_bx rg_el ivg-i\" data-ved=\"0ahUKEwjk9PCm-7zOAhUKEiwKHcyOCj0QMwgDKAEwAQ\"\u003E\u003Ca jsaction=\"fire.ivg_o;mouseover:str.hmov;mouseout:str.hmou\" class=\"rg_l\" style=\"background:rgb(249,252,249)\"\u003E\u003Cimg data-sz=\"f\" name=\"eRjGCc0cFyVkKM:\" src=\"https:\/\/encrypted-tbn2.gstatic.com\/images?q=tbn:ANd9GcSI7SZlbDwdMCgGXzJkpwgdn9uL41xUJ1IiIcKs0qW43_Yp0EhEsg\" class=\"rg_i rg_ic\" alt=\"Image result for south\" jsaction=\"load:str.tbn\" onload=\"google.aft\u0026\u0026google.aft(this)\"\u003E\u003Cdiv class=\"_aOd rg_ilm\"\u003E\u003Cdiv class=\"rg_ilmbg\"\u003E\u003Cspan class=\"rg_ilmn\"\u003E 2000\u0026nbsp;\u0026#215;\u0026nbsp;1002 - commons.wikimedia.org \u003C\/span\u003E\u003C\/div\u003E\u003C\/div\u003E\u003C\/a\u003E\u003Cdiv class=\"rg_meta\"\u003E{\"id\":\"eRjGCc0cFyVkKM:\",\"isu\":\"commons.wikimedia.org\",\"itg\":false,\"ity\":\"png\",\"oh\":1002,\"ou\":\"https:\/\/upload.wikimedia.org\/wikipedia\/commons\/thumb\/8\/84\/South_plate.svg\/2000px-South_plate.svg.png\",\"ow\":2000,\"pt\":\"File:South plate.svg - Wikimedia Commons\",\"rid\":\"F8TVsT2GBLb6RM\",\"ru\":\"https:\/\/commons.wikimedia.org\/wiki\/File:South_plate.svg\",\"s\":\"This image rendered as PNG in other widths: 200px, 500px, 1000px, 2000px.\",\"sc\":1,\"th\":159,\"tu\":\"https:\/\/encrypted-tbn2.gstatic.com\/images?q\\u003dtbn:ANd9GcSI7SZlbDwdMCgGXzJkpwgdn9uL41xUJ1IiIcKs0qW43_Yp0EhEsg\",\"tw\":317}\u003C\/div\u003E\u003C\/div\u003E\u003C!--n--\u003E\u003C\/div\u003E"]]""" # noqa
+ response = mock.Mock(text=html)
+ results = google_images.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], u'Southern United States - Wikipedia, the free encyclopedia')
+ self.assertEqual(results[0]['url'], 'https://en.wikipedia.org/wiki/Southern_United_States')
+ self.assertEqual(results[0]['img_src'],
+ 'https://upload.wikimedia.org/wikipedia/commons/e/e4/Us_south_census.png')
+ self.assertEqual(results[0]['content'],
+ 'The Southern United States as defined by the United States Census Bureau.')
+ self.assertEqual(results[0]['thumbnail_src'],
+ 'https://encrypted-tbn0.gstatic.com/images?q=tbn')
diff --git a/tests/unit/engines/test_google_news.py b/tests/unit/engines/test_google_news.py
new file mode 100644
index 0000000..6454dde
--- /dev/null
+++ b/tests/unit/engines/test_google_news.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+
+from collections import defaultdict
+import mock
+from searx.engines import google_news
+from searx.testing import SearxTestCase
+
+
+class TestGoogleNewsEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr_FR'
+ dicto['time_range'] = 'w'
+ params = google_news.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('fr', params['url'])
+
+ dicto['language'] = 'all'
+ params = google_news.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertNotIn('fr', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, google_news.response, None)
+ self.assertRaises(AttributeError, google_news.response, [])
+ self.assertRaises(AttributeError, google_news.response, '')
+ self.assertRaises(AttributeError, google_news.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(google_news.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(google_news.response(response), [])
+
+ html = u"""
+<div class="g">
+<div class="ts _V6c _Zmc _XO _knc _d7c"><a class="top _vQb _mnc" href="http://this.is.the.url" onmousedown="return rwt(this,'','','','5','AFQjCNGixEtJGC3qTB9pYFLXlRj8XXwdiA','','0ahUKEwiG7O_M5-rQAhWDtRoKHd0RD5QQvIgBCCwwBA','','',event)"><img class="th _lub" id="news-thumbnail-image-52779299683347" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBwgHBgkIBwgKCgkLDRYPDQwMDRsUFRAWIB0iIiAdHx8kKDQsJCYxJx8fLT0tMTU3Ojo6Iys/RD84QzQ5OjcBCgoKDQwNGg8PGjclHyU3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3N//AABEIAGQAZAMBIgACEQEDEQH/xAAbAAACAwEBAQAAAAAAAAAAAAAFBgADBAIBB//EADsQAAIBAwIEAwUGBAUFAAAAAAECAwAEEQUhBhIxURNBYRQiMnGBFSORobHBB1Ji4UKS0fDxFiQzNYL/xAAYAQEBAQEBAAAAAAAAAAAAAAACAwEEAP/EACERAAMBAAICAgMBAAAAAAAAAAABAhEhMQMSE2EEQVEi/9oADAMBAAIRAxEAPwDSkKr8JFWIzwnMTlSepFdiBgMiuxEdid6B7QbrEr8qE4L4wGNCIT7u/WifE0eIosHBz0oWnugCjQ4BfEipJHbpK/JG0gDP/KO9XT6Lw7BGDHrofIBIJQ4NXalAs8IDJnFLepS21pmOOFXl679FoY66ZTcNwWxtGzaaqwOf8JFcXupSSr4cupSOp6gnGaDQahJESQwU/wBIxXbX0l0jl+VgduYjJrfj/ofc9MluueV1z3xV2jlX1AlDkY61TY3MAk5LuJSnm4G4+nnTFb2ltEweFRvuCO1ebw8uTYNnQ9iDTtDKJYY8DflFJgXLp8xT5FbJHBGx/lFFM9a4MrQFjnFSilvPGiYaItv1zUrTnwHljgLViqwUDyNFEskKlmIDCs08JMWHfl32INZ8hnIscWII4oWPmdqCg9KLccsUtLZM596g6HmVT6Um9WlvGeXk/gWruCObGFz3pb0/R59Y1D2WDYk5lmfpGP3PpW7iK4aIxY3CqWA7t5UxcG3SQWxsZI2VI0MxM6hedj1Oeo9Kc5M6PPagQ3DdjpcpWeHx2U7s+9LuvPCsr+yoETIwAOgpq1DV59RmuJIpbS2hhOG5zvmlLVXE4OCpcnBKdDRW7ptZgMEuCCPrTJwteGYS2z78vvpnt5j/AH3pWIxRbhZ+XV4h5Orj8s/tTtcE5fI6xKWmjVepYfrX0trQrbxLICDyDavm0EvgXEU2M8jhsfI033fHiyxxSfZkwj+EtjP6VzpPR0E8ICQR0qUuT8Y2DSExxtj69alVwOINy3Esye7zKfnXdpZzzMjSfAO1KGm8UX99qNvbm1SNZZApYnoPOimr6rf2crBLmQoTsFIwo8htUHDRk+N0Zv4nQi2itMHALUAiP3SH0oxJrUtyqJcKk4GDiZeb9a0LqkOADY2nz8FaSeLCkxgsWUVrecWQRXwDQW8DzMpGckdNvPr0orxZfpa2if8AYywPIfDkY4YDYHbHbcVgucf9YWd3CqR+MxUgDC5C7bDy2FecRzXQsxJd3lmJN8wCPCvgkZBBOc1Vr2kccaA7PkuBOm6qzD3+XZh1/UflWG+isoGAgMjPzb8x2Jz2r37Sm8AouynqKFyOQxkk8unqa9KYKawolI529CRRLhr/ANxb46AOT/lNBgSTTPwTL7PqMlwpHOkXKuR3IzTvonPY2WqCa6hiPR3VT9TTfx3pzaRb2IskUJICpBPUgUvDXphjLR5HQ8oq6bii9uOUXExmC/CJBzY/GoTWFXOiKV1BZpvEjJYyEkgbb1Kdvt19j9zk9fuk/wBKlL5foz0Zk0vTWa0N/hyVYqiKcZ26/jSvdX81pqET+0PJCX8Nw3l6H5b19H0pccPWpxu2WO/9R3pQ4s0yzi1K4gcFGuW8WN+b3STny8t6xr9lU+MRe2AQVPukZGKq9qTxzCGJcLzbHIqm2uILSCO2uiyvF7pdh1HUNjtQO0uootbLSmQ25kIfwyOYrnyztmvT/oL4C91M3NHIh96GRZB9D+/T61xxNf6XqbBrC2LSbkhVyfypk1Cew1SP7F0exNjbW8LXeovKwMx5VJCs3/0vToT6EUhcHTqOIEfConIyLy+QNX9MWElYOmkYHDKVx5EYrFIrSNk7imriCFrm7kt+TM8R5ie696y6Do41G5naUlLa2TnkxjJ7L9TR3BOdeCzgbg82fntRvR1mtbpGYe5IvUdiP7isrWDSTuIwFIyewA+tbraC7js4J7WVpYz92VUZMTdeUr28wenX1rXyg+rTC7TkHrXonPeiGk6TFeWeNQuWsLot7kkihoXHrj3l+e4oZJbSxzTxY5mgcrJyHmAx57eXrXOVc1Pawt8dq8rPv5VKww+jWUgbhGymTO8QIPod6y8cadHfaQsgws8GWUjrjO4/erXxa8M2cETZCW6bEdNq513UlsbSO6mh9oRXXmh5+UPkk4Jx54NdUrQ08Qj2cQ1ezMDkpexA7k/EBQ+C3ubOVXliICts3l1rXqEr3+qPeWASK7n55vZ7bHhxDGeQf1coJP4dc13Y8SMiPDeRBiRjp19DUaiofBs3NLkKahcwcNaLJaQMtzquspz3jhuYRRE5EYPc9W/4oDDYGyvLO4upLdB4YzFFJmRQq5HMvcj9qtvI7SPXpGguxqFvFysZSvKHON167gdM1a/EUb6fLYz2FvI01yJZrphmVlyCVHYfLue9dTWyRisotfVpLm8D272suB0mURtjtkV3p2qwWF5P7VbmETAK6ZyrD0odrtq+nTiy1PT47Oc4mWWPBflYbA4OMenWsdnd3FusqxOkyMpVkYdR9a5nLXBer2tQyxRWcjznT5CpfDBMbtjoPl6VlN+sCLNbwMZjIysoAGPQj8OtBre5dJeaPKN5ods0aGoW2o2rQ3i/eHYSpsw+dTc4dng/L9J9WjSsK3Og3V/JeQWvhL4cUcr5Zj5j0JGw9aG6Jpst3NDFLcmzNzA8ts2ATIyZ265Xz/vtWbVbOzgQCW4eJ4oT4I8MkSHyUYHmepJAHY0PWGSbT11CFFSGKQwM6EhmYjOTv2OM7darCyeDl/J8r8nk1sIwXDSRK7jDHrnY586lNuh6Pouu6Xb3ZvE06ZV8KaFCFUuvVgD0zsa8oNE9CmpA/YtoRkhrdPptWPidZ5dGuIraETSye6V64jQB3IH82QD8gcb1gvuIDJp9vaW8JVkRYyZN8kDyArBbXx1XXLO2vb2SKI8yyGBSFRSu+MZJzhc/6V1QsYLeo1R3Oh3dnby6RYvDcWcQE1wUVRM3KASQCd/n39aUkhN7rltFJ1muI0OP6mA/ejeqX1jYaxLpOmzmfT4IQgmOMu5PMzbAdwPpS+l37Pq8NzGvM0Myui9yrAgfpXrfIZ6CkmjXWpa9qcPD9s3hQvIyoGGBGrcvmfP96OcI8YaTpelw2mp6MJkR2cThVkEj9dww28umdsVm0HSotb1O6tbK5v8ATp1R2dmcOo94Aqccp8/yoHxBbrpt39lLMswtCweRVwGdt2/ABR9KbWAX2aNX1231DiCXVru3M5kkLmGR/cIxhV+QAH4UHvbqC5lMsFvFa7Y5Yc4P4k0X1vS7XS9E0znwb+6BmkXkX3E8t8ZzuOpxsawXEa6Td3MKrHLJ4JhdiPgYgc2PUbrn51Nz/WPfoxpOcY8QEjbB3q2KUswzjbzWt+t3Ftcw2Hh28cKwRLCzqP8AydMtjA32rnUtG9jiS4srhJ7OT4JUYZ+TDqDQpCVFpmW/g9llYArvGxHwmrNX0v2HQdKkjcxx3HOZk5jhnGMk9+woRFKUO/Uedbprye8tGiZs28Eikcx+FmBG3ocfkKKWcG1zyPv8PLi2Xh3wpbOSQxTuodFHvA4PbuTUpG07XtW0qA29lMYoy3MVx5kD07AVKxzyZphmvZnWViR/KMeQrvQ4lutQm8bJ8G0lmUZ/xKuRn61KldDACoHYzu7MSx6sepo9pFtE/vMMloyT9cj8sV7UoPsS6G7+HX3Wm6jdLvM0ioWPYLn9SaQFka91NHuTzme4Bkz58zb/AK17Uq1dImg5rcrXPGcUU2GSIxqq+QGA36ml+bMkaXEjM0kw8R89yd6lSpiCfCcEV7fXdpcxq8TWUjjujAggr2NXcJ3k08F9pcpDWslpJNyEfA6gEFe29SpQZqAgbmAY9TvXAY5YZOCRUqV79ifRthucRhXgglxsGkTmIHbNSpUrAn//2Q==" alt="A(z) south témájának képe a következőből: CBC.ca" data-deferred="1" onload="google.aft&amp;&amp;google.aft(this)"></a><div class="_cnc"><h3 class="r _U6c"><a class="l _HId" href="http://this.is.the.url" onmousedown="return rwt(this,'','','','5','AFQjCNGixEtJGC3qTB9pYFLXlRj8XXwdiA','','0ahUKEwiG7O_M5-rQAhWDtRoKHd0RD5QQqQIILSgAMAQ','','',event)">Meet Thuli Madonsela — <em>South</em> Africa's conscience</a></h3><div class="slp"><span class="_tQb _IId">CBC.ca</span><span class="_v5">-</span><span class="f nsa _uQb">9 órával ezelőtt</span></div><div class="st"><em>South</em> African Public Protector</div></div><div class="_Xmc card-section"><a class="_sQb" href="http://www.news24.com/Columnists/Mpumelelo_Mkhabela/who-really-governs-south-africa-20161209" onmousedown="return rwt(this,'','','','5','AFQjCNHhc2MnYSZ5T4COqInzvgoju5k5bA','','0ahUKEwiG7O_M5-rQAhWDtRoKHd0RD5QQuogBCC4oATAE','','',event)">Who really governs <em>South</em> Africa?</a><br><span class="_Wmc _GId">Vélemény</span><span class="_v5">-</span><span class="_tQb _IId">News24</span><span class="_v5">-</span><span class="f nsa _uQb">2016. dec. 8.</span></div><div class="_Vmc"></div></div>
+</div>
+ """ # noqa
+ response = mock.Mock(text=html)
+ results = google_news.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], u'Meet Thuli Madonsela \u2014 South Africa\'s conscience')
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url')
+ self.assertEqual(results[0]['content'], 'South African Public Protector')
diff --git a/tests/unit/engines/test_ina.py b/tests/unit/engines/test_ina.py
new file mode 100644
index 0000000..109a959
--- /dev/null
+++ b/tests/unit/engines/test_ina.py
@@ -0,0 +1,64 @@
+from collections import defaultdict
+import mock
+from searx.engines import ina
+from searx.testing import SearxTestCase
+
+
+class TestInaEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = ina.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('ina.fr' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, ina.response, None)
+ self.assertRaises(AttributeError, ina.response, [])
+ self.assertRaises(AttributeError, ina.response, '')
+ self.assertRaises(AttributeError, ina.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(ina.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(ina.response(response), [])
+
+ json = """
+ {"content":"\\t<div class=\\"container\\">\\n\\t\\n\
+ <!-- DEBUT CONTENU PRINCIPAL -->\\n<div class=\\"row\\">\\n\
+ <div class=\\"search-results--list\\"><div class=\\"media\\">\\n\
+ \\t\\t\\t\\t<a class=\\"media-left media-video premium xiti_click_action\\" \
+ data-xiti-params=\\"recherche_v4::resultats_conference_de_presse_du_general_de_gaulle::N\\" \
+ href=\\"\\/video\\/CAF89035682\\/conference-de-presse-du-general-de-gaulle-video.html\\">\\n\
+ <img src=\\"https:\\/\\/www.ina.fr\\/images_v2\\/140x105\\/CAF89035682.jpeg\\" \
+ alt=\\"Conf\\u00e9rence de presse du G\\u00e9n\\u00e9ral de Gaulle \\">\\n\
+ \\t\\t\\t\\t\\t<\\/a>\\n\
+ \\t\\t\\t\\t\\t<div class=\\"media-body\\">\\n\\t\\t\\t\\t\\t\\t<h3 class=\\"h3--title media-heading\\">\\n\
+ \\t\\t\\t\\t\\t\\t\\t<a class=\\"xiti_click_action\\" \
+ data-xiti-params=\\"recherche_v4::resultats_conference_de_presse_du_general_de_gaulle::N\\" \
+ href=\\"\\/video\\/CAF89035682\\/conference-de-presse-du-general-de-gaulle-video.html\\">\
+ Conf\\u00e9rence de presse du G\\u00e9n\\u00e9ral de Gaulle <\\/a>\\n\
+ <\\/h3>\\n\
+ <div class=\\"media-body__info\\">\\n<span class=\\"broadcast\\">27\\/11\\/1967<\\/span>\\n\
+ <span class=\\"views\\">29321 vues<\\/span>\\n\
+ <span class=\\"duration\\">01h 33m 07s<\\/span>\\n\
+ <\\/div>\\n\
+ <p class=\\"media-body__summary\\">VERSION INTEGRALE DE LA CONFERENCE DE PRESSE DU GENERAL DE GAULLE . \
+ - PA le Pr\\u00e9sident DE GAULLE : il ouvre les bras et s'assied. DP journalis...<\\/p>\\n\
+ <\\/div>\\n<\\/div><!-- \\/.media -->\\n"
+ }
+ """
+ response = mock.Mock(text=json)
+ results = ina.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], u'Conf\xe9rence de presse du G\xe9n\xe9ral de Gaulle')
+ self.assertEqual(results[0]['url'],
+ 'https://www.ina.fr/video/CAF89035682/conference-de-presse-du-general-de-gaulle-video.html')
+ self.assertEqual(results[0]['content'],
+ u"VERSION INTEGRALE DE LA CONFERENCE DE PRESSE DU GENERAL DE GAULLE ."
+ u" - PA le Pr\u00e9sident DE GAULLE : il ouvre les bras et s'assied. DP journalis...")
diff --git a/tests/unit/engines/test_kickass.py b/tests/unit/engines/test_kickass.py
new file mode 100644
index 0000000..3a75c66
--- /dev/null
+++ b/tests/unit/engines/test_kickass.py
@@ -0,0 +1,397 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import kickass
+from searx.testing import SearxTestCase
+
+
+class TestKickassEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = kickass.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('kickass.cd', params['url'])
+ self.assertFalse(params['verify'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, kickass.response, None)
+ self.assertRaises(AttributeError, kickass.response, [])
+ self.assertRaises(AttributeError, kickass.response, '')
+ self.assertRaises(AttributeError, kickass.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(kickass.response(response), [])
+
+ html = """
+ <table cellpadding="0" cellspacing="0" class="data" style="width: 100%">
+ <tr class="firstr">
+ <th class="width100perc nopad">torrent name</th>
+ <th class="center">
+ <a href="/search/test/?field=size&sorder=desc" rel="nofollow">size</a>
+ </th>
+ <th class="center"><span class="files">
+ <a href="/search/test/?field=files_count&sorder=desc" rel="nofollow">files</a></span>
+ </th>
+ <th class="center"><span>
+ <a href="/search/test/?field=time_add&sorder=desc" rel="nofollow">age</a></span>
+ </th>
+ <th class="center"><span class="seed">
+ <a href="/search/test/?field=seeders&sorder=desc" rel="nofollow">seed</a></span>
+ </th>
+ <th class="lasttd nobr center">
+ <a href="/search/test/?field=leechers&sorder=desc" rel="nofollow">leech</a>
+ </th>
+ </tr>
+ <tr class="even" id="torrent_test6478745">
+ <td>
+ <div class="iaconbox center floatright">
+ <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
+ <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
+ <i class="ka ka-comment"></i>
+ </a>
+ <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
+ <i class="ka ka16 ka-verify ka-green"></i>
+ </a>
+ <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
+ <i class="ka ka16 ka-arrow-down partner1Button"></i>
+ </a>
+ <a title="Torrent magnet link"
+ href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
+ <i class="ka ka16 ka-magnet"></i>
+ </a>
+ <a title="Download torrent file"
+ href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
+ <i class="ka ka16 ka-arrow-down"></i>
+ </a>
+ </div>
+ <div class="torrentname">
+ <a href="/test-t6478745.html" class="torType txtType"></a>
+ <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
+ <div class="markeredBlock torType txtType">
+ <a href="/url.html" class="cellMainLink">
+ <strong class="red">This should be the title</strong>
+ </a>
+ <span class="font11px lightgrey block">
+ Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
+ <a class="plain" href="/user/riri/">riri</a> in
+ <span id="cat_6478745">
+ <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
+ </span>
+ </span>
+ </div>
+ </td>
+ <td class="nobr center">449 bytes</td>
+ <td class="center">4</td>
+ <td class="center">2&nbsp;years</td>
+ <td class="green center">10</td>
+ <td class="red lasttd center">1</td>
+ </tr>
+ </table>
+ """
+ response = mock.Mock(text=html)
+ results = kickass.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This should be the title')
+ self.assertEqual(results[0]['url'], 'https://kickass.cd/url.html')
+ self.assertEqual(results[0]['content'], 'Posted by riri in Other > Unsorted')
+ self.assertEqual(results[0]['seed'], 10)
+ self.assertEqual(results[0]['leech'], 1)
+ self.assertEqual(results[0]['filesize'], 449)
+ self.assertEqual(results[0]['files'], 4)
+ self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:MAGNETURL&dn=test')
+ self.assertEqual(results[0]['torrentfile'], 'http://torcache.net/torrent/53917.torrent?title=test')
+
+ html = """
+ <table cellpadding="0" cellspacing="0" class="data" style="width: 100%">
+ <tr class="firstr">
+ <th class="width100perc nopad">torrent name</th>
+ <th class="center">
+ <a href="/search/test/?field=size&sorder=desc" rel="nofollow">size</a>
+ </th>
+ <th class="center"><span class="files">
+ <a href="/search/test/?field=files_count&sorder=desc" rel="nofollow">files</a></span>
+ </th>
+ <th class="center"><span>
+ <a href="/search/test/?field=time_add&sorder=desc" rel="nofollow">age</a></span>
+ </th>
+ <th class="center"><span class="seed">
+ <a href="/search/test/?field=seeders&sorder=desc" rel="nofollow">seed</a></span>
+ </th>
+ <th class="lasttd nobr center">
+ <a href="/search/test/?field=leechers&sorder=desc" rel="nofollow">leech</a>
+ </th>
+ </tr>
+ </table>
+ """
+ response = mock.Mock(text=html)
+ results = kickass.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ html = """
+ <table cellpadding="0" cellspacing="0" class="data" style="width: 100%">
+ <tr class="firstr">
+ <th class="width100perc nopad">torrent name</th>
+ <th class="center">
+ <a href="/search/test/?field=size&sorder=desc" rel="nofollow">size</a>
+ </th>
+ <th class="center"><span class="files">
+ <a href="/search/test/?field=files_count&sorder=desc" rel="nofollow">files</a></span>
+ </th>
+ <th class="center"><span>
+ <a href="/search/test/?field=time_add&sorder=desc" rel="nofollow">age</a></span>
+ </th>
+ <th class="center"><span class="seed">
+ <a href="/search/test/?field=seeders&sorder=desc" rel="nofollow">seed</a></span>
+ </th>
+ <th class="lasttd nobr center">
+ <a href="/search/test/?field=leechers&sorder=desc" rel="nofollow">leech</a>
+ </th>
+ </tr>
+ <tr class="even" id="torrent_test6478745">
+ <td>
+ <div class="iaconbox center floatright">
+ <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
+ <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
+ <i class="ka ka-comment"></i>
+ </a>
+ <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
+ <i class="ka ka16 ka-verify ka-green"></i>
+ </a>
+ <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
+ <i class="ka ka16 ka-arrow-down partner1Button"></i>
+ </a>
+ <a title="Torrent magnet link"
+ href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
+ <i class="ka ka16 ka-magnet"></i>
+ </a>
+ <a title="Download torrent file"
+ href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
+ <i class="ka ka16 ka-arrow-down"></i>
+ </a>
+ </div>
+ <div class="torrentname">
+ <a href="/test-t6478745.html" class="torType txtType"></a>
+ <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
+ <div class="markeredBlock torType txtType">
+ <a href="/url.html" class="cellMainLink">
+ <strong class="red">This should be the title</strong>
+ </a>
+ <span class="font11px lightgrey block">
+ Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
+ <a class="plain" href="/user/riri/">riri</a> in
+ <span id="cat_6478745">
+ <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
+ </span>
+ </span>
+ </div>
+ </td>
+ <td class="nobr center">1 KiB</td>
+ <td class="center">4</td>
+ <td class="center">2&nbsp;years</td>
+ <td class="green center">10</td>
+ <td class="red lasttd center">1</td>
+ </tr>
+ <tr class="even" id="torrent_test6478745">
+ <td>
+ <div class="iaconbox center floatright">
+ <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
+ <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
+ <i class="ka ka-comment"></i>
+ </a>
+ <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
+ <i class="ka ka16 ka-verify ka-green"></i>
+ </a>
+ <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
+ <i class="ka ka16 ka-arrow-down partner1Button"></i>
+ </a>
+ <a title="Torrent magnet link"
+ href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
+ <i class="ka ka16 ka-magnet"></i>
+ </a>
+ <a title="Download torrent file"
+ href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
+ <i class="ka ka16 ka-arrow-down"></i>
+ </a>
+ </div>
+ <div class="torrentname">
+ <a href="/test-t6478745.html" class="torType txtType"></a>
+ <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
+ <div class="markeredBlock torType txtType">
+ <a href="/url.html" class="cellMainLink">
+ <strong class="red">This should be the title</strong>
+ </a>
+ <span class="font11px lightgrey block">
+ Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
+ <a class="plain" href="/user/riri/">riri</a> in
+ <span id="cat_6478745">
+ <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
+ </span>
+ </span>
+ </div>
+ </td>
+ <td class="nobr center">1 MiB</td>
+ <td class="center">4</td>
+ <td class="center">2&nbsp;years</td>
+ <td class="green center">9</td>
+ <td class="red lasttd center">1</td>
+ </tr>
+ <tr class="even" id="torrent_test6478745">
+ <td>
+ <div class="iaconbox center floatright">
+ <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
+ <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
+ <i class="ka ka-comment"></i>
+ </a>
+ <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
+ <i class="ka ka16 ka-verify ka-green"></i>
+ </a>
+ <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
+ <i class="ka ka16 ka-arrow-down partner1Button"></i>
+ </a>
+ <a title="Torrent magnet link"
+ href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
+ <i class="ka ka16 ka-magnet"></i>
+ </a>
+ <a title="Download torrent file"
+ href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
+ <i class="ka ka16 ka-arrow-down"></i>
+ </a>
+ </div>
+ <div class="torrentname">
+ <a href="/test-t6478745.html" class="torType txtType"></a>
+ <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
+ <div class="markeredBlock torType txtType">
+ <a href="/url.html" class="cellMainLink">
+ <strong class="red">This should be the title</strong>
+ </a>
+ <span class="font11px lightgrey block">
+ Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
+ <a class="plain" href="/user/riri/">riri</a> in
+ <span id="cat_6478745">
+ <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
+ </span>
+ </span>
+ </div>
+ </td>
+ <td class="nobr center">1 GiB</td>
+ <td class="center">4</td>
+ <td class="center">2&nbsp;years</td>
+ <td class="green center">8</td>
+ <td class="red lasttd center">1</td>
+ </tr>
+ <tr class="even" id="torrent_test6478745">
+ <td>
+ <div class="iaconbox center floatright">
+ <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
+ <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
+ <i class="ka ka-comment"></i>
+ </a>
+ <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
+ <i class="ka ka16 ka-verify ka-green"></i>
+ </a>
+ <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
+ <i class="ka ka16 ka-arrow-down partner1Button"></i>
+ </a>
+ <a title="Torrent magnet link"
+ href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
+ <i class="ka ka16 ka-magnet"></i>
+ </a>
+ <a title="Download torrent file"
+ href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
+ <i class="ka ka16 ka-arrow-down"></i>
+ </a>
+ </div>
+ <div class="torrentname">
+ <a href="/test-t6478745.html" class="torType txtType"></a>
+ <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
+ <div class="markeredBlock torType txtType">
+ <a href="/url.html" class="cellMainLink">
+ <strong class="red">This should be the title</strong>
+ </a>
+ <span class="font11px lightgrey block">
+ Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
+ <a class="plain" href="/user/riri/">riri</a> in
+ <span id="cat_6478745">
+ <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
+ </span>
+ </span>
+ </div>
+ </td>
+ <td class="nobr center">1 TiB</td>
+ <td class="center">4</td>
+ <td class="center">2&nbsp;years</td>
+ <td class="green center">7</td>
+ <td class="red lasttd center">1</td>
+ </tr>
+ <tr class="even" id="torrent_test6478745">
+ <td>
+ <div class="iaconbox center floatright">
+ <a rel="6478745,0" class="icommentjs icon16" href="/test-t6478745.html#comment">
+ <em style="font-size: 12px; margin: 0 4px 0 4px;" class="iconvalue">3</em>
+ <i class="ka ka-comment"></i>
+ </a>
+ <a class="iverify icon16" href="/test-t6478745.html" title="Verified Torrent">
+ <i class="ka ka16 ka-verify ka-green"></i>
+ </a>
+ <a href="#" onclick="_scq.push([]); return false;" class="partner1Button idownload icon16">
+ <i class="ka ka16 ka-arrow-down partner1Button"></i>
+ </a>
+ <a title="Torrent magnet link"
+ href="magnet:?xt=urn:btih:MAGNETURL&dn=test" class="imagnet icon16">
+ <i class="ka ka16 ka-magnet"></i>
+ </a>
+ <a title="Download torrent file"
+ href="http://torcache.net/torrent/53917.torrent?title=test" class="idownload icon16">
+ <i class="ka ka16 ka-arrow-down"></i>
+ </a>
+ </div>
+ <div class="torrentname">
+ <a href="/test-t6478745.html" class="torType txtType"></a>
+ <a href="/test-t6478745.html" class="normalgrey font12px plain bold"></a>
+ <div class="markeredBlock torType txtType">
+ <a href="/url.html" class="cellMainLink">
+ <strong class="red">This should be the title</strong>
+ </a>
+ <span class="font11px lightgrey block">
+ Posted by <i class="ka ka-verify" style="font-size: 16px;color:orange;"></i>
+ <a class="plain" href="/user/riri/">riri</a> in
+ <span id="cat_6478745">
+ <strong><a href="/other/">Other</a> > <a href="/unsorted/">Unsorted</a></strong>
+ </span>
+ </span>
+ </div>
+ </td>
+ <td class="nobr center">z bytes</td>
+ <td class="center">r</td>
+ <td class="center">2&nbsp;years</td>
+ <td class="green center">a</td>
+ <td class="red lasttd center">t</td>
+ </tr>
+ </table>
+ """
+ response = mock.Mock(text=html)
+ results = kickass.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 5)
+ self.assertEqual(results[0]['title'], 'This should be the title')
+ self.assertEqual(results[0]['url'], 'https://kickass.cd/url.html')
+ self.assertEqual(results[0]['content'], 'Posted by riri in Other > Unsorted')
+ self.assertEqual(results[0]['seed'], 10)
+ self.assertEqual(results[0]['leech'], 1)
+ self.assertEqual(results[0]['files'], 4)
+ self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:MAGNETURL&dn=test')
+ self.assertEqual(results[0]['torrentfile'], 'http://torcache.net/torrent/53917.torrent?title=test')
+ self.assertEqual(results[0]['filesize'], 1000)
+ self.assertEqual(results[1]['filesize'], 1000000)
+ self.assertEqual(results[2]['filesize'], 1000000000)
+ self.assertEqual(results[3]['filesize'], 1000000000000)
+ self.assertEqual(results[4]['seed'], 0)
+ self.assertEqual(results[4]['leech'], 0)
+ self.assertEqual(results[4]['files'], None)
+ self.assertEqual(results[4]['filesize'], None)
diff --git a/tests/unit/engines/test_mediawiki.py b/tests/unit/engines/test_mediawiki.py
new file mode 100644
index 0000000..b863727
--- /dev/null
+++ b/tests/unit/engines/test_mediawiki.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import mediawiki
+from searx.testing import SearxTestCase
+
+
+class TestMediawikiEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr_FR'
+ params = mediawiki.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('wikipedia.org', params['url'])
+ self.assertIn('fr', params['url'])
+
+ dicto['language'] = 'all'
+ params = mediawiki.request(query, dicto)
+ self.assertIn('en', params['url'])
+
+ mediawiki.base_url = "http://test.url/"
+ mediawiki.search_url = mediawiki.base_url +\
+ 'w/api.php?action=query'\
+ '&list=search'\
+ '&{query}'\
+ '&srprop=timestamp'\
+ '&format=json'\
+ '&sroffset={offset}'\
+ '&srlimit={limit}' # noqa
+ params = mediawiki.request(query, dicto)
+ self.assertIn('test.url', params['url'])
+
+ def test_response(self):
+ dicto = defaultdict(dict)
+ dicto['language'] = 'fr'
+ mediawiki.base_url = "https://{language}.wikipedia.org/"
+
+ self.assertRaises(AttributeError, mediawiki.response, None)
+ self.assertRaises(AttributeError, mediawiki.response, [])
+ self.assertRaises(AttributeError, mediawiki.response, '')
+ self.assertRaises(AttributeError, mediawiki.response, '[]')
+
+ response = mock.Mock(text='{}', search_params=dicto)
+ self.assertEqual(mediawiki.response(response), [])
+
+ response = mock.Mock(text='{"data": []}', search_params=dicto)
+ self.assertEqual(mediawiki.response(response), [])
+
+ json = """
+ {
+ "query-continue": {
+ "search": {
+ "sroffset": 1
+ }
+ },
+ "query": {
+ "searchinfo": {
+ "totalhits": 29721
+ },
+ "search": [
+ {
+ "ns": 0,
+ "title": "This is the title étude",
+ "timestamp": "2014-12-19T17:42:52Z"
+ }
+ ]
+ }
+ }
+ """
+ response = mock.Mock(text=json, search_params=dicto)
+ results = mediawiki.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], u'This is the title étude')
+ self.assertIn('fr.wikipedia.org', results[0]['url'])
+ self.assertIn('This_is_the_title', results[0]['url'])
+ self.assertIn('%C3%A9tude', results[0]['url'])
+ self.assertEqual(results[0]['content'], '')
+
+ json = """
+ {
+ "query-continue": {
+ "search": {
+ "sroffset": 1
+ }
+ },
+ "query": {
+ "searchinfo": {
+ "totalhits": 29721
+ },
+ "search": [
+ ]
+ }
+ }
+ """
+ response = mock.Mock(text=json, search_params=dicto)
+ results = mediawiki.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ json = """
+ {
+ "query-continue": {
+ "search": {
+ "sroffset": 1
+ }
+ },
+ "query": {
+ }
+ }
+ """
+ response = mock.Mock(text=json, search_params=dicto)
+ results = mediawiki.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ json = r"""
+ {"toto":[
+ {"id":200,"name":"Artist Name",
+ "link":"http:\/\/www.mediawiki.com\/artist\/1217","type":"artist"}
+ ]}
+ """
+ response = mock.Mock(text=json, search_params=dicto)
+ results = mediawiki.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_mixcloud.py b/tests/unit/engines/test_mixcloud.py
new file mode 100644
index 0000000..9c79a47
--- /dev/null
+++ b/tests/unit/engines/test_mixcloud.py
@@ -0,0 +1,67 @@
+from collections import defaultdict
+import mock
+from searx.engines import mixcloud
+from searx.testing import SearxTestCase
+
+
+class TestMixcloudEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = mixcloud.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('mixcloud.com' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, mixcloud.response, None)
+ self.assertRaises(AttributeError, mixcloud.response, [])
+ self.assertRaises(AttributeError, mixcloud.response, '')
+ self.assertRaises(AttributeError, mixcloud.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(mixcloud.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(mixcloud.response(response), [])
+
+ json = """
+ {"data":[
+ {
+ "user": {
+ "url": "http://www.mixcloud.com/user/",
+ "username": "user",
+ "name": "User",
+ "key": "/user/"
+ },
+ "key": "/user/this-is-the-url/",
+ "created_time": "2014-11-14T13:30:02Z",
+ "audio_length": 3728,
+ "slug": "this-is-the-url",
+ "name": "Title of track",
+ "url": "http://www.mixcloud.com/user/this-is-the-url/",
+ "updated_time": "2014-11-14T13:14:10Z"
+ }
+ ]}
+ """
+ response = mock.Mock(text=json)
+ results = mixcloud.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title of track')
+ self.assertEqual(results[0]['url'], 'http://www.mixcloud.com/user/this-is-the-url/')
+ self.assertEqual(results[0]['content'], 'User')
+ self.assertTrue('http://www.mixcloud.com/user/this-is-the-url/' in results[0]['embedded'])
+
+ json = r"""
+ {"toto":[
+ {"id":200,"name":"Artist Name",
+ "link":"http:\/\/www.mixcloud.com\/artist\/1217","type":"artist"}
+ ]}
+ """
+ response = mock.Mock(text=json)
+ results = mixcloud.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_nyaa.py b/tests/unit/engines/test_nyaa.py
new file mode 100644
index 0000000..db412e1
--- /dev/null
+++ b/tests/unit/engines/test_nyaa.py
@@ -0,0 +1,66 @@
+from collections import defaultdict
+import mock
+from searx.engines import nyaa
+from searx.testing import SearxTestCase
+
+
+class TestNyaaEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dic = defaultdict(dict)
+ dic['pageno'] = 1
+ params = nyaa.request(query, dic)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('nyaa.se' in params['url'])
+
+ def test_response(self):
+ resp = mock.Mock(text='<html></html>')
+ self.assertEqual(nyaa.response(resp), [])
+
+ html = """
+ <table class="tlist">
+ <tbody>
+ <tr class="trusted tlistrow">
+ <td class="tlisticon">
+ <a href="//www.nyaa.se" title="English-translated Anime">
+ <img src="//files.nyaa.se" alt="English-translated Anime">
+ </a>
+ </td>
+ <td class="tlistname">
+ <a href="//www.nyaa.se/?page3">
+ Sample torrent title
+ </a>
+ </td>
+ <td class="tlistdownload">
+ <a href="//www.nyaa.se/?page_dl" title="Download">
+ <img src="//files.nyaa.se/www-dl.png" alt="DL">
+ </a>
+ </td>
+ <td class="tlistsize">10 MiB</td>
+ <td class="tlistsn">1</td>
+ <td class="tlistln">3</td>
+ <td class="tlistdn">666</td>
+ <td class="tlistmn">0</td>
+ </tr>
+ </tbody>
+ </table>
+ """
+
+ resp = mock.Mock(text=html)
+ results = nyaa.response(resp)
+
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+
+ r = results[0]
+ self.assertTrue(r['url'].find('www.nyaa.se/?page3') >= 0)
+ self.assertTrue(r['torrentfile'].find('www.nyaa.se/?page_dl') >= 0)
+ self.assertTrue(r['content'].find('English-translated Anime') >= 0)
+ self.assertTrue(r['content'].find('Downloaded 666 times.') >= 0)
+
+ self.assertEqual(r['title'], 'Sample torrent title')
+ self.assertEqual(r['seed'], 1)
+ self.assertEqual(r['leech'], 3)
+ self.assertEqual(r['filesize'], 10 * 1024 * 1024)
diff --git a/tests/unit/engines/test_openstreetmap.py b/tests/unit/engines/test_openstreetmap.py
new file mode 100644
index 0000000..7b7783f
--- /dev/null
+++ b/tests/unit/engines/test_openstreetmap.py
@@ -0,0 +1,199 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import openstreetmap
+from searx.testing import SearxTestCase
+
+
+class TestOpenstreetmapEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = openstreetmap.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('openstreetmap.org', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, openstreetmap.response, None)
+ self.assertRaises(AttributeError, openstreetmap.response, [])
+ self.assertRaises(AttributeError, openstreetmap.response, '')
+ self.assertRaises(AttributeError, openstreetmap.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(openstreetmap.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(openstreetmap.response(response), [])
+
+ json = """
+ [
+ {
+ "place_id": "127732055",
+ "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright",
+ "osm_type": "relation",
+ "osm_id": "7444",
+ "boundingbox": [
+ "48.8155755",
+ "48.902156",
+ "2.224122",
+ "2.4697602"
+ ],
+ "lat": "48.8565056",
+ "lon": "2.3521334",
+ "display_name": "This is the title",
+ "class": "place",
+ "type": "city",
+ "importance": 0.96893459932191,
+ "icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
+ "address": {
+ "city": "Paris",
+ "county": "Paris",
+ "state": "Île-de-France",
+ "country": "France",
+ "country_code": "fr"
+ },
+ "geojson": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 2.224122,
+ 48.854199
+ ]
+ ]
+ ]
+ }
+ }
+ ]
+ """
+ response = mock.Mock(text=json)
+ results = openstreetmap.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'https://openstreetmap.org/relation/7444')
+ self.assertIn('coordinates', results[0]['geojson'])
+ self.assertEqual(results[0]['geojson']['coordinates'][0][0][0], 2.224122)
+ self.assertEqual(results[0]['geojson']['coordinates'][0][0][1], 48.854199)
+ self.assertEqual(results[0]['address'], None)
+ self.assertIn('48.8155755', results[0]['boundingbox'])
+ self.assertIn('48.902156', results[0]['boundingbox'])
+ self.assertIn('2.224122', results[0]['boundingbox'])
+ self.assertIn('2.4697602', results[0]['boundingbox'])
+
+ json = """
+ [
+ {
+ "place_id": "127732055",
+ "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright",
+ "osm_type": "relation",
+ "osm_id": "7444",
+ "boundingbox": [
+ "48.8155755",
+ "48.902156",
+ "2.224122",
+ "2.4697602"
+ ],
+ "lat": "48.8565056",
+ "lon": "2.3521334",
+ "display_name": "This is the title",
+ "class": "tourism",
+ "type": "city",
+ "importance": 0.96893459932191,
+ "icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
+ "address": {
+ "city": "Paris",
+ "county": "Paris",
+ "state": "Île-de-France",
+ "country": "France",
+ "country_code": "fr",
+ "address29": "Address"
+ },
+ "geojson": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 2.224122,
+ 48.854199
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "place_id": "127732055",
+ "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright",
+ "osm_type": "relation",
+ "osm_id": "7444",
+ "boundingbox": [
+ "48.8155755",
+ "48.902156",
+ "2.224122",
+ "2.4697602"
+ ],
+ "lat": "48.8565056",
+ "lon": "2.3521334",
+ "display_name": "This is the title",
+ "class": "tourism",
+ "type": "city",
+ "importance": 0.96893459932191,
+ "icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
+ "address": {
+ "city": "Paris",
+ "county": "Paris",
+ "state": "Île-de-France",
+ "country": "France",
+ "postcode": 75000,
+ "country_code": "fr"
+ },
+ "geojson": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 2.224122,
+ 48.854199
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "place_id": "127732055",
+ "licence": "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright",
+ "osm_type": "node",
+ "osm_id": "7444",
+ "boundingbox": [
+ "48.8155755",
+ "48.902156",
+ "2.224122",
+ "2.4697602"
+ ],
+ "lat": "48.8565056",
+ "lon": "2.3521334",
+ "display_name": "This is the title",
+ "class": "tourism",
+ "type": "city",
+ "importance": 0.96893459932191,
+ "icon": "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png",
+ "address": {
+ "city": "Paris",
+ "county": "Paris",
+ "state": "Île-de-France",
+ "country": "France",
+ "country_code": "fr",
+ "address29": "Address"
+ }
+ }
+ ]
+ """
+ response = mock.Mock(text=json)
+ results = openstreetmap.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 3)
+ self.assertIn('48.8565056', results[2]['geojson']['coordinates'])
+ self.assertIn('2.3521334', results[2]['geojson']['coordinates'])
diff --git a/tests/unit/engines/test_pdbe.py b/tests/unit/engines/test_pdbe.py
new file mode 100644
index 0000000..7aa8e26
--- /dev/null
+++ b/tests/unit/engines/test_pdbe.py
@@ -0,0 +1,109 @@
+import mock
+from collections import defaultdict
+from searx.engines import pdbe
+from searx.testing import SearxTestCase
+
+
+class TestPdbeEngine(SearxTestCase):
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ params = pdbe.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue('ebi.ac.uk' in params['url'])
+ self.assertTrue('data' in params)
+ self.assertTrue('q' in params['data'])
+ self.assertTrue(query in params['data']['q'])
+ self.assertTrue('wt' in params['data'])
+ self.assertTrue('json' in params['data']['wt'])
+ self.assertTrue('method' in params)
+ self.assertTrue(params['method'] == 'POST')
+
+ def test_response(self):
+ self.assertRaises(AttributeError, pdbe.response, None)
+ self.assertRaises(AttributeError, pdbe.response, [])
+ self.assertRaises(AttributeError, pdbe.response, '')
+ self.assertRaises(AttributeError, pdbe.response, '[]')
+
+ json = """
+{
+ "response": {
+ "docs": [
+ {
+ "citation_title": "X-ray crystal structure of ferric Aplysia limacina myoglobin in different liganded states.",
+ "citation_year": 1993,
+ "entry_author_list": [
+ "Conti E, Moser C, Rizzi M, Mattevi A, Lionetti C, Coda A, Ascenzi P, Brunori M, Bolognesi M"
+ ],
+ "journal": "J. Mol. Biol.",
+ "journal_page": "498-508",
+ "journal_volume": "233",
+ "pdb_id": "2fal",
+ "status": "REL",
+ "title": "X-RAY CRYSTAL STRUCTURE OF FERRIC APLYSIA LIMACINA MYOGLOBIN IN DIFFERENT LIGANDED STATES"
+ }
+ ],
+ "numFound": 1,
+ "start": 0
+ },
+ "responseHeader": {
+ "QTime": 0,
+ "params": {
+ "q": "2fal",
+ "wt": "json"
+ },
+ "status": 0
+ }
+}
+"""
+
+ response = mock.Mock(text=json)
+ results = pdbe.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'],
+ 'X-RAY CRYSTAL STRUCTURE OF FERRIC APLYSIA LIMACINA MYOGLOBIN IN DIFFERENT LIGANDED STATES')
+ self.assertEqual(results[0]['url'], pdbe.pdbe_entry_url.format(pdb_id='2fal'))
+ self.assertEqual(results[0]['img_src'], pdbe.pdbe_preview_url.format(pdb_id='2fal'))
+ self.assertTrue('Conti E' in results[0]['content'])
+ self.assertTrue('X-ray crystal structure of ferric Aplysia limacina myoglobin in different liganded states.' in
+ results[0]['content'])
+ self.assertTrue('1993' in results[0]['content'])
+
+ # Testing proper handling of PDB entries marked as obsolete
+ json = """
+{
+ "response": {
+ "docs": [
+ {
+ "citation_title": "Obsolete entry test",
+ "citation_year": 2016,
+ "entry_author_list": ["Doe J"],
+ "journal": "J. Obs.",
+ "journal_page": "1-2",
+ "journal_volume": "1",
+ "pdb_id": "xxxx",
+ "status": "OBS",
+ "title": "OBSOLETE ENTRY TEST",
+ "superseded_by": "yyyy"
+ }
+ ],
+ "numFound": 1,
+ "start": 0
+ },
+ "responseHeader": {
+ "QTime": 0,
+ "params": {
+ "q": "xxxx",
+ "wt": "json"
+ },
+ "status": 0
+ }
+}
+"""
+ response = mock.Mock(text=json)
+ results = pdbe.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'OBSOLETE ENTRY TEST&nbsp;(OBSOLETE)')
+ self.assertTrue(results[0]['content'].startswith('<em>This entry has been superseded by'))
diff --git a/tests/unit/engines/test_photon.py b/tests/unit/engines/test_photon.py
new file mode 100644
index 0000000..7344978
--- /dev/null
+++ b/tests/unit/engines/test_photon.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import photon
+from searx.testing import SearxTestCase
+
+
+class TestPhotonEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'all'
+ params = photon.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('photon.komoot.de', params['url'])
+
+ dicto['language'] = 'all'
+ params = photon.request(query, dicto)
+ self.assertNotIn('lang', params['url'])
+
+ dicto['language'] = 'al'
+ params = photon.request(query, dicto)
+ self.assertNotIn('lang', params['url'])
+
+ dicto['language'] = 'fr'
+ params = photon.request(query, dicto)
+ self.assertIn('fr', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, photon.response, None)
+ self.assertRaises(AttributeError, photon.response, [])
+ self.assertRaises(AttributeError, photon.response, '')
+ self.assertRaises(AttributeError, photon.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(photon.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(photon.response(response), [])
+
+ json = """
+ {
+ "features": [
+ {
+ "properties": {
+ "osm_key": "waterway",
+ "extent": [
+ -1.4508446,
+ 51.1614997,
+ -1.4408036,
+ 51.1525635
+ ],
+ "name": "This is the title",
+ "state": "England",
+ "osm_id": 114823817,
+ "osm_type": "W",
+ "osm_value": "river",
+ "city": "Test Valley",
+ "country": "United Kingdom"
+ },
+ "type": "Feature",
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ -1.4458571,
+ 51.1576661
+ ]
+ }
+ },
+ {
+ "properties": {
+ "osm_key": "place",
+ "street": "Rue",
+ "state": "Ile-de-France",
+ "osm_id": 129211377,
+ "osm_type": "R",
+ "housenumber": "10",
+ "postcode": "75011",
+ "osm_value": "house",
+ "city": "Paris",
+ "country": "France"
+ },
+ "type": "Feature",
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 2.3725025,
+ 48.8654481
+ ]
+ }
+ },
+ {
+ "properties": {
+ "osm_key": "amenity",
+ "street": "Allée",
+ "name": "Bibliothèque",
+ "state": "Ile-de-France",
+ "osm_id": 1028573132,
+ "osm_type": "N",
+ "postcode": "75001",
+ "osm_value": "library",
+ "city": "Paris",
+ "country": "France"
+ },
+ "type": "Feature",
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 2.3445634,
+ 48.862494
+ ]
+ }
+ },
+ {
+ "properties": {
+ "osm_key": "amenity",
+ "osm_id": 1028573132,
+ "osm_type": "Y",
+ "postcode": "75001",
+ "osm_value": "library",
+ "city": "Paris",
+ "country": "France"
+ },
+ "type": "Feature",
+ "geometry": {
+ "type": "Point",
+ "coordinates": [
+ 2.3445634,
+ 48.862494
+ ]
+ }
+ },
+ {
+ }
+ ],
+ "type": "FeatureCollection"
+ }
+ """
+ response = mock.Mock(text=json)
+ results = photon.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 3)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['content'], '')
+ self.assertEqual(results[0]['longitude'], -1.4458571)
+ self.assertEqual(results[0]['latitude'], 51.1576661)
+ self.assertIn(-1.4508446, results[0]['boundingbox'])
+ self.assertIn(51.1614997, results[0]['boundingbox'])
+ self.assertIn(-1.4408036, results[0]['boundingbox'])
+ self.assertIn(51.1525635, results[0]['boundingbox'])
+ self.assertIn('type', results[0]['geojson'])
+ self.assertEqual(results[0]['geojson']['type'], 'Point')
+ self.assertEqual(results[0]['address'], None)
+ self.assertEqual(results[0]['osm']['type'], 'way')
+ self.assertEqual(results[0]['osm']['id'], 114823817)
+ self.assertEqual(results[0]['url'], 'https://openstreetmap.org/way/114823817')
+ self.assertEqual(results[1]['osm']['type'], 'relation')
+ self.assertEqual(results[2]['address']['name'], u'Bibliothèque')
+ self.assertEqual(results[2]['address']['house_number'], None)
+ self.assertEqual(results[2]['address']['locality'], 'Paris')
+ self.assertEqual(results[2]['address']['postcode'], '75001')
+ self.assertEqual(results[2]['address']['country'], 'France')
+ self.assertEqual(results[2]['osm']['type'], 'node')
diff --git a/tests/unit/engines/test_piratebay.py b/tests/unit/engines/test_piratebay.py
new file mode 100644
index 0000000..5699380
--- /dev/null
+++ b/tests/unit/engines/test_piratebay.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import piratebay
+from searx.testing import SearxTestCase
+
+
+class TestPiratebayEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['category'] = 'Toto'
+ params = piratebay.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('piratebay.se', params['url'])
+ self.assertIn('0', params['url'])
+
+ dicto['category'] = 'music'
+ params = piratebay.request(query, dicto)
+ self.assertIn('100', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, piratebay.response, None)
+ self.assertRaises(AttributeError, piratebay.response, [])
+ self.assertRaises(AttributeError, piratebay.response, '')
+ self.assertRaises(AttributeError, piratebay.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(piratebay.response(response), [])
+
+ html = """
+ <table id="searchResult">
+ <tr>
+ </tr>
+ <tr>
+ <td class="vertTh">
+ <center>
+ <a href="#" title="More from this category">Anime</a><br/>
+ (<a href="#" title="More from this category">Anime</a>)
+ </center>
+ </td>
+ <td>
+ <div class="detName">
+ <a href="/this.is.the.link" class="detLink" title="Title">
+ This is the title
+ </a>
+ </div>
+ <a href="magnet:?xt=urn:btih:MAGNETLINK" title="Download this torrent using magnet">
+ <img src="/static/img/icon-magnet.gif" alt="Magnet link"/>
+ </a>
+ <a href="http://torcache.net/torrent/TORRENTFILE.torrent" title="Download this torrent">
+ <img src="/static/img/dl.gif" class="dl" alt="Download"/>
+ </a>
+ <a href="/user/HorribleSubs">
+ <img src="/static/img/vip.gif" alt="VIP" title="VIP" style="width:11px;" border='0'/>
+ </a>
+ <img src="/static/img/11x11p.png"/>
+ <font class="detDesc">
+ This is the content <span>and should be</span> OK
+ </font>
+ </td>
+ <td align="right">13</td>
+ <td align="right">334</td>
+ </tr>
+ <tr>
+ <td class="vertTh">
+ <center>
+ <a href="#" title="More from this category">Anime</a><br/>
+ (<a href="#" title="More from this category">Anime</a>)
+ </center>
+ </td>
+ <td>
+ <div class="detName">
+ <a href="/this.is.the.link" class="detLink" title="Title">
+ This is the title
+ </a>
+ </div>
+ <a href="magnet:?xt=urn:btih:MAGNETLINK" title="Download this torrent using magnet">
+ <img src="/static/img/icon-magnet.gif" alt="Magnet link"/>
+ </a>
+ <a href="/user/HorribleSubs">
+ <img src="/static/img/vip.gif" alt="VIP" title="VIP" style="width:11px;" border='0'/>
+ </a>
+ <img src="/static/img/11x11p.png"/>
+ <font class="detDesc">
+ This is the content <span>and should be</span> OK
+ </font>
+ </td>
+ <td align="right">13</td>
+ <td align="right">334</td>
+ </tr>
+ </table>
+ """
+ response = mock.Mock(text=html)
+ results = piratebay.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'https://thepiratebay.se/this.is.the.link')
+ self.assertEqual(results[0]['content'], 'This is the content and should be OK')
+ self.assertEqual(results[0]['seed'], 13)
+ self.assertEqual(results[0]['leech'], 334)
+ self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:MAGNETLINK')
+ self.assertEqual(results[0]['torrentfile'], 'http://torcache.net/torrent/TORRENTFILE.torrent')
+
+ self.assertEqual(results[1]['torrentfile'], None)
+
+ html = """
+ <table id="searchResult">
+ <tr>
+ </tr>
+ <tr>
+ <td class="vertTh">
+ <center>
+ <a href="#" title="More from this category">Anime</a><br/>
+ (<a href="#" title="More from this category">Anime</a>)
+ </center>
+ </td>
+ <td>
+ <div class="detName">
+ <a href="/this.is.the.link" class="detLink" title="Title">
+ This is the title
+ </a>
+ </div>
+ <a href="magnet:?xt=urn:btih:MAGNETLINK" title="Download this torrent using magnet">
+ <img src="/static/img/icon-magnet.gif" alt="Magnet link"/>
+ </a>
+ <a href="http://torcache.net/torrent/TORRENTFILE.torrent" title="Download this torrent">
+ <img src="/static/img/dl.gif" class="dl" alt="Download"/>
+ </a>
+ <a href="/user/HorribleSubs">
+ <img src="/static/img/vip.gif" alt="VIP" title="VIP" style="width:11px;" border='0'/>
+ </a>
+ <img src="/static/img/11x11p.png"/>
+ <font class="detDesc">
+ This is the content <span>and should be</span> OK
+ </font>
+ </td>
+ <td align="right">s</td>
+ <td align="right">d</td>
+ </tr>
+ </table>
+ """
+ response = mock.Mock(text=html)
+ results = piratebay.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'https://thepiratebay.se/this.is.the.link')
+ self.assertEqual(results[0]['content'], 'This is the content and should be OK')
+ self.assertEqual(results[0]['seed'], 0)
+ self.assertEqual(results[0]['leech'], 0)
+ self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:MAGNETLINK')
+ self.assertEqual(results[0]['torrentfile'], 'http://torcache.net/torrent/TORRENTFILE.torrent')
+
+ html = """
+ <table id="searchResult">
+ </table>
+ """
+ response = mock.Mock(text=html)
+ results = piratebay.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_qwant.py b/tests/unit/engines/test_qwant.py
new file mode 100644
index 0000000..b7133c0
--- /dev/null
+++ b/tests/unit/engines/test_qwant.py
@@ -0,0 +1,338 @@
+from collections import defaultdict
+import mock
+from searx.engines import qwant
+from searx.testing import SearxTestCase
+
+
+class TestQwantEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ dicto['language'] = 'fr-FR'
+ qwant.categories = ['']
+ params = qwant.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('web', params['url'])
+ self.assertIn('qwant.com', params['url'])
+ self.assertIn('fr_fr', params['url'])
+
+ dicto['language'] = 'all'
+ qwant.categories = ['news']
+ params = qwant.request(query, dicto)
+ self.assertFalse('fr' in params['url'])
+ self.assertIn('news', params['url'])
+
+ qwant.supported_languages = ['en', 'fr-FR', 'fr-CA']
+ dicto['language'] = 'fr'
+ params = qwant.request(query, dicto)
+ self.assertIn('fr_fr', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, qwant.response, None)
+ self.assertRaises(AttributeError, qwant.response, [])
+ self.assertRaises(AttributeError, qwant.response, '')
+ self.assertRaises(AttributeError, qwant.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(qwant.response(response), [])
+
+ response = mock.Mock(text='{"data": {}}')
+ self.assertEqual(qwant.response(response), [])
+
+ json = """
+ {
+ "status": "success",
+ "data": {
+ "query": {
+ "locale": "en_us",
+ "query": "Test",
+ "offset": 10
+ },
+ "result": {
+ "items": [
+ {
+ "title": "Title",
+ "score": 9999,
+ "url": "http://www.url.xyz",
+ "source": "...",
+ "desc": "Description",
+ "date": "",
+ "_id": "db0aadd62c2a8565567ffc382f5c61fa",
+ "favicon": "https://s.qwant.com/fav.ico"
+ }
+ ],
+ "filters": []
+ },
+ "cache": {
+ "key": "e66aa864c00147a0e3a16ff7a5efafde",
+ "created": 1433092754,
+ "expiration": 259200,
+ "status": "miss",
+ "age": 0
+ }
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ qwant.categories = ['general']
+ results = qwant.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title')
+ self.assertEqual(results[0]['url'], 'http://www.url.xyz')
+ self.assertEqual(results[0]['content'], 'Description')
+
+ json = """
+ {
+ "status": "success",
+ "data": {
+ "query": {
+ "locale": "en_us",
+ "query": "Test",
+ "offset": 10
+ },
+ "result": {
+ "items": [
+ {
+ "title": "Title",
+ "score": 9999,
+ "url": "http://www.url.xyz",
+ "source": "...",
+ "media": "http://image.jpg",
+ "desc": "",
+ "thumbnail": "http://thumbnail.jpg",
+ "date": "",
+ "_id": "db0aadd62c2a8565567ffc382f5c61fa",
+ "favicon": "https://s.qwant.com/fav.ico"
+ }
+ ],
+ "filters": []
+ },
+ "cache": {
+ "key": "e66aa864c00147a0e3a16ff7a5efafde",
+ "created": 1433092754,
+ "expiration": 259200,
+ "status": "miss",
+ "age": 0
+ }
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ qwant.categories = ['images']
+ results = qwant.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title')
+ self.assertEqual(results[0]['url'], 'http://www.url.xyz')
+ self.assertEqual(results[0]['content'], '')
+ self.assertEqual(results[0]['thumbnail_src'], 'http://thumbnail.jpg')
+ self.assertEqual(results[0]['img_src'], 'http://image.jpg')
+
+ json = """
+ {
+ "status": "success",
+ "data": {
+ "query": {
+ "locale": "en_us",
+ "query": "Test",
+ "offset": 10
+ },
+ "result": {
+ "items": [
+ {
+ "title": "Title",
+ "score": 9999,
+ "url": "http://www.url.xyz",
+ "source": "...",
+ "desc": "Description",
+ "date": 1433260920,
+ "_id": "db0aadd62c2a8565567ffc382f5c61fa",
+ "favicon": "https://s.qwant.com/fav.ico"
+ }
+ ],
+ "filters": []
+ },
+ "cache": {
+ "key": "e66aa864c00147a0e3a16ff7a5efafde",
+ "created": 1433092754,
+ "expiration": 259200,
+ "status": "miss",
+ "age": 0
+ }
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ qwant.categories = ['news']
+ results = qwant.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title')
+ self.assertEqual(results[0]['url'], 'http://www.url.xyz')
+ self.assertEqual(results[0]['content'], 'Description')
+ self.assertIn('publishedDate', results[0])
+
+ json = """
+ {
+ "status": "success",
+ "data": {
+ "query": {
+ "locale": "en_us",
+ "query": "Test",
+ "offset": 10
+ },
+ "result": {
+ "items": [
+ {
+ "title": "Title",
+ "score": 9999,
+ "url": "http://www.url.xyz",
+ "source": "...",
+ "desc": "Description",
+ "date": 1433260920,
+ "_id": "db0aadd62c2a8565567ffc382f5c61fa",
+ "favicon": "https://s.qwant.com/fav.ico"
+ }
+ ],
+ "filters": []
+ },
+ "cache": {
+ "key": "e66aa864c00147a0e3a16ff7a5efafde",
+ "created": 1433092754,
+ "expiration": 259200,
+ "status": "miss",
+ "age": 0
+ }
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ qwant.categories = ['social media']
+ results = qwant.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title')
+ self.assertEqual(results[0]['url'], 'http://www.url.xyz')
+ self.assertEqual(results[0]['content'], 'Description')
+ self.assertIn('publishedDate', results[0])
+
+ json = """
+ {
+ "status": "success",
+ "data": {
+ "query": {
+ "locale": "en_us",
+ "query": "Test",
+ "offset": 10
+ },
+ "result": {
+ "items": [
+ {
+ "title": "Title",
+ "score": 9999,
+ "url": "http://www.url.xyz",
+ "source": "...",
+ "desc": "Description",
+ "date": 1433260920,
+ "_id": "db0aadd62c2a8565567ffc382f5c61fa",
+ "favicon": "https://s.qwant.com/fav.ico"
+ }
+ ],
+ "filters": []
+ },
+ "cache": {
+ "key": "e66aa864c00147a0e3a16ff7a5efafde",
+ "created": 1433092754,
+ "expiration": 259200,
+ "status": "miss",
+ "age": 0
+ }
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ qwant.categories = ['']
+ results = qwant.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ json = """
+ {
+ "status": "success",
+ "data": {
+ "query": {
+ "locale": "en_us",
+ "query": "Test",
+ "offset": 10
+ },
+ "result": {
+ "filters": []
+ },
+ "cache": {
+ "key": "e66aa864c00147a0e3a16ff7a5efafde",
+ "created": 1433092754,
+ "expiration": 259200,
+ "status": "miss",
+ "age": 0
+ }
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ results = qwant.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ json = """
+ {
+ "status": "success",
+ "data": {
+ "query": {
+ "locale": "en_us",
+ "query": "Test",
+ "offset": 10
+ },
+ "cache": {
+ "key": "e66aa864c00147a0e3a16ff7a5efafde",
+ "created": 1433092754,
+ "expiration": 259200,
+ "status": "miss",
+ "age": 0
+ }
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ results = qwant.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ json = """
+ {
+ "status": "success"
+ }
+ """
+ response = mock.Mock(text=json)
+ results = qwant.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ def test_fetch_supported_languages(self):
+ page = """some code...
+ config_set('project.regionalisation', {"continents":{},"languages":
+ {"de":{"code":"de","name":"Deutsch","countries":["DE","CH","AT"]},
+ "it":{"code":"it","name":"Italiano","countries":["IT","CH"]}}});
+ some more code..."""
+ response = mock.Mock(text=page)
+ languages = qwant._fetch_supported_languages(response)
+ self.assertEqual(type(languages), list)
+ self.assertEqual(len(languages), 5)
+ self.assertIn('de-DE', languages)
+ self.assertIn('de-CH', languages)
+ self.assertIn('de-AT', languages)
+ self.assertIn('it-IT', languages)
+ self.assertIn('it-CH', languages)
diff --git a/tests/unit/engines/test_reddit.py b/tests/unit/engines/test_reddit.py
new file mode 100644
index 0000000..9c94f4e
--- /dev/null
+++ b/tests/unit/engines/test_reddit.py
@@ -0,0 +1,71 @@
+from collections import defaultdict
+import mock
+from searx.engines import reddit
+from searx.testing import SearxTestCase
+from datetime import datetime
+
+
+class TestRedditEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dic = defaultdict(dict)
+ params = reddit.request(query, dic)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('reddit.com' in params['url'])
+
+ def test_response(self):
+ resp = mock.Mock(text='{}')
+ self.assertEqual(reddit.response(resp), [])
+
+ json = """
+ {
+ "kind": "Listing",
+ "data": {
+ "children": [{
+ "data": {
+ "url": "http://google2.com/",
+ "permalink": "http://google.com/",
+ "title": "Title number one",
+ "selftext": "Sample",
+ "created_utc": 1401219957.0,
+ "thumbnail": "http://image.com/picture.jpg"
+ }
+ }, {
+ "data": {
+ "url": "https://reddit2.com/",
+ "permalink": "https://reddit.com/",
+ "title": "Title number two",
+ "selftext": "Dominus vobiscum",
+ "created_utc": 1438792533.0,
+ "thumbnail": "self"
+ }
+ }]
+ }
+ }
+ """
+
+ resp = mock.Mock(text=json)
+ results = reddit.response(resp)
+
+ self.assertEqual(len(results), 2)
+ self.assertEqual(type(results), list)
+
+ # testing first result (picture)
+ r = results[0]
+ self.assertEqual(r['url'], 'http://google.com/')
+ self.assertEqual(r['title'], 'Title number one')
+ self.assertEqual(r['template'], 'images.html')
+ self.assertEqual(r['img_src'], 'http://google2.com/')
+ self.assertEqual(r['thumbnail_src'], 'http://image.com/picture.jpg')
+
+ # testing second result (self-post)
+ r = results[1]
+ self.assertEqual(r['url'], 'https://reddit.com/')
+ self.assertEqual(r['title'], 'Title number two')
+ self.assertEqual(r['content'], 'Dominus vobiscum')
+ created = datetime.fromtimestamp(1438792533.0)
+ self.assertEqual(r['publishedDate'], created)
+ self.assertTrue('thumbnail_src' not in r)
+ self.assertTrue('img_src' not in r)
diff --git a/tests/unit/engines/test_scanr_structures.py b/tests/unit/engines/test_scanr_structures.py
new file mode 100644
index 0000000..a7b9e91
--- /dev/null
+++ b/tests/unit/engines/test_scanr_structures.py
@@ -0,0 +1,175 @@
+from collections import defaultdict
+import mock
+from searx.engines import scanr_structures
+from searx.testing import SearxTestCase
+
+
+class TestScanrStructuresEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = scanr_structures.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['data'])
+ self.assertIn('scanr.enseignementsup-recherche.gouv.fr', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, scanr_structures.response, None)
+ self.assertRaises(AttributeError, scanr_structures.response, [])
+ self.assertRaises(AttributeError, scanr_structures.response, '')
+ self.assertRaises(AttributeError, scanr_structures.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(scanr_structures.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(scanr_structures.response(response), [])
+
+ json = u"""
+ {
+ "request":
+ {
+ "query":"test_query",
+ "page":1,
+ "pageSize":20,
+ "sortOrder":"RELEVANCY",
+ "sortDirection":"ASC",
+ "searchField":"ALL",
+ "from":0
+ },
+ "total":2471,
+ "results":[
+ {
+ "id":"200711886U",
+ "label":"Laboratoire d'Informatique de Grenoble",
+ "kind":"RNSR",
+ "publicEntity":true,
+ "address":{"city":"Grenoble","departement":"38"},
+ "logo":"/static/logos/200711886U.png",
+ "acronym":"LIG",
+ "type":{"code":"UR","label":"Unit\xe9 de recherche"},
+ "level":2,
+ "institutions":[
+ {
+ "id":"193819125",
+ "label":"Grenoble INP",
+ "acronym":"IPG",
+ "code":"UMR 5217"
+ },
+ {
+ "id":"130021397",
+ "label":"Universit\xe9 de Grenoble Alpes",
+ "acronym":"UGA",
+ "code":"UMR 5217"
+ },
+ {
+ "id":"180089013",
+ "label":"Centre national de la recherche scientifique",
+ "acronym":"CNRS",
+ "code":"UMR 5217"
+ },
+ {
+ "id":"180089047",
+ "label":"Institut national de recherche en informatique et en automatique",
+ "acronym":"Inria",
+ "code":"UMR 5217"
+ }
+ ],
+ "highlights":[
+ {
+ "type":"projects",
+ "value":"linguicielles d\xe9velopp\xe9s jusqu'ici par le GETALP\
+ du <strong>LIG</strong> en tant que prototypes op\xe9rationnels.\
+\\r\\nDans le contexte"
+ },
+ {
+ "type":"acronym",
+ "value":"<strong>LIG</strong>"
+ },
+ {
+ "type":"websiteContents",
+ "value":"S\xe9lection\\nListe structures\\nD\xe9tail\\n\
+ Accueil\\n200711886U : <strong>LIG</strong>\
+ Laboratoire d'Informatique de Grenoble Unit\xe9 de recherche"},
+ {
+ "type":"publications",
+ "value":"de noms. Nous avons d'abord d\xe9velopp\xe9 LOOV \
+ (pour <strong>Lig</strong> Overlaid OCR in Vid\xe9o), \
+ un outil d'extraction des"
+ }
+ ]
+ },
+ {
+ "id":"199511665F",
+ "label":"Laboratoire Bordelais de Recherche en Informatique",
+ "kind":"RNSR",
+ "publicEntity":true,
+ "address":{"city":"Talence","departement":"33"},
+ "logo":"/static/logos/199511665F.png",
+ "acronym":"LaBRI",
+ "type":{"code":"UR","label":"Unit\xe9 de recherche"},
+ "level":2,
+ "institutions":[
+ {
+ "id":"130006356",
+ "label":"Institut polytechnique de Bordeaux",
+ "acronym":"IPB",
+ "code":"UMR 5800"
+ },
+ {
+ "id":"130018351",
+ "label":"Universit\xe9 de Bordeaux",
+ "acronym":null,
+ "code":"UMR 5800"
+ },
+ {
+ "id":"180089013",
+ "label":"Centre national de la recherche scientifique",
+ "acronym":"CNRS",
+ "code":"UMR 5800"
+ },
+ {
+ "id":"180089047",
+ "label":"Institut national de recherche en informatique et en automatique",
+ "acronym":"Inria",
+ "code":"UMR 5800"
+ }
+ ],
+ "highlights":[
+ {
+ "type":"websiteContents",
+ "value":"Samia Kerdjoudj\\n2016-07-05\\nDouble-exponential\
+ and <strong>triple</strong>-exponential bounds for\
+ choosability problems parameterized"
+ },
+ {
+ "type":"publications",
+ "value":"de cam\xe9ras install\xe9es dans les lieux publiques \
+ a <strong>tripl\xe9</strong> en 2009, passant de 20 000 \
+ \xe0 60 000. Malgr\xe9 le"
+ }
+ ]
+ }
+ ]
+ }
+ """
+ response = mock.Mock(text=json)
+ results = scanr_structures.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], u"Laboratoire d'Informatique de Grenoble")
+ self.assertEqual(results[0]['url'], 'https://scanr.enseignementsup-recherche.gouv.fr/structure/200711886U')
+ self.assertEqual(results[0]['content'],
+ u"linguicielles d\xe9velopp\xe9s jusqu'ici par le GETALP "
+ u"du LIG en tant que prototypes "
+ u"op\xe9rationnels. Dans le contexte")
+ self.assertEqual(results[1]['img_src'],
+ 'https://scanr.enseignementsup-recherche.gouv.fr//static/logos/199511665F.png')
+ self.assertEqual(results[1]['content'],
+ "Samia Kerdjoudj 2016-07-05 Double-exponential and"
+ " triple-exponential bounds for "
+ "choosability problems parameterized")
+ self.assertEqual(results[1]['url'], 'https://scanr.enseignementsup-recherche.gouv.fr/structure/199511665F')
+ self.assertEqual(results[1]['title'], u"Laboratoire Bordelais de Recherche en Informatique")
diff --git a/tests/unit/engines/test_searchcode_code.py b/tests/unit/engines/test_searchcode_code.py
new file mode 100644
index 0000000..955aea1
--- /dev/null
+++ b/tests/unit/engines/test_searchcode_code.py
@@ -0,0 +1,75 @@
+from collections import defaultdict
+import mock
+from searx.engines import searchcode_code
+from searx.testing import SearxTestCase
+
+
+class TestSearchcodeCodeEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = searchcode_code.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('searchcode.com', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, searchcode_code.response, None)
+ self.assertRaises(AttributeError, searchcode_code.response, [])
+ self.assertRaises(AttributeError, searchcode_code.response, '')
+ self.assertRaises(AttributeError, searchcode_code.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(searchcode_code.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(searchcode_code.response(response), [])
+
+ json = """
+ {
+ "matchterm": "test",
+ "previouspage": null,
+ "searchterm": "test",
+ "query": "test",
+ "total": 1000,
+ "page": 0,
+ "nextpage": 1,
+ "results": [
+ {
+ "repo": "https://repo",
+ "linescount": 1044,
+ "location": "/tests",
+ "name": "Name",
+ "url": "https://url",
+ "md5hash": "ecac6e479edd2b9406c9e08603cec655",
+ "lines": {
+ "1": "// Test 011",
+ "2": "// Source: "
+ },
+ "id": 51223527,
+ "filename": "File.CPP"
+ }
+ ]
+ }
+ """
+ response = mock.Mock(text=json)
+ results = searchcode_code.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Name - File.CPP')
+ self.assertEqual(results[0]['url'], 'https://url')
+ self.assertEqual(results[0]['repository'], 'https://repo')
+ self.assertEqual(results[0]['code_language'], 'cpp')
+
+ json = r"""
+ {"toto":[
+ {"id":200,"name":"Artist Name",
+ "link":"http:\/\/www.searchcode_code.com\/artist\/1217","type":"artist"}
+ ]}
+ """
+ response = mock.Mock(text=json)
+ results = searchcode_code.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_searchcode_doc.py b/tests/unit/engines/test_searchcode_doc.py
new file mode 100644
index 0000000..d02bb7a
--- /dev/null
+++ b/tests/unit/engines/test_searchcode_doc.py
@@ -0,0 +1,70 @@
+from collections import defaultdict
+import mock
+from searx.engines import searchcode_doc
+from searx.testing import SearxTestCase
+
+
+class TestSearchcodeDocEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = searchcode_doc.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('searchcode.com', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, searchcode_doc.response, None)
+ self.assertRaises(AttributeError, searchcode_doc.response, [])
+ self.assertRaises(AttributeError, searchcode_doc.response, '')
+ self.assertRaises(AttributeError, searchcode_doc.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(searchcode_doc.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(searchcode_doc.response(response), [])
+
+ json = """
+ {
+ "matchterm": "test",
+ "previouspage": null,
+ "searchterm": "test",
+ "query": "test",
+ "total": 60,
+ "page": 0,
+ "nextpage": 1,
+ "results": [
+ {
+ "synopsis": "Synopsis",
+ "displayname": null,
+ "name": "test",
+ "url": "http://url",
+ "type": "Type",
+ "icon": null,
+ "namespace": "Namespace",
+ "description": "Description"
+ }
+ ]
+ }
+ """
+ response = mock.Mock(text=json)
+ results = searchcode_doc.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], '[Type] Namespace test')
+ self.assertEqual(results[0]['url'], 'http://url')
+ self.assertIn('Description', results[0]['content'])
+
+ json = r"""
+ {"toto":[
+ {"id":200,"name":"Artist Name",
+ "link":"http:\/\/www.searchcode_doc.com\/artist\/1217","type":"artist"}
+ ]}
+ """
+ response = mock.Mock(text=json)
+ results = searchcode_doc.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_seedpeer.py b/tests/unit/engines/test_seedpeer.py
new file mode 100644
index 0000000..37b2de8
--- /dev/null
+++ b/tests/unit/engines/test_seedpeer.py
@@ -0,0 +1,51 @@
+import mock
+from collections import defaultdict
+from searx.engines import seedpeer
+from searx.testing import SearxTestCase
+from datetime import datetime
+
+
+class TestSeedPeerEngine(SearxTestCase):
+
+ html = ''
+ with open('./tests/unit/engines/seedpeer_fixture.html') as fixture:
+ html += fixture.read()
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = seedpeer.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('seedpeer.eu', params['url'])
+
+ def test_response_raises_attr_error_on_empty_response(self):
+ self.assertRaises(AttributeError, seedpeer.response, None)
+ self.assertRaises(AttributeError, seedpeer.response, [])
+ self.assertRaises(AttributeError, seedpeer.response, '')
+ self.assertRaises(AttributeError, seedpeer.response, '[]')
+
+ def test_response_returns_empty_list(self):
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(seedpeer.response(response), [])
+
+ def test_response_returns_all_results(self):
+ response = mock.Mock(text=self.html)
+ results = seedpeer.response(response)
+ self.assertTrue(isinstance(results, list))
+ self.assertEqual(len(results), 2)
+
+ def test_response_returns_correct_results(self):
+ response = mock.Mock(text=self.html)
+ results = seedpeer.response(response)
+ self.assertEqual(
+ results[0]['title'], 'Narcos - Season 2 - 720p WEBRiP - x265 HEVC - ShAaNiG '
+ )
+ self.assertEqual(
+ results[0]['url'],
+ 'http://www.seedpeer.eu/details/11685972/Narcos---Season-2---720p-WEBRiP---x265-HEVC---ShAaNiG.html'
+ )
+ self.assertEqual(results[0]['content'], '2.48 GB, 1 day')
+ self.assertEqual(results[0]['seed'], '861')
+ self.assertEqual(results[0]['leech'], '332')
diff --git a/tests/unit/engines/test_soundcloud.py b/tests/unit/engines/test_soundcloud.py
new file mode 100644
index 0000000..3077d3b
--- /dev/null
+++ b/tests/unit/engines/test_soundcloud.py
@@ -0,0 +1,192 @@
+from collections import defaultdict
+import mock
+from searx.engines import soundcloud
+from searx.testing import SearxTestCase
+from searx.url_utils import quote_plus
+
+
+class TestSoundcloudEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = soundcloud.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('soundcloud.com', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, soundcloud.response, None)
+ self.assertRaises(AttributeError, soundcloud.response, [])
+ self.assertRaises(AttributeError, soundcloud.response, '')
+ self.assertRaises(AttributeError, soundcloud.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(soundcloud.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(soundcloud.response(response), [])
+
+ json = """
+ {
+ "collection": [
+ {
+ "kind": "track",
+ "id": 159723640,
+ "created_at": "2014/07/22 00:51:21 +0000",
+ "user_id": 2976616,
+ "duration": 303780,
+ "commentable": true,
+ "state": "finished",
+ "original_content_size": 13236349,
+ "last_modified": "2015/01/31 15:14:50 +0000",
+ "sharing": "public",
+ "tag_list": "seekae flume",
+ "permalink": "seekae-test-recognise-flume-re-work",
+ "streamable": true,
+ "embeddable_by": "all",
+ "downloadable": true,
+ "purchase_url": "http://www.facebook.com/seekaemusic",
+ "label_id": null,
+ "purchase_title": "Seekae",
+ "genre": "freedownload",
+ "title": "This is the title",
+ "description": "This is the content",
+ "label_name": "Future Classic",
+ "release": "",
+ "track_type": "remix",
+ "key_signature": "",
+ "isrc": "",
+ "video_url": null,
+ "bpm": null,
+ "release_year": 2014,
+ "release_month": 7,
+ "release_day": 22,
+ "original_format": "mp3",
+ "license": "all-rights-reserved",
+ "uri": "https://api.soundcloud.com/tracks/159723640",
+ "user": {
+ "id": 2976616,
+ "kind": "user",
+ "permalink": "flume",
+ "username": "Flume",
+ "last_modified": "2014/11/24 19:21:29 +0000",
+ "uri": "https://api.soundcloud.com/users/2976616",
+ "permalink_url": "http://soundcloud.com/flume",
+ "avatar_url": "https://i1.sndcdn.com/avatars-000044475439-4zi7ii-large.jpg"
+ },
+ "permalink_url": "http://soundcloud.com/this.is.the.url",
+ "artwork_url": "https://i1.sndcdn.com/artworks-000085857162-xdxy5c-large.jpg",
+ "waveform_url": "https://w1.sndcdn.com/DWrL1lAN8BkP_m.png",
+ "stream_url": "https://api.soundcloud.com/tracks/159723640/stream",
+ "download_url": "https://api.soundcloud.com/tracks/159723640/download",
+ "playback_count": 2190687,
+ "download_count": 54856,
+ "favoritings_count": 49061,
+ "comment_count": 826,
+ "likes_count": 49061,
+ "reposts_count": 15910,
+ "attachments_uri": "https://api.soundcloud.com/tracks/159723640/attachments",
+ "policy": "ALLOW"
+ }
+ ],
+ "total_results": 375750,
+ "next_href": "https://api.soundcloud.com/search?&q=test",
+ "tx_id": ""
+ }
+ """
+ response = mock.Mock(text=json)
+ results = soundcloud.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'http://soundcloud.com/this.is.the.url')
+ self.assertEqual(results[0]['content'], 'This is the content')
+ self.assertIn(quote_plus('https://api.soundcloud.com/tracks/159723640'), results[0]['embedded'])
+
+ json = """
+ {
+ "collection": [
+ {
+ "kind": "user",
+ "id": 159723640,
+ "created_at": "2014/07/22 00:51:21 +0000",
+ "user_id": 2976616,
+ "duration": 303780,
+ "commentable": true,
+ "state": "finished",
+ "original_content_size": 13236349,
+ "last_modified": "2015/01/31 15:14:50 +0000",
+ "sharing": "public",
+ "tag_list": "seekae flume",
+ "permalink": "seekae-test-recognise-flume-re-work",
+ "streamable": true,
+ "embeddable_by": "all",
+ "downloadable": true,
+ "purchase_url": "http://www.facebook.com/seekaemusic",
+ "label_id": null,
+ "purchase_title": "Seekae",
+ "genre": "freedownload",
+ "title": "This is the title",
+ "description": "This is the content",
+ "label_name": "Future Classic",
+ "release": "",
+ "track_type": "remix",
+ "key_signature": "",
+ "isrc": "",
+ "video_url": null,
+ "bpm": null,
+ "release_year": 2014,
+ "release_month": 7,
+ "release_day": 22,
+ "original_format": "mp3",
+ "license": "all-rights-reserved",
+ "uri": "https://api.soundcloud.com/tracks/159723640",
+ "user": {
+ "id": 2976616,
+ "kind": "user",
+ "permalink": "flume",
+ "username": "Flume",
+ "last_modified": "2014/11/24 19:21:29 +0000",
+ "uri": "https://api.soundcloud.com/users/2976616",
+ "permalink_url": "http://soundcloud.com/flume",
+ "avatar_url": "https://i1.sndcdn.com/avatars-000044475439-4zi7ii-large.jpg"
+ },
+ "permalink_url": "http://soundcloud.com/this.is.the.url",
+ "artwork_url": "https://i1.sndcdn.com/artworks-000085857162-xdxy5c-large.jpg",
+ "waveform_url": "https://w1.sndcdn.com/DWrL1lAN8BkP_m.png",
+ "stream_url": "https://api.soundcloud.com/tracks/159723640/stream",
+ "download_url": "https://api.soundcloud.com/tracks/159723640/download",
+ "playback_count": 2190687,
+ "download_count": 54856,
+ "favoritings_count": 49061,
+ "comment_count": 826,
+ "likes_count": 49061,
+ "reposts_count": 15910,
+ "attachments_uri": "https://api.soundcloud.com/tracks/159723640/attachments",
+ "policy": "ALLOW"
+ }
+ ],
+ "total_results": 375750,
+ "next_href": "https://api.soundcloud.com/search?&q=test",
+ "tx_id": ""
+ }
+ """
+ response = mock.Mock(text=json)
+ results = soundcloud.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ json = """
+ {
+ "collection": [],
+ "total_results": 375750,
+ "next_href": "https://api.soundcloud.com/search?&q=test",
+ "tx_id": ""
+ }
+ """
+ response = mock.Mock(text=json)
+ results = soundcloud.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_spotify.py b/tests/unit/engines/test_spotify.py
new file mode 100644
index 0000000..e37c344
--- /dev/null
+++ b/tests/unit/engines/test_spotify.py
@@ -0,0 +1,124 @@
+from collections import defaultdict
+import mock
+from searx.engines import spotify
+from searx.testing import SearxTestCase
+
+
+class TestSpotifyEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = spotify.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('spotify.com', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, spotify.response, None)
+ self.assertRaises(AttributeError, spotify.response, [])
+ self.assertRaises(AttributeError, spotify.response, '')
+ self.assertRaises(AttributeError, spotify.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(spotify.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(spotify.response(response), [])
+
+ json = """
+ {
+ "tracks": {
+ "href": "https://api.spotify.com/v1/search?query=nosfell&offset=0&limit=20&type=track",
+ "items": [
+ {
+ "album": {
+ "album_type": "album",
+ "external_urls": {
+ "spotify": "https://open.spotify.com/album/5c9ap1PBkSGLxT3J73toxA"
+ },
+ "href": "https://api.spotify.com/v1/albums/5c9ap1PBkSGLxT3J73toxA",
+ "id": "5c9ap1PBkSGLxT3J73toxA",
+ "name": "Album Title",
+ "type": "album",
+ "uri": "spotify:album:5c9ap1PBkSGLxT3J73toxA"
+ },
+ "artists": [
+ {
+ "external_urls": {
+ "spotify": "https://open.spotify.com/artist/0bMc6b75FfZEpQHG1jifKu"
+ },
+ "href": "https://api.spotify.com/v1/artists/0bMc6b75FfZEpQHG1jifKu",
+ "id": "0bMc6b75FfZEpQHG1jifKu",
+ "name": "Artist Name",
+ "type": "artist",
+ "uri": "spotify:artist:0bMc6b75FfZEpQHG1jifKu"
+ }
+ ],
+ "disc_number": 1,
+ "duration_ms": 202386,
+ "explicit": false,
+ "external_ids": {
+ "isrc": "FRV640600067"
+ },
+ "external_urls": {
+ "spotify": "https://open.spotify.com/track/2GzvFiedqW8hgqUpWcASZa"
+ },
+ "href": "https://api.spotify.com/v1/tracks/2GzvFiedqW8hgqUpWcASZa",
+ "id": "1000",
+ "is_playable": true,
+ "name": "Title of track",
+ "popularity": 6,
+ "preview_url": "https://p.scdn.co/mp3-preview/7b8ecda580965a066b768c2647f877e43f7b1a0a",
+ "track_number": 3,
+ "type": "track",
+ "uri": "spotify:track:2GzvFiedqW8hgqUpWcASZa"
+ }
+ ],
+ "limit": 20,
+ "next": "https://api.spotify.com/v1/search?query=nosfell&offset=20&limit=20&type=track",
+ "offset": 0,
+ "previous": null,
+ "total": 107
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ results = spotify.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title of track')
+ self.assertEqual(results[0]['url'], 'https://open.spotify.com/track/2GzvFiedqW8hgqUpWcASZa')
+ self.assertEqual(results[0]['content'], 'Artist Name - Album Title - Title of track')
+ self.assertIn('1000', results[0]['embedded'])
+
+ json = """
+ {
+ "tracks": {
+ "href": "https://api.spotify.com/v1/search?query=nosfell&offset=0&limit=20&type=track",
+ "items": [
+ {
+ "href": "https://api.spotify.com/v1/tracks/2GzvFiedqW8hgqUpWcASZa",
+ "id": "1000",
+ "is_playable": true,
+ "name": "Title of track",
+ "popularity": 6,
+ "preview_url": "https://p.scdn.co/mp3-preview/7b8ecda580965a066b768c2647f877e43f7b1a0a",
+ "track_number": 3,
+ "type": "album",
+ "uri": "spotify:track:2GzvFiedqW8hgqUpWcASZa"
+ }
+ ],
+ "limit": 20,
+ "next": "https://api.spotify.com/v1/search?query=nosfell&offset=20&limit=20&type=track",
+ "offset": 0,
+ "previous": null,
+ "total": 107
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ results = spotify.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_stackoverflow.py b/tests/unit/engines/test_stackoverflow.py
new file mode 100644
index 0000000..18a1ff4
--- /dev/null
+++ b/tests/unit/engines/test_stackoverflow.py
@@ -0,0 +1,106 @@
+from collections import defaultdict
+import mock
+from searx.engines import stackoverflow
+from searx.testing import SearxTestCase
+
+
+class TestStackoverflowEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = stackoverflow.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('stackoverflow.com' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, stackoverflow.response, None)
+ self.assertRaises(AttributeError, stackoverflow.response, [])
+ self.assertRaises(AttributeError, stackoverflow.response, '')
+ self.assertRaises(AttributeError, stackoverflow.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(stackoverflow.response(response), [])
+
+ html = """
+ <div class="question-summary search-result" id="answer-id-1783426">
+ <div class="statscontainer">
+ <div class="statsarrow"></div>
+ <div class="stats">
+ <div class="vote">
+ <div class="votes answered">
+ <span class="vote-count-post "><strong>2583</strong></span>
+ <div class="viewcount">votes</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="summary">
+ <div class="result-link">
+ <span>
+ <a href="/questions/this.is.the.url"
+ data-searchsession="/questions"
+ title="Checkout remote Git branch">
+ This is the title
+ </a>
+ </span>
+ </div>
+ <div class="excerpt">
+ This is the content
+ </div>
+ <div class="tags user-tags t-git t-git-checkout t-remote-branch">
+ </div>
+ <div class="started fr">
+ answered <span title="2009-11-23 14:26:08Z" class="relativetime">nov 23 '09</span> by
+ <a href="/users/214090/hallski">hallski</a>
+ </div>
+ </div>
+ </div>
+ """
+ response = mock.Mock(text=html)
+ results = stackoverflow.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'https://stackoverflow.com/questions/this.is.the.url')
+ self.assertEqual(results[0]['content'], 'This is the content')
+
+ html = """
+ <div class="statscontainer">
+ <div class="statsarrow"></div>
+ <div class="stats">
+ <div class="vote">
+ <div class="votes answered">
+ <span class="vote-count-post "><strong>2583</strong></span>
+ <div class="viewcount">votes</div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="summary">
+ <div class="result-link">
+ <span>
+ <a href="/questions/this.is.the.url"
+ data-searchsession="/questions"
+ title="Checkout remote Git branch">
+ This is the title
+ </a>
+ </span>
+ </div>
+ <div class="excerpt">
+ This is the content
+ </div>
+ <div class="tags user-tags t-git t-git-checkout t-remote-branch">
+ </div>
+ <div class="started fr">
+ answered <span title="2009-11-23 14:26:08Z" class="relativetime">nov 23 '09</span> by
+ <a href="/users/214090/hallski">hallski</a>
+ </div>
+ </div>
+ """
+ response = mock.Mock(text=html)
+ results = stackoverflow.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_startpage.py b/tests/unit/engines/test_startpage.py
new file mode 100644
index 0000000..a7a9778
--- /dev/null
+++ b/tests/unit/engines/test_startpage.py
@@ -0,0 +1,140 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import startpage
+from searx.testing import SearxTestCase
+
+
+class TestStartpageEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr_FR'
+ params = startpage.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn('startpage.com', params['url'])
+ self.assertIn('data', params)
+ self.assertIn('query', params['data'])
+ self.assertIn(query, params['data']['query'])
+ self.assertIn('with_language', params['data'])
+ self.assertIn('lang_fr', params['data']['with_language'])
+
+ dicto['language'] = 'all'
+ params = startpage.request(query, dicto)
+ self.assertNotIn('with_language', params['data'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, startpage.response, None)
+ self.assertRaises(AttributeError, startpage.response, [])
+ self.assertRaises(AttributeError, startpage.response, '')
+ self.assertRaises(AttributeError, startpage.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(startpage.response(response), [])
+
+ html = """
+ <div class='result' style=' *width : auto; *margin-right : 10%;'>
+ <h3>
+ <a href='http://this.should.be.the.link/' id='title_2' name='title_2' >
+ This should be the title
+ </a>
+ <span id='title_stars_2' name='title_stars_2'> </span>
+ </h3>
+ <p class='desc clk'>
+ This should be the content.
+ </p>
+ <p>
+ <span class='url'>www.speed<b>test</b>.net/fr/
+ </span>
+ -
+ <A class="proxy" id="proxy_link" HREF="https://ixquick-proxy.com/do/spg/proxy?ep=&edata=&ek=&ekdata="
+ class='proxy'>
+ Navigation avec Ixquick Proxy
+ </A>
+ -
+ <A HREF="https://ixquick-proxy.com/do/spg/highlight.pl?l=francais&c=hf&cat=web&q=test&rl=NONE&rid=
+ &hlq=https://startpage.com/do/search&mtabp=-1&mtcmd=process_search&mtlanguage=francais&mtengine0=
+ &mtcat=web&u=http:%2F%2Fwww.speedtest.net%2Ffr%2F" class='proxy'>
+ Mis en surbrillance
+ </A>
+ </p>
+ </div>
+ """
+ response = mock.Mock(text=html.encode('utf-8'))
+ results = startpage.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This should be the title')
+ self.assertEqual(results[0]['url'], 'http://this.should.be.the.link/')
+ self.assertEqual(results[0]['content'], 'This should be the content.')
+
+ html = """
+ <div class='result' style=' *width : auto; *margin-right : 10%;'>
+ <h3>
+ <a href='http://www.google.com/aclk?sa=l&ai=C' id='title_2' name='title_2' >
+ This should be the title
+ </a>
+ <span id='title_stars_2' name='title_stars_2'> </span>
+ </h3>
+ <p class='desc clk'>
+ This should be the content.
+ </p>
+ <p>
+ <span class='url'>www.speed<b>test</b>.net/fr/
+ </span>
+ -
+ <A class="proxy" id="proxy_link" HREF="https://ixquick-proxy.com/do/spg/proxy?ep=&edata=&ek=&ekdata="
+ class='proxy'>
+ Navigation avec Ixquick Proxy
+ </A>
+ -
+ <A HREF="https://ixquick-proxy.com/do/spg/highlight.pl?l=francais&c=hf&cat=web&q=test&rl=NONE&rid=
+ &hlq=https://startpage.com/do/search&mtabp=-1&mtcmd=process_search&mtlanguage=francais&mtengine0=
+ &mtcat=web&u=http:%2F%2Fwww.speedtest.net%2Ffr%2F" class='proxy'>
+ Mis en surbrillance
+ </A>
+ </p>
+ </div>
+ <div class='result' style=' *width : auto; *margin-right : 10%;'>
+ <h3>
+ <span id='title_stars_2' name='title_stars_2'> </span>
+ </h3>
+ <p class='desc clk'>
+ This should be the content.
+ </p>
+ <p>
+ <span class='url'>www.speed<b>test</b>.net/fr/
+ </span>
+ </p>
+ </div>
+ <div class='result' style=' *width : auto; *margin-right : 10%;'>
+ <h3>
+ <a href='http://this.should.be.the.link/' id='title_2' name='title_2' >
+ This should be the title
+ </a>
+ <span id='title_stars_2' name='title_stars_2'> </span>
+ </h3>
+ <p>
+ <span class='url'>www.speed<b>test</b>.net/fr/
+ </span>
+ -
+ <A class="proxy" id="proxy_link" HREF="https://ixquick-proxy.com/do/spg/proxy?ep=&edata=&ek=&ekdata="
+ class='proxy'>
+ Navigation avec Ixquick Proxy
+ </A>
+ -
+ <A HREF="https://ixquick-proxy.com/do/spg/highlight.pl?l=francais&c=hf&cat=web&q=test&rl=NONE&rid=
+ &hlq=https://startpage.com/do/search&mtabp=-1&mtcmd=process_search&mtlanguage=francais&mtengine0=
+ &mtcat=web&u=http:%2F%2Fwww.speedtest.net%2Ffr%2F" class='proxy'>
+ Mis en surbrillance
+ </A>
+ </p>
+ </div>
+ """
+ response = mock.Mock(text=html.encode('utf-8'))
+ results = startpage.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['content'], '')
diff --git a/tests/unit/engines/test_subtitleseeker.py b/tests/unit/engines/test_subtitleseeker.py
new file mode 100644
index 0000000..a22ee74
--- /dev/null
+++ b/tests/unit/engines/test_subtitleseeker.py
@@ -0,0 +1,174 @@
+from collections import defaultdict
+import mock
+from searx.engines import subtitleseeker
+from searx.testing import SearxTestCase
+
+
+class TestSubtitleseekerEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr-FR'
+ params = subtitleseeker.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('subtitleseeker.com' in params['url'])
+
+ def test_response(self):
+ dicto = defaultdict(dict)
+ dicto['language'] = 'fr-FR'
+ response = mock.Mock(search_params=dicto)
+
+ self.assertRaises(AttributeError, subtitleseeker.response, None)
+ self.assertRaises(AttributeError, subtitleseeker.response, [])
+ self.assertRaises(AttributeError, subtitleseeker.response, '')
+ self.assertRaises(AttributeError, subtitleseeker.response, '[]')
+
+ response = mock.Mock(text='<html></html>', search_params=dicto)
+ self.assertEqual(subtitleseeker.response(response), [])
+
+ html = """
+ <div class="boxRows">
+ <div class="boxRowsInner" style="width:600px;">
+ <img src="http://static.subtitleseeker.com/images/movie.gif"
+ style="width:16px; height:16px;" class="icon">
+ <a href="http://this.is.the.url/"
+ class="blue" title="Title subtitle" >
+ This is the Title
+ </a>
+ <br><br>
+ <span class="f10b grey-dark arial" style="padding:0px 0px 5px 20px">
+ "Alternative Title"
+ </span>
+ </div>
+ <div class="boxRowsInner f12b red" style="width:70px;">
+ 1998
+ </div>
+ <div class="boxRowsInner grey-web f12" style="width:120px;">
+ <img src="http://static.subtitleseeker.com/images/basket_put.png"
+ style="width:16px; height:16px;" class="icon">
+ 1039 Subs
+ </div>
+ <div class="boxRowsInner grey-web f10" style="width:130px;">
+ <img src="http://static.subtitleseeker.com/images/arrow_refresh_small.png"
+ style="width:16px; height:16px;" class="icon">
+ 1 hours ago
+ </div>
+ <div class="clear"></div>
+ </div>
+ """
+ response = mock.Mock(text=html, search_params=dicto)
+ results = subtitleseeker.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the Title')
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url/French/')
+ self.assertIn('1998', results[0]['content'])
+ self.assertIn('1039 Subs', results[0]['content'])
+ self.assertIn('Alternative Title', results[0]['content'])
+
+ dicto['language'] = 'pt-BR'
+ results = subtitleseeker.response(response)
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url/Brazilian/')
+
+ html = """
+ <div class="boxRows">
+ <div class="boxRowsInner" style="width:600px;">
+ <img src="http://static.subtitleseeker.com/images/movie.gif"
+ style="width:16px; height:16px;" class="icon">
+ <a href="http://this.is.the.url/"
+ class="blue" title="Title subtitle" >
+ This is the Title
+ </a>
+ </div>
+ <div class="boxRowsInner f12b red" style="width:70px;">
+ 1998
+ </div>
+ <div class="boxRowsInner grey-web f12" style="width:120px;">
+ <img src="http://static.subtitleseeker.com/images/basket_put.png"
+ style="width:16px; height:16px;" class="icon">
+ 1039 Subs
+ </div>
+ <div class="boxRowsInner grey-web f10" style="width:130px;">
+ <img src="http://static.subtitleseeker.com/images/arrow_refresh_small.png"
+ style="width:16px; height:16px;" class="icon">
+ 1 hours ago
+ </div>
+ <div class="clear"></div>
+ </div>
+ """
+ dicto['language'] = 'all'
+ response = mock.Mock(text=html, search_params=dicto)
+ results = subtitleseeker.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the Title')
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url/')
+ self.assertIn('1998', results[0]['content'])
+ self.assertIn('1039 Subs', results[0]['content'])
+
+ html = """
+ <div class="boxRows">
+ <div class="boxRowsInner" style="width:600px;">
+ <img src="http://static.subtitleseeker.com/images/movie.gif"
+ style="width:16px; height:16px;" class="icon">
+ <a href="http://this.is.the.url/"
+ class="blue" title="Title subtitle" >
+ This is the Title
+ </a>
+ </div>
+ <div class="boxRowsInner f12b red" style="width:70px;">
+ 1998
+ </div>
+ <div class="boxRowsInner grey-web f12" style="width:120px;">
+ <img src="http://static.subtitleseeker.com/images/basket_put.png"
+ style="width:16px; height:16px;" class="icon">
+ 1039 Subs
+ </div>
+ <div class="boxRowsInner grey-web f10" style="width:130px;">
+ <img src="http://static.subtitleseeker.com/images/arrow_refresh_small.png"
+ style="width:16px; height:16px;" class="icon">
+ 1 hours ago
+ </div>
+ <div class="clear"></div>
+ </div>
+ """
+ subtitleseeker.language = 'English'
+ response = mock.Mock(text=html, search_params=dicto)
+ results = subtitleseeker.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the Title')
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url/English/')
+ self.assertIn('1998', results[0]['content'])
+ self.assertIn('1039 Subs', results[0]['content'])
+
+ html = """
+ <div class="boxRowsInner" style="width:600px;">
+ <img src="http://static.subtitleseeker.com/images/movie.gif"
+ style="width:16px; height:16px;" class="icon">
+ <a href="http://this.is.the.url/"
+ class="blue" title="Title subtitle" >
+ This is the Title
+ </a>
+ </div>
+ <div class="boxRowsInner f12b red" style="width:70px;">
+ 1998
+ </div>
+ <div class="boxRowsInner grey-web f12" style="width:120px;">
+ <img src="http://static.subtitleseeker.com/images/basket_put.png"
+ style="width:16px; height:16px;" class="icon">
+ 1039 Subs
+ </div>
+ <div class="boxRowsInner grey-web f10" style="width:130px;">
+ <img src="http://static.subtitleseeker.com/images/arrow_refresh_small.png"
+ style="width:16px; height:16px;" class="icon">
+ 1 hours ago
+ </div>
+ """
+ response = mock.Mock(text=html, search_params=dicto)
+ results = subtitleseeker.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_swisscows.py b/tests/unit/engines/test_swisscows.py
new file mode 100644
index 0000000..53890be
--- /dev/null
+++ b/tests/unit/engines/test_swisscows.py
@@ -0,0 +1,155 @@
+from collections import defaultdict
+import mock
+from searx.engines import swisscows
+from searx.testing import SearxTestCase
+
+
+class TestSwisscowsEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'de-DE'
+ params = swisscows.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('swisscows.ch' in params['url'])
+ self.assertTrue('uiLanguage=de' in params['url'])
+ self.assertTrue('region=de-DE' in params['url'])
+
+ dicto['language'] = 'all'
+ params = swisscows.request(query, dicto)
+ self.assertTrue('uiLanguage=browser' in params['url'])
+ self.assertTrue('region=browser' in params['url'])
+
+ dicto['category'] = 'images'
+ params = swisscows.request(query, dicto)
+ self.assertIn('image', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, swisscows.response, None)
+ self.assertRaises(AttributeError, swisscows.response, [])
+ self.assertRaises(AttributeError, swisscows.response, '')
+ self.assertRaises(AttributeError, swisscows.response, '[]')
+
+ response = mock.Mock(text=b'<html></html>')
+ self.assertEqual(swisscows.response(response), [])
+
+ response = mock.Mock(text=b'<html></html>')
+ self.assertEqual(swisscows.response(response), [])
+
+ html = b"""
+ <script>
+ App.Dispatcher.dispatch("initialize", {
+ html5history: true,
+ initialData: {"Request":
+ {"Page":1,
+ "ItemsCount":1,
+ "Query":"This should ",
+ "NormalizedQuery":"This should ",
+ "Region":"de-AT",
+ "UILanguage":"de"},
+ "Results":{"items":[
+ {"Title":"\uE000This should\uE001 be the title",
+ "Description":"\uE000This should\uE001 be the content.",
+ "Url":"http://this.should.be.the.link/",
+ "DisplayUrl":"www.\uE000this.should.be.the\uE001.link",
+ "Id":"782ef287-e439-451c-b380-6ebc14ba033d"},
+ {"Title":"Datei:This should1.svg",
+ "Url":"https://i.swisscows.ch/?link=http%3a%2f%2fts2.mm.This/should1.png",
+ "SourceUrl":"http://de.wikipedia.org/wiki/Datei:This should1.svg",
+ "DisplayUrl":"de.wikipedia.org/wiki/Datei:This should1.svg",
+ "Width":950,
+ "Height":534,
+ "FileSize":92100,
+ "ContentType":"image/jpeg",
+ "Thumbnail":{
+ "Url":"https://i.swisscows.ch/?link=http%3a%2f%2fts2.mm.This/should1.png",
+ "ContentType":"image/jpeg",
+ "Width":300,
+ "Height":168,
+ "FileSize":9134},
+ "Id":"6a97a542-8f65-425f-b7f6-1178c3aba7be"
+ }
+ ],"TotalCount":55300,
+ "Query":"This should "
+ },
+ "Images":[{"Title":"Datei:This should.svg",
+ "Url":"https://i.swisscows.ch/?link=http%3a%2f%2fts2.mm.This/should.png",
+ "SourceUrl":"http://de.wikipedia.org/wiki/Datei:This should.svg",
+ "DisplayUrl":"de.wikipedia.org/wiki/Datei:This should.svg",
+ "Width":1280,
+ "Height":677,
+ "FileSize":50053,
+ "ContentType":"image/png",
+ "Thumbnail":{"Url":"https://i.swisscows.ch/?link=http%3a%2f%2fts2.mm.This/should.png",
+ "ContentType":"image/png",
+ "Width":300,
+ "Height":158,
+ "FileSize":8023},
+ "Id":"ae230fd8-a06a-47d6-99d5-e74766d8143a"}]},
+ environment: "production"
+ }).then(function (options) {
+ $('#Search_Form').on('submit', function (e) {
+ if (!Modernizr.history) return;
+ e.preventDefault();
+
+ var $form = $(this),
+ $query = $('#Query'),
+ query = $.trim($query.val()),
+ path = App.Router.makePath($form.attr('action'), null, $form.serializeObject())
+
+ if (query.length) {
+ options.html5history ?
+ ReactRouter.HistoryLocation.push(path) :
+ ReactRouter.RefreshLocation.push(path);
+ }
+ else $('#Query').trigger('blur');
+ });
+
+ });
+ </script>
+ """
+ response = mock.Mock(text=html)
+ results = swisscows.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 3)
+ self.assertEqual(results[0]['title'], 'This should be the title')
+ self.assertEqual(results[0]['url'], 'http://this.should.be.the.link/')
+ self.assertEqual(results[0]['content'], 'This should be the content.')
+ self.assertEqual(results[1]['title'], 'Datei:This should1.svg')
+ self.assertEqual(results[1]['url'], 'http://de.wikipedia.org/wiki/Datei:This should1.svg')
+ self.assertEqual(results[1]['img_src'], 'http://ts2.mm.This/should1.png')
+ self.assertEqual(results[1]['template'], 'images.html')
+ self.assertEqual(results[2]['title'], 'Datei:This should.svg')
+ self.assertEqual(results[2]['url'], 'http://de.wikipedia.org/wiki/Datei:This should.svg')
+ self.assertEqual(results[2]['img_src'], 'http://ts2.mm.This/should.png')
+ self.assertEqual(results[2]['template'], 'images.html')
+
+ def test_fetch_supported_languages(self):
+ html = """<html></html>"""
+ response = mock.Mock(text=html)
+ languages = swisscows._fetch_supported_languages(response)
+ self.assertEqual(type(languages), list)
+ self.assertEqual(len(languages), 0)
+
+ html = """
+ <html>
+ <div id="regions-popup">
+ <div>
+ <ul>
+ <li><a data-val="browser"></a></li>
+ <li><a data-val="de-CH"></a></li>
+ <li><a data-val="fr-CH"></a></li>
+ </ul>
+ </div>
+ </div>
+ </html>
+ """
+ response = mock.Mock(text=html)
+ languages = swisscows._fetch_supported_languages(response)
+ self.assertEqual(type(languages), list)
+ self.assertEqual(len(languages), 3)
+ self.assertIn('de-CH', languages)
+ self.assertIn('fr-CH', languages)
diff --git a/tests/unit/engines/test_tokyotoshokan.py b/tests/unit/engines/test_tokyotoshokan.py
new file mode 100644
index 0000000..b5c6fad
--- /dev/null
+++ b/tests/unit/engines/test_tokyotoshokan.py
@@ -0,0 +1,110 @@
+import mock
+from collections import defaultdict
+from searx.engines import tokyotoshokan
+from searx.testing import SearxTestCase
+from datetime import datetime
+
+
+class TestTokyotoshokanEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dic = defaultdict(dict)
+ dic['pageno'] = 1
+ params = tokyotoshokan.request(query, dic)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('tokyotosho.info' in params['url'])
+
+ def test_response(self):
+ resp = mock.Mock(text='<html></html>')
+ self.assertEqual(tokyotoshokan.response(resp), [])
+
+ html = """
+ <table class="listing">
+ <tbody>
+ <tr class="shade category_0">
+ <td rowspan="2">
+ <a href="/?cat=7"><span class="sprite_cat-raw"></span></a>
+ </td>
+ <td class="desc-top">
+ <a href="magnet:?xt=urn:btih:4c19eb46b5113685fbd2288ed2531b0b">
+ <span class="sprite_magnet"></span>
+ </a>
+ <a rel="nofollow" type="application/x-bittorrent" href="http://www.nyaa.se/f">
+ Koyomimonogatari
+ </a>
+ </td>
+ <td class="web"><a rel="nofollow" href="details.php?id=975700">Details</a></td>
+ </tr>
+ <tr class="shade category_0">
+ <td class="desc-bot">
+ Authorized: <span class="auth_ok">Yes</span>
+ Submitter: <a href="?username=Ohys">Ohys</a> |
+ Size: 10.5MB |
+ Date: 2016-03-26 16:41 UTC |
+ Comment: sample comment
+ </td>
+ <td style="color: #BBB; font-family: monospace" class="stats" align="right">
+ S: <span style="color: red">53</span>
+ L: <span style="color: red">18</span>
+ C: <span style="color: red">0</span>
+ ID: 975700
+ </td>
+ </tr>
+
+ <tr class="category_0">
+ <td rowspan="2">
+ <a href="/?cat=7"><span class="sprite_cat-raw"></span></a>
+ </td>
+ <td class="desc-top">
+ <a rel="nofollow" type="application/x-bittorrent" href="http://google.com/q">
+ Owarimonogatari
+ </a>
+ </td>
+ <td class="web"><a rel="nofollow" href="details.php?id=975700">Details</a></td>
+ </tr>
+ <tr class="category_0">
+ <td class="desc-bot">
+ Submitter: <a href="?username=Ohys">Ohys</a> |
+ Size: 932.84EB |
+ Date: QWERTY-03-26 16:41 UTC
+ </td>
+ <td style="color: #BBB; font-family: monospace" class="stats" align="right">
+ S: <span style="color: red">0</span>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ """
+
+ resp = mock.Mock(text=html)
+ results = tokyotoshokan.response(resp)
+
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+
+ # testing the first result, which has correct format
+ # and should have all information fields filled
+ r = results[0]
+ self.assertEqual(r['url'], 'http://www.nyaa.se/f')
+ self.assertEqual(r['title'], 'Koyomimonogatari')
+ self.assertEqual(r['magnetlink'], 'magnet:?xt=urn:btih:4c19eb46b5113685fbd2288ed2531b0b')
+ self.assertEqual(r['filesize'], int(1024 * 1024 * 10.5))
+ self.assertEqual(r['publishedDate'], datetime(2016, 3, 26, 16, 41))
+ self.assertEqual(r['content'], 'Comment: sample comment')
+ self.assertEqual(r['seed'], 53)
+ self.assertEqual(r['leech'], 18)
+
+ # testing the second result, which does not include magnet link,
+ # seed & leech info, and has incorrect size & creation date
+ r = results[1]
+ self.assertEqual(r['url'], 'http://google.com/q')
+ self.assertEqual(r['title'], 'Owarimonogatari')
+
+ self.assertFalse('magnetlink' in r)
+ self.assertFalse('filesize' in r)
+ self.assertFalse('content' in r)
+ self.assertFalse('publishedDate' in r)
+ self.assertFalse('seed' in r)
+ self.assertFalse('leech' in r)
diff --git a/tests/unit/engines/test_torrentz.py b/tests/unit/engines/test_torrentz.py
new file mode 100644
index 0000000..2f836f7
--- /dev/null
+++ b/tests/unit/engines/test_torrentz.py
@@ -0,0 +1,91 @@
+import mock
+from collections import defaultdict
+from searx.engines import torrentz
+from searx.testing import SearxTestCase
+from datetime import datetime
+
+
+class TestTorrentzEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dic = defaultdict(dict)
+ dic['pageno'] = 1
+ params = torrentz.request(query, dic)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('torrentz.eu' in params['url'])
+
+ def test_response(self):
+ resp = mock.Mock(text='<html></html>')
+ self.assertEqual(torrentz.response(resp), [])
+
+ html = """
+ <div class="results">
+ <dl>
+ <dt>
+ <a href="/4362e08b1d80e1820fb2550b752f9f3126fe76d6">
+ Completely valid info
+ </a>
+ books ebooks
+ </dt>
+ <dd>
+ <span class="v">1</span>
+ <span class="a">
+ <span title="Sun, 22 Nov 2015 03:01:42">4 months</span>
+ </span>
+ <span class="s">30 MB</span>
+ <span class="u">14</span>
+ <span class="d">1</span>
+ </dd>
+ </dl>
+
+ <dl>
+ <dt>
+ <a href="/poaskdpokaspod">
+ Invalid hash and date and filesize
+ </a>
+ books ebooks
+ </dt>
+ <dd>
+ <span class="v">1</span>
+ <span class="a">
+ <span title="Sun, 2124091j0j190gm42">4 months</span>
+ </span>
+ <span class="s">30MB</span>
+ <span class="u">5,555</span>
+ <span class="d">1,234,567</span>
+ </dd>
+ </dl>
+ </div>
+ """
+
+ resp = mock.Mock(text=html)
+ results = torrentz.response(resp)
+
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+
+ # testing against the first result
+ r = results[0]
+ self.assertEqual(r['url'], 'https://torrentz.eu/4362e08b1d80e1820fb2550b752f9f3126fe76d6')
+ self.assertEqual(r['title'], 'Completely valid info books ebooks')
+ # 22 Nov 2015 03:01:42
+ self.assertEqual(r['publishedDate'], datetime(2015, 11, 22, 3, 1, 42))
+ self.assertEqual(r['seed'], 14)
+ self.assertEqual(r['leech'], 1)
+ self.assertEqual(r['filesize'], 30 * 1024 * 1024)
+ self.assertEqual(r['magnetlink'], 'magnet:?xt=urn:btih:4362e08b1d80e1820fb2550b752f9f3126fe76d6')
+
+ # testing against the second result
+ r = results[1]
+ self.assertEqual(r['url'], 'https://torrentz.eu/poaskdpokaspod')
+ self.assertEqual(r['title'], 'Invalid hash and date and filesize books ebooks')
+ self.assertEqual(r['seed'], 5555)
+ self.assertEqual(r['leech'], 1234567)
+
+ # in the second result we have invalid hash, creation date & torrent size,
+ # so these tests should fail
+ self.assertFalse('magnetlink' in r)
+ self.assertFalse('filesize' in r)
+ self.assertFalse('publishedDate' in r)
diff --git a/tests/unit/engines/test_twitter.py b/tests/unit/engines/test_twitter.py
new file mode 100644
index 0000000..b444b48
--- /dev/null
+++ b/tests/unit/engines/test_twitter.py
@@ -0,0 +1,502 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import twitter
+from searx.testing import SearxTestCase
+
+
+class TestTwitterEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ dicto['language'] = 'fr_FR'
+ params = twitter.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('twitter.com', params['url'])
+ self.assertIn('cookies', params)
+ self.assertIn('lang', params['cookies'])
+ self.assertIn('fr', params['cookies']['lang'])
+
+ dicto['language'] = 'all'
+ params = twitter.request(query, dicto)
+ self.assertIn('cookies', params)
+ self.assertIn('lang', params['cookies'])
+ self.assertIn('en', params['cookies']['lang'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, twitter.response, None)
+ self.assertRaises(AttributeError, twitter.response, [])
+ self.assertRaises(AttributeError, twitter.response, '')
+ self.assertRaises(AttributeError, twitter.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(twitter.response(response), [])
+
+ html = """
+ <li class="js-stream-item stream-item stream-item expanding-stream-item" data-item-id="563005573290287105"
+ id="stream-item-tweet-563005573290287105" data-item-type="tweet">
+ <div class="tweet original-tweet js-stream-tweet js-actionable-tweet js-profile-popup-actionable
+ js-original-tweet has-cards has-native-media" data-tweet-id="563005573290287105" data-disclosure-type=""
+ data-item-id="563005573290287105" data-screen-name="Jalopnik" data-name="Jalopnik"
+ data-user-id="3060631" data-has-native-media="true" data-has-cards="true" data-card-type="photo"
+ data-expanded-footer="&lt;div class=&quot;js-tweet-details-fixer
+ tweet-details-fixer&quot;&gt;&#10;&#10;&#10;
+ &lt;div class=&quot;cards-media-container js-media-container&quot;&gt;&lt;div
+ data-card-url=&quot;//twitter.com/Jalopnik/status/563005573290287105/photo/1&quot; data-card-type=&quot;
+ photo&quot; class=&quot;cards-base cards-multimedia&quot; data-element-context=&quot;platform_photo_card
+ &quot;&gt;&#10;&#10;&#10; &lt;a class=&quot;media media-thumbnail twitter-timeline-link is-preview
+ &quot; data-url=&quot;https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg:large&quot;
+ data-resolved-url-large=&quot;https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg:large&quot;
+ href=&quot;//twitter.com/Jalopnik/status/563005573290287105/photo/1&quot;&gt;&#10;
+ &lt;div class=&quot;&quot;&gt;&#10; &lt;img src=&quot;
+ https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg&quot;
+ alt=&quot;Embedded image permalink&quot; width=&quot;636&quot; height=&quot;309&quot;&gt;&#10;
+ &lt;/div&gt;&#10;&#10; &lt;/a&gt;&#10;&#10; &lt;div class=&quot;cards-content&quot;&gt;&#10;
+ &lt;div class=&quot;byline&quot;&gt;&#10; &#10; &lt;/div&gt;&#10; &#10; &lt;/div&gt;&#10;
+ &#10;&lt;/div&gt;&#10;&#10;&#10;&#10;&#10;&lt;/div&gt;&#10;&#10;&#10;&#10; &lt;div
+ class=&quot;js-machine-translated-tweet-container&quot;&gt;&lt;/div&gt;&#10; &lt;div
+ class=&quot;js-tweet-stats-container tweet-stats-container &quot;&gt;&#10; &lt;/div&gt;&#10;&#10;
+ &lt;div class=&quot;client-and-actions&quot;&gt;&#10; &lt;span class=&quot;metadata&quot;&gt;&#10;
+ &lt;span&gt;5:06 PM - 4 Feb 2015&lt;/span&gt;&#10;&#10; &amp;middot; &lt;a
+ class=&quot;permalink-link js-permalink js-nav&quot; href=&quot;/Jalopnik/status/563005573290287105
+ &quot;tabindex=&quot;-1&quot;&gt;Details&lt;/a&gt;&#10; &#10;&#10; &#10; &#10;
+ &#10;&#10; &lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&#10;&lt;/div&gt;&#10;" data-you-follow="false"
+ data-you-block="false">
+ <div class="context">
+ </div>
+ <div class="content">
+ <div class="stream-item-header">
+ <a class="account-group js-account-group js-action-profile js-user-profile-link js-nav"
+ href="/Jalopnik" data-user-id="3060631">
+ <img class="avatar js-action-profile-avatar"
+ src="https://pbs.twimg.com/profile_images/2976430168/5cd4a59_bigger.jpeg" alt="">
+ <strong class="fullname js-action-profile-name show-popup-with-id" data-aria-label-part>
+ Jalopnik
+ </strong>
+ <span>&rlm;</span>
+ <span class="username js-action-profile-name" data-aria-label-part>
+ <s>@</s><b>TitleName</b>
+ </span>
+ </a>
+ <small class="time">
+ <a href="/this.is.the.url"
+ class="tweet-timestamp js-permalink js-nav js-tooltip" title="5:06 PM - 4 Feb 2015" >
+ <span class="u-hiddenVisually" data-aria-label-part="last">17 minutes ago</span>
+ </a>
+ </small>
+ </div>
+ <p class="js-tweet-text tweet-text" lang="en" data-aria-label-part="0">
+ This is the content étude à€
+ <a href="http://t.co/nRWsqQAwBL" rel="nofollow" dir="ltr"
+ data-expanded-url="http://jalo.ps/ReMENu4" class="twitter-timeline-link"
+ target="_blank" title="http://jalo.ps/ReMENu4" >
+ <span class="tco-ellipsis">
+ </span>
+ <span class="invisible">http://</span><span class="js-display-url">link.in.tweet</span>
+ <span class="invisible"></span>
+ <span class="tco-ellipsis">
+ <span class="invisible">&nbsp;</span>
+ </span>
+ </a>
+ <a href="http://t.co/rbFsfeE0l3" class="twitter-timeline-link u-hidden"
+ data-pre-embedded="true" dir="ltr">
+ pic.twitter.com/rbFsfeE0l3
+ </a>
+ </p>
+ <div class="expanded-content js-tweet-details-dropdown">
+ </div>
+ <div class="stream-item-footer">
+ <a class="details with-icn js-details" href="/Jalopnik/status/563005573290287105">
+ <span class="Icon Icon--photo">
+ </span>
+ <b>
+ <span class="expand-stream-item js-view-details">
+ View photo
+ </span>
+ <span class="collapse-stream-item js-hide-details">
+ Hide photo
+ </span>
+ </b>
+ </a>
+ <span class="ProfileTweet-action--reply u-hiddenVisually">
+ <span class="ProfileTweet-actionCount" aria-hidden="true" data-tweet-stat-count="0">
+ <span class="ProfileTweet-actionCountForAria" >0 replies</span>
+ </span>
+ </span>
+ <span class="ProfileTweet-action--retweet u-hiddenVisually">
+ <span class="ProfileTweet-actionCount" data-tweet-stat-count="8">
+ <span class="ProfileTweet-actionCountForAria" data-aria-label-part>8 retweets</span>
+ </span>
+ </span>
+ <span class="ProfileTweet-action--favorite u-hiddenVisually">
+ <span class="ProfileTweet-actionCount" data-tweet-stat-count="14">
+ <span class="ProfileTweet-actionCountForAria" data-aria-label-part>14 favorites</span>
+ </span>
+ </span>
+ <div role="group" aria-label="Tweet actions" class="ProfileTweet-actionList u-cf js-actions">
+ <div class="ProfileTweet-action ProfileTweet-action--reply">
+ <button class="ProfileTweet-actionButton u-textUserColorHover js-actionButton
+ js-actionReply" data-modal="ProfileTweet-reply" type="button" title="Reply">
+ <span class="Icon Icon--reply">
+ </span>
+ <span class="u-hiddenVisually">Reply</span>
+ <span class="ProfileTweet-actionCount u-textUserColorHover
+ ProfileTweet-actionCount--isZero">
+ <span class="ProfileTweet-actionCountForPresentation" aria-hidden="true">
+ </span>
+ </span>
+ </button>
+ </div>
+ <div class="ProfileTweet-action ProfileTweet-action--retweet js-toggleState js-toggleRt">
+ <button class="ProfileTweet-actionButton js-actionButton js-actionRetweet js-tooltip"
+ title="Retweet" data-modal="ProfileTweet-retweet" type="button">
+ <span class="Icon Icon--retweet">
+ </span>
+ <span class="u-hiddenVisually">Retweet</span>
+ <span class="ProfileTweet-actionCount">
+ <span class="ProfileTweet-actionCountForPresentation">8</span>
+ </span>
+ </button>
+ <button class="ProfileTweet-actionButtonUndo js-actionButton js-actionRetweet"
+ data-modal="ProfileTweet-retweet" title="Undo retweet" type="button">
+ <span class="Icon Icon--retweet">
+ </span>
+ <span class="u-hiddenVisually">Retweeted</span>
+ <span class="ProfileTweet-actionCount">
+ <span class="ProfileTweet-actionCountForPresentation">8</span>
+ </span>
+ </button>
+ </div>
+ <div class="ProfileTweet-action ProfileTweet-action--favorite js-toggleState">
+ <button class="ProfileTweet-actionButton js-actionButton js-actionFavorite js-tooltip"
+ title="Favorite" type="button">
+ <span class="Icon Icon--favorite">
+ </span>
+ <span class="u-hiddenVisually">Favorite</span>
+ <span class="ProfileTweet-actionCount">
+ <span class="ProfileTweet-actionCountForPresentation">14</span>
+ </span>
+ </button>
+ <button class="ProfileTweet-actionButtonUndo u-linkClean js-actionButton
+ js-actionFavorite" title="Undo favorite" type="button">
+ <span class="Icon Icon--favorite">
+ </span>
+ <span class="u-hiddenVisually">Favorited</span>
+ <span class="ProfileTweet-actionCount">
+ <span class="ProfileTweet-actionCountForPresentation">
+ 14
+ </span>
+ </span>
+ </button>
+ </div>
+ <div class="ProfileTweet-action ProfileTweet-action--more js-more-ProfileTweet-actions">
+ <div class="dropdown">
+ <button class="ProfileTweet-actionButton u-textUserColorHover dropdown-toggle
+ js-tooltip js-dropdown-toggle" type="button" title="More">
+ <span class="Icon Icon--dots">
+ </span>
+ <span class="u-hiddenVisually">More</span>
+ </button>
+ <div class="dropdown-menu">
+ <div class="dropdown-caret">
+ <div class="caret-outer">
+ </div>
+ <div class="caret-inner">
+ </div>
+ </div>
+ <ul>
+ <li class="share-via-dm js-actionShareViaDM" data-nav="share_tweet_dm">
+ <button type="button" class="dropdown-link">
+ Share via Direct Message
+ </button>
+ </li>
+ <li class="embed-link js-actionEmbedTweet" data-nav="embed_tweet">
+ <button type="button" class="dropdown-link">
+ Embed Tweet
+ </button>
+ </li>
+ <li class="mute-user-item pretty-link">
+ <button type="button" class="dropdown-link">
+ Mute
+ </button>
+ </li>
+ <li class="unmute-user-item pretty-link">
+ <button type="button" class="dropdown-link">
+ Unmute
+ </button>
+ </li>
+ <li class="block-or-report-link js-actionBlockOrReport"
+ data-nav="block_or_report">
+ <button type="button" class="dropdown-link">
+ Block or report
+ </button>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ """
+ response = mock.Mock(text=html)
+ results = twitter.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], '@TitleName')
+ self.assertEqual(results[0]['url'], 'https://twitter.com/this.is.the.url')
+ self.assertIn(u'This is the content', results[0]['content'])
+ # self.assertIn(u'This is the content étude à€', results[0]['content'])
+
+ html = """
+ <li class="js-stream-item stream-item stream-item expanding-stream-item" data-item-id="563005573290287105"
+ id="stream-item-tweet-563005573290287105" data-item-type="tweet">
+ <div class="tweet original-tweet js-stream-tweet js-actionable-tweet js-profile-popup-actionable
+ js-original-tweet has-cards has-native-media" data-tweet-id="563005573290287105" data-disclosure-type=""
+ data-item-id="563005573290287105" data-screen-name="Jalopnik" data-name="Jalopnik"
+ data-user-id="3060631" data-has-native-media="true" data-has-cards="true" data-card-type="photo"
+ data-expanded-footer="&lt;div class=&quot;js-tweet-details-fixer
+ tweet-details-fixer&quot;&gt;&#10;&#10;&#10;
+ &lt;div class=&quot;cards-media-container js-media-container&quot;&gt;&lt;div
+ data-card-url=&quot;//twitter.com/Jalopnik/status/563005573290287105/photo/1&quot; data-card-type=&quot;
+ photo&quot; class=&quot;cards-base cards-multimedia&quot; data-element-context=&quot;platform_photo_card
+ &quot;&gt;&#10;&#10;&#10; &lt;a class=&quot;media media-thumbnail twitter-timeline-link is-preview
+ &quot; data-url=&quot;https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg:large&quot;
+ data-resolved-url-large=&quot;https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg:large&quot;
+ href=&quot;//twitter.com/Jalopnik/status/563005573290287105/photo/1&quot;&gt;&#10;
+ &lt;div class=&quot;&quot;&gt;&#10; &lt;img src=&quot;
+ https://pbs.twimg.com/media/B9Aylf5IMAAuziP.jpg&quot;
+ alt=&quot;Embedded image permalink&quot; width=&quot;636&quot; height=&quot;309&quot;&gt;&#10;
+ &lt;/div&gt;&#10;&#10; &lt;/a&gt;&#10;&#10; &lt;div class=&quot;cards-content&quot;&gt;&#10;
+ &lt;div class=&quot;byline&quot;&gt;&#10; &#10; &lt;/div&gt;&#10; &#10; &lt;/div&gt;&#10;
+ &#10;&lt;/div&gt;&#10;&#10;&#10;&#10;&#10;&lt;/div&gt;&#10;&#10;&#10;&#10; &lt;div
+ class=&quot;js-machine-translated-tweet-container&quot;&gt;&lt;/div&gt;&#10; &lt;div
+ class=&quot;js-tweet-stats-container tweet-stats-container &quot;&gt;&#10; &lt;/div&gt;&#10;&#10;
+ &lt;div class=&quot;client-and-actions&quot;&gt;&#10; &lt;span class=&quot;metadata&quot;&gt;&#10;
+ &lt;span&gt;5:06 PM - 4 Feb 2015&lt;/span&gt;&#10;&#10; &amp;middot; &lt;a
+ class=&quot;permalink-link js-permalink js-nav&quot; href=&quot;/Jalopnik/status/563005573290287105
+ &quot;tabindex=&quot;-1&quot;&gt;Details&lt;/a&gt;&#10; &#10;&#10; &#10; &#10;
+ &#10;&#10; &lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&#10;&lt;/div&gt;&#10;" data-you-follow="false"
+ data-you-block="false">
+ <div class="context">
+ </div>
+ <div class="content">
+ <div class="stream-item-header">
+ <a class="account-group js-account-group js-action-profile js-user-profile-link js-nav"
+ href="/Jalopnik" data-user-id="3060631">
+ <img class="avatar js-action-profile-avatar"
+ src="https://pbs.twimg.com/profile_images/2976430168/5cd4a59_bigger.jpeg" alt="">
+ <strong class="fullname js-action-profile-name show-popup-with-id" data-aria-label-part>
+ Jalopnik
+ </strong>
+ <span>&rlm;</span>
+ <span class="username js-action-profile-name" data-aria-label-part>
+ <s>@</s><b>TitleName</b>
+ </span>
+ </a>
+ <small class="time">
+ <a href="/this.is.the.url"
+ class="tweet-timestamp js-permalink js-nav js-tooltip" title="5:06 PM - 4 Feb 2015" >
+ <span class="_timestamp js-short-timestamp js-relative-timestamp" data-time="1423065963"
+ data-time-ms="1423065963000" data-long-form="true" aria-hidden="true">
+ 17m
+ </span>
+ <span class="u-hiddenVisually" data-aria-label-part="last">17 minutes ago</span>
+ </a>
+ </small>
+ </div>
+ <p class="js-tweet-text tweet-text" lang="en" data-aria-label-part="0">
+ This is the content étude à€
+ <a href="http://t.co/nRWsqQAwBL" rel="nofollow" dir="ltr"
+ data-expanded-url="http://jalo.ps/ReMENu4" class="twitter-timeline-link"
+ target="_blank" title="http://jalo.ps/ReMENu4" >
+ <span class="tco-ellipsis">
+ </span>
+ <span class="invisible">http://</span><span class="js-display-url">link.in.tweet</span>
+ <span class="invisible"></span>
+ <span class="tco-ellipsis">
+ <span class="invisible">&nbsp;</span>
+ </span>
+ </a>
+ <a href="http://t.co/rbFsfeE0l3" class="twitter-timeline-link u-hidden"
+ data-pre-embedded="true" dir="ltr">
+ pic.twitter.com/rbFsfeE0l3
+ </a>
+ </p>
+ <div class="expanded-content js-tweet-details-dropdown">
+ </div>
+ <div class="stream-item-footer">
+ <a class="details with-icn js-details" href="/Jalopnik/status/563005573290287105">
+ <span class="Icon Icon--photo">
+ </span>
+ <b>
+ <span class="expand-stream-item js-view-details">
+ View photo
+ </span>
+ <span class="collapse-stream-item js-hide-details">
+ Hide photo
+ </span>
+ </b>
+ </a>
+ <span class="ProfileTweet-action--reply u-hiddenVisually">
+ <span class="ProfileTweet-actionCount" aria-hidden="true" data-tweet-stat-count="0">
+ <span class="ProfileTweet-actionCountForAria" >0 replies</span>
+ </span>
+ </span>
+ <span class="ProfileTweet-action--retweet u-hiddenVisually">
+ <span class="ProfileTweet-actionCount" data-tweet-stat-count="8">
+ <span class="ProfileTweet-actionCountForAria" data-aria-label-part>8 retweets</span>
+ </span>
+ </span>
+ <span class="ProfileTweet-action--favorite u-hiddenVisually">
+ <span class="ProfileTweet-actionCount" data-tweet-stat-count="14">
+ <span class="ProfileTweet-actionCountForAria" data-aria-label-part>14 favorites</span>
+ </span>
+ </span>
+ <div role="group" aria-label="Tweet actions" class="ProfileTweet-actionList u-cf js-actions">
+ <div class="ProfileTweet-action ProfileTweet-action--reply">
+ <button class="ProfileTweet-actionButton u-textUserColorHover js-actionButton
+ js-actionReply" data-modal="ProfileTweet-reply" type="button" title="Reply">
+ <span class="Icon Icon--reply">
+ </span>
+ <span class="u-hiddenVisually">Reply</span>
+ <span class="ProfileTweet-actionCount u-textUserColorHover
+ ProfileTweet-actionCount--isZero">
+ <span class="ProfileTweet-actionCountForPresentation" aria-hidden="true">
+ </span>
+ </span>
+ </button>
+ </div>
+ <div class="ProfileTweet-action ProfileTweet-action--retweet js-toggleState js-toggleRt">
+ <button class="ProfileTweet-actionButton js-actionButton js-actionRetweet js-tooltip"
+ title="Retweet" data-modal="ProfileTweet-retweet" type="button">
+ <span class="Icon Icon--retweet">
+ </span>
+ <span class="u-hiddenVisually">Retweet</span>
+ <span class="ProfileTweet-actionCount">
+ <span class="ProfileTweet-actionCountForPresentation">8</span>
+ </span>
+ </button>
+ <button class="ProfileTweet-actionButtonUndo js-actionButton js-actionRetweet"
+ data-modal="ProfileTweet-retweet" title="Undo retweet" type="button">
+ <span class="Icon Icon--retweet">
+ </span>
+ <span class="u-hiddenVisually">Retweeted</span>
+ <span class="ProfileTweet-actionCount">
+ <span class="ProfileTweet-actionCountForPresentation">8</span>
+ </span>
+ </button>
+ </div>
+ <div class="ProfileTweet-action ProfileTweet-action--favorite js-toggleState">
+ <button class="ProfileTweet-actionButton js-actionButton js-actionFavorite js-tooltip"
+ title="Favorite" type="button">
+ <span class="Icon Icon--favorite">
+ </span>
+ <span class="u-hiddenVisually">Favorite</span>
+ <span class="ProfileTweet-actionCount">
+ <span class="ProfileTweet-actionCountForPresentation">14</span>
+ </span>
+ </button>
+ <button class="ProfileTweet-actionButtonUndo u-linkClean js-actionButton
+ js-actionFavorite" title="Undo favorite" type="button">
+ <span class="Icon Icon--favorite">
+ </span>
+ <span class="u-hiddenVisually">Favorited</span>
+ <span class="ProfileTweet-actionCount">
+ <span class="ProfileTweet-actionCountForPresentation">
+ 14
+ </span>
+ </span>
+ </button>
+ </div>
+ <div class="ProfileTweet-action ProfileTweet-action--more js-more-ProfileTweet-actions">
+ <div class="dropdown">
+ <button class="ProfileTweet-actionButton u-textUserColorHover dropdown-toggle
+ js-tooltip js-dropdown-toggle" type="button" title="More">
+ <span class="Icon Icon--dots">
+ </span>
+ <span class="u-hiddenVisually">More</span>
+ </button>
+ <div class="dropdown-menu">
+ <div class="dropdown-caret">
+ <div class="caret-outer">
+ </div>
+ <div class="caret-inner">
+ </div>
+ </div>
+ <ul>
+ <li class="share-via-dm js-actionShareViaDM" data-nav="share_tweet_dm">
+ <button type="button" class="dropdown-link">
+ Share via Direct Message
+ </button>
+ </li>
+ <li class="embed-link js-actionEmbedTweet" data-nav="embed_tweet">
+ <button type="button" class="dropdown-link">
+ Embed Tweet
+ </button>
+ </li>
+ <li class="mute-user-item pretty-link">
+ <button type="button" class="dropdown-link">
+ Mute
+ </button>
+ </li>
+ <li class="unmute-user-item pretty-link">
+ <button type="button" class="dropdown-link">
+ Unmute
+ </button>
+ </li>
+ <li class="block-or-report-link js-actionBlockOrReport"
+ data-nav="block_or_report">
+ <button type="button" class="dropdown-link">
+ Block or report
+ </button>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ """
+ response = mock.Mock(text=html)
+ results = twitter.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], '@TitleName')
+ self.assertEqual(results[0]['url'], 'https://twitter.com/this.is.the.url')
+ self.assertIn(u'This is the content', results[0]['content'])
+
+ html = """
+ <li class="b_algo" u="0|5109|4755453613245655|UAGjXgIrPH5yh-o5oNHRx_3Zta87f_QO">
+ <div Class="sa_mc">
+ <div class="sb_tlst">
+ <h2>
+ <a href="http://this.should.be.the.link/" h="ID=SERP,5124.1">
+ <strong>This</strong> should be the title</a>
+ </h2>
+ </div>
+ <div class="sb_meta">
+ <cite>
+ <strong>this</strong>.meta.com</cite>
+ <span class="c_tlbxTrg">
+ <span class="c_tlbxH" H="BASE:CACHEDPAGEDEFAULT" K="SERP,5125.1">
+ </span>
+ </span>
+ </div>
+ <p>
+ <strong>This</strong> should be the content.</p>
+ </div>
+ </li>
+ """
+ response = mock.Mock(text=html)
+ results = twitter.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_vimeo.py b/tests/unit/engines/test_vimeo.py
new file mode 100644
index 0000000..c86b50a
--- /dev/null
+++ b/tests/unit/engines/test_vimeo.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import vimeo
+from searx.testing import SearxTestCase
+
+
+class TestVimeoEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ params = vimeo.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('vimeo.com' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, vimeo.response, None)
+ self.assertRaises(AttributeError, vimeo.response, [])
+ self.assertRaises(AttributeError, vimeo.response, '')
+ self.assertRaises(AttributeError, vimeo.response, '[]')
+
+ json = u"""
+{"filtered":{"total":274641,"page":1,"per_page":18,"paging":{"next":"?sizes=590x332&page=2","previous":null,"first":"?sizes=590x332&page=1","last":"?sizes=590x332&page=15258"},"data":[{"is_staffpick":false,"is_featured":true,"type":"clip","clip":{"uri":"\\/videos\\/106557563","name":"Hot Rod Revue: The South","link":"https:\\/\\/vimeo.com\\/106557563","duration":4069,"created_time":"2014-09-19T03:38:04+00:00","privacy":{"view":"ptv"},"pictures":{"sizes":[{"width":"590","height":"332","link":"https:\\/\\/i.vimeocdn.com\\/video\\/489717884_590x332.jpg?r=pad","link_with_play_button":"https:\\/\\/i.vimeocdn.com\\/filter\\/overlay?src0=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F489717884_590x332.jpg&src1=http%3A%2F%2Ff.vimeocdn.com%2Fp%2Fimages%2Fcrawler_play.png"}]},"stats":{"plays":null},"metadata":{"connections":{"comments":{"total":0},"likes":{"total":5}},"interactions":[]},"user":{"name":"Cal Thorley","link":"https:\\/\\/vimeo.com\\/calthorley","pictures":{"sizes":[{"width":30,"height":30,"link":"https:\\/\\/i.vimeocdn.com\\/portrait\\/2545308_30x30?r=pad"},{"width":75,"height":75,"link":"https:\\/\\/i.vimeocdn.com\\/portrait\\/2545308_75x75?r=pad"},{"width":100,"height":100,"link":"https:\\/\\/i.vimeocdn.com\\/portrait\\/2545308_100x100?r=pad"},{"width":300,"height":300,"link":"https:\\/\\/i.vimeocdn.com\\/portrait\\/2545308_300x300?r=pad"}]}}}}]}};
+
+""" # noqa
+ response = mock.Mock(text=json)
+ results = vimeo.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], u'Hot Rod Revue: The South')
+ self.assertEqual(results[0]['url'], 'https://vimeo.com/106557563')
+ self.assertEqual(results[0]['content'], '')
+ self.assertEqual(results[0]['thumbnail'], 'https://i.vimeocdn.com/video/489717884_590x332.jpg?r=pad')
diff --git a/tests/unit/engines/test_wikidata.py b/tests/unit/engines/test_wikidata.py
new file mode 100644
index 0000000..aa69f11
--- /dev/null
+++ b/tests/unit/engines/test_wikidata.py
@@ -0,0 +1,503 @@
+# -*- coding: utf-8 -*-
+from lxml.html import fromstring
+from collections import defaultdict
+import mock
+from searx.engines import wikidata
+from searx.testing import SearxTestCase
+
+
+class TestWikidataEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['language'] = 'all'
+ params = wikidata.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('wikidata.org', params['url'])
+ self.assertIn('en', params['url'])
+
+ dicto['language'] = 'es_ES'
+ params = wikidata.request(query, dicto)
+ self.assertIn(query, params['url'])
+ self.assertIn('es', params['url'])
+
+ # successful cases are not tested here to avoid sending additional requests
+ def test_response(self):
+ self.assertRaises(AttributeError, wikidata.response, None)
+ self.assertRaises(AttributeError, wikidata.response, [])
+ self.assertRaises(AttributeError, wikidata.response, '')
+ self.assertRaises(AttributeError, wikidata.response, '[]')
+
+ response = mock.Mock(text='<html></html>', search_params={"language": "all"})
+ self.assertEqual(wikidata.response(response), [])
+
+ def test_getDetail(self):
+ response = {}
+ results = wikidata.getDetail(response, "Q123", "en", "en-US")
+ self.assertEqual(results, [])
+
+ title_html = '<div><div class="wikibase-title-label">Test</div></div>'
+ html = """
+ <div>
+ <div class="wikibase-entitytermsview-heading-description">
+ </div>
+ <div>
+ <ul class="wikibase-sitelinklistview-listview">
+ <li data-wb-siteid="enwiki"><a href="http://en.wikipedia.org/wiki/Test">Test</a></li>
+ </ul>
+ </div>
+ </div>
+ """
+ response = {"parse": {"displaytitle": title_html, "text": html}}
+
+ results = wikidata.getDetail(response, "Q123", "en", "en-US")
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['url'], 'https://en.wikipedia.org/wiki/Test')
+
+ title_html = """
+ <div>
+ <div class="wikibase-title-label">
+ <span lang="en">Test</span>
+ <sup class="wb-language-fallback-indicator">English</sup>
+ </div>
+ </div>
+ """
+ html = """
+ <div>
+ <div class="wikibase-entitytermsview-heading-description">
+ <span lang="en">Description</span>
+ <sup class="wb-language-fallback-indicator">English</sup>
+ </div>
+ <div id="P856">
+ <div class="wikibase-statementgroupview-property-label">
+ <a href="/wiki/Property:P856">
+ <span lang="en">official website</span>
+ <sup class="wb-language-fallback-indicator">English</sup>
+ </a>
+ </div>
+ <div class="wikibase-statementview-mainsnak">
+ <a class="external free" href="https://officialsite.com">
+ https://officialsite.com
+ </a>
+ </div>
+ </div>
+ <div>
+ <ul class="wikibase-sitelinklistview-listview">
+ <li data-wb-siteid="enwiki"><a href="http://en.wikipedia.org/wiki/Test">Test</a></li>
+ </ul>
+ </div>
+ </div>
+ """
+ response = {"parse": {"displaytitle": title_html, "text": html}}
+
+ results = wikidata.getDetail(response, "Q123", "yua", "yua_MX")
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], 'Official website')
+ self.assertEqual(results[0]['url'], 'https://officialsite.com')
+
+ self.assertEqual(results[1]['infobox'], 'Test')
+ self.assertEqual(results[1]['id'], None)
+ self.assertEqual(results[1]['content'], 'Description')
+ self.assertEqual(results[1]['attributes'], [])
+ self.assertEqual(results[1]['urls'][0]['title'], 'Official website')
+ self.assertEqual(results[1]['urls'][0]['url'], 'https://officialsite.com')
+ self.assertEqual(results[1]['urls'][1]['title'], 'Wikipedia (en)')
+ self.assertEqual(results[1]['urls'][1]['url'], 'https://en.wikipedia.org/wiki/Test')
+
+ def test_add_image(self):
+ image_src = wikidata.add_image(fromstring("<div></div>"))
+ self.assertEqual(image_src, None)
+
+ html = u"""
+ <div>
+ <div id="P18">
+ <div class="wikibase-statementgroupview-property-label">
+ <a href="/wiki/Property:P18">
+ image
+ </a>
+ </div>
+ <div class="wikibase-statementlistview">
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-rankselector">
+ <span class="wikibase-rankselector-normal"></span>
+ </div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a href="https://commons.wikimedia.org/wiki/File:image.png">
+ image.png
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ html_etree = fromstring(html)
+
+ image_src = wikidata.add_image(html_etree)
+ self.assertEqual(image_src,
+ "https://commons.wikimedia.org/wiki/Special:FilePath/image.png?width=500&height=400")
+
+ html = u"""
+ <div>
+ <div id="P2910">
+ <div class="wikibase-statementgroupview-property-label">
+ <a href="/wiki/Property:P2910">
+ icon
+ </a>
+ </div>
+ <div class="wikibase-statementlistview">
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-rankselector">
+ <span class="wikibase-rankselector-normal"></span>
+ </div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a href="https://commons.wikimedia.org/wiki/File:icon.png">
+ icon.png
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="P154">
+ <div class="wikibase-statementgroupview-property-label">
+ <a href="/wiki/Property:P154">
+ logo
+ </a>
+ </div>
+ <div class="wikibase-statementlistview">
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-rankselector">
+ <span class="wikibase-rankselector-normal"></span>
+ </div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a href="https://commons.wikimedia.org/wiki/File:logo.png">
+ logo.png
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ html_etree = fromstring(html)
+
+ image_src = wikidata.add_image(html_etree)
+ self.assertEqual(image_src,
+ "https://commons.wikimedia.org/wiki/Special:FilePath/logo.png?width=500&height=400")
+
+ def test_add_attribute(self):
+ html = u"""
+ <div>
+ <div id="P27">
+ <div class="wikibase-statementgroupview-property-label">
+ <a href="/wiki/Property:P27">
+ country of citizenship
+ </a>
+ </div>
+ <div class="wikibase-statementlistview">
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-rankselector">
+ <span class="wikibase-rankselector-normal"></span>
+ </div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a href="/wiki/Q145">
+ United Kingdom
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ attributes = []
+ html_etree = fromstring(html)
+
+ wikidata.add_attribute(attributes, html_etree, "Fail")
+ self.assertEqual(attributes, [])
+
+ wikidata.add_attribute(attributes, html_etree, "P27")
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes[0]["label"], "Country of citizenship")
+ self.assertEqual(attributes[0]["value"], "United Kingdom")
+
+ html = u"""
+ <div>
+ <div id="P569">
+ <div class="wikibase-statementgroupview-property-label">
+ <a href="/wiki/Property:P569">
+ date of birth
+ </a>
+ </div>
+ <div class="wikibase-statementlistview">
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-rankselector">
+ <span class="wikibase-rankselector-normal"></span>
+ </div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ 27 January 1832
+ <sup class="wb-calendar-name">
+ Gregorian
+ </sup>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ attributes = []
+ html_etree = fromstring(html)
+ wikidata.add_attribute(attributes, html_etree, "P569", date=True)
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes[0]["label"], "Date of birth")
+ self.assertEqual(attributes[0]["value"], "27 January 1832")
+
+ html = u"""
+ <div>
+ <div id="P6">
+ <div class="wikibase-statementgroupview-property-label">
+ <a href="/wiki/Property:P27">
+ head of government
+ </a>
+ </div>
+ <div class="wikibase-statementlistview">
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-rankselector">
+ <span class="wikibase-rankselector-normal"></span>
+ </div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a href="/wiki/Q206">
+ Old Prime Minister
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-rankselector">
+ <span class="wikibase-rankselector-preferred"></span>
+ </div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a href="/wiki/Q3099714">
+ Actual Prime Minister
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ attributes = []
+ html_etree = fromstring(html)
+ wikidata.add_attribute(attributes, html_etree, "P6")
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes[0]["label"], "Head of government")
+ self.assertEqual(attributes[0]["value"], "Old Prime Minister, Actual Prime Minister")
+
+ attributes = []
+ html_etree = fromstring(html)
+ wikidata.add_attribute(attributes, html_etree, "P6", trim=True)
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes[0]["value"], "Actual Prime Minister")
+
+ def test_add_url(self):
+ html = u"""
+ <div>
+ <div id="P856">
+ <div class="wikibase-statementgroupview-property-label">
+ <a href="/wiki/Property:P856">
+ official website
+ </a>
+ </div>
+ <div class="wikibase-statementlistview">
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a class="external free" href="https://searx.me">
+ https://searx.me/
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ urls = []
+ html_etree = fromstring(html)
+ wikidata.add_url(urls, html_etree, 'P856')
+ self.assertEquals(len(urls), 1)
+ self.assertIn({'title': 'Official website', 'url': 'https://searx.me/'}, urls)
+ urls = []
+ results = []
+ wikidata.add_url(urls, html_etree, 'P856', 'custom label', results=results)
+ self.assertEquals(len(urls), 1)
+ self.assertEquals(len(results), 1)
+ self.assertIn({'title': 'custom label', 'url': 'https://searx.me/'}, urls)
+ self.assertIn({'title': 'custom label', 'url': 'https://searx.me/'}, results)
+
+ html = u"""
+ <div>
+ <div id="P856">
+ <div class="wikibase-statementgroupview-property-label">
+ <a href="/wiki/Property:P856">
+ official website
+ </a>
+ </div>
+ <div class="wikibase-statementlistview">
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a class="external free" href="http://www.worldofwarcraft.com">
+ http://www.worldofwarcraft.com
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="wikibase-statementview listview-item">
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a class="external free" href="http://eu.battle.net/wow/en/">
+ http://eu.battle.net/wow/en/
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ urls = []
+ html_etree = fromstring(html)
+ wikidata.add_url(urls, html_etree, 'P856')
+ self.assertEquals(len(urls), 2)
+ self.assertIn({'title': 'Official website', 'url': 'http://www.worldofwarcraft.com'}, urls)
+ self.assertIn({'title': 'Official website', 'url': 'http://eu.battle.net/wow/en/'}, urls)
+
+ def test_get_imdblink(self):
+ html = u"""
+ <div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a class="wb-external-id" href="http://www.imdb.com/tt0433664">
+ tt0433664
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ html_etree = fromstring(html)
+ imdblink = wikidata.get_imdblink(html_etree, 'https://www.imdb.com/')
+
+ html = u"""
+ <div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ <a class="wb-external-id"
+ href="href="http://tools.wmflabs.org/...http://www.imdb.com/&id=nm4915994"">
+ nm4915994
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ html_etree = fromstring(html)
+ imdblink = wikidata.get_imdblink(html_etree, 'https://www.imdb.com/')
+ self.assertIn('https://www.imdb.com/name/nm4915994', imdblink)
+
+ def test_get_geolink(self):
+ html = u"""
+ <div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ 60°N, 40°E
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ html_etree = fromstring(html)
+ geolink = wikidata.get_geolink(html_etree)
+ self.assertIn('https://www.openstreetmap.org/', geolink)
+ self.assertIn('lat=60&lon=40', geolink)
+
+ html = u"""
+ <div>
+ <div class="wikibase-statementview-mainsnak">
+ <div>
+ <div class="wikibase-snakview-value">
+ 34°35'59"S, 58°22'55"W
+ </div>
+ </div>
+ </div>
+ </div>
+ """
+ html_etree = fromstring(html)
+ geolink = wikidata.get_geolink(html_etree)
+ self.assertIn('https://www.openstreetmap.org/', geolink)
+ self.assertIn('lat=-34.59', geolink)
+ self.assertIn('lon=-58.38', geolink)
+
+ def test_get_wikilink(self):
+ html = """
+ <div>
+ <div>
+ <ul class="wikibase-sitelinklistview-listview">
+ <li data-wb-siteid="arwiki"><a href="http://ar.wikipedia.org/wiki/Test">Test</a></li>
+ <li data-wb-siteid="enwiki"><a href="http://en.wikipedia.org/wiki/Test">Test</a></li>
+ </ul>
+ </div>
+ <div>
+ <ul class="wikibase-sitelinklistview-listview">
+ <li data-wb-siteid="enwikiquote"><a href="https://en.wikiquote.org/wiki/Test">Test</a></li>
+ </ul>
+ </div>
+ </div>
+ """
+ html_etree = fromstring(html)
+ wikilink = wikidata.get_wikilink(html_etree, 'nowiki')
+ self.assertEqual(wikilink, None)
+ wikilink = wikidata.get_wikilink(html_etree, 'enwiki')
+ self.assertEqual(wikilink, 'https://en.wikipedia.org/wiki/Test')
+ wikilink = wikidata.get_wikilink(html_etree, 'arwiki')
+ self.assertEqual(wikilink, 'https://ar.wikipedia.org/wiki/Test')
+ wikilink = wikidata.get_wikilink(html_etree, 'enwikiquote')
+ self.assertEqual(wikilink, 'https://en.wikiquote.org/wiki/Test')
diff --git a/tests/unit/engines/test_wikipedia.py b/tests/unit/engines/test_wikipedia.py
new file mode 100644
index 0000000..7a86514
--- /dev/null
+++ b/tests/unit/engines/test_wikipedia.py
@@ -0,0 +1,259 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import wikipedia
+from searx.testing import SearxTestCase
+
+
+class TestWikipediaEngine(SearxTestCase):
+
+ def test_request(self):
+ wikipedia.supported_languages = ['fr', 'en']
+
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['language'] = 'fr-FR'
+ params = wikipedia.request(query.encode('utf-8'), dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('test_query', params['url'])
+ self.assertIn('Test_Query', params['url'])
+ self.assertIn('fr.wikipedia.org', params['url'])
+
+ query = u'Test_Query'
+ params = wikipedia.request(query.encode('utf-8'), dicto)
+ self.assertIn('Test_Query', params['url'])
+ self.assertNotIn('test_query', params['url'])
+
+ dicto['language'] = 'all'
+ params = wikipedia.request(query, dicto)
+ self.assertIn('en', params['url'])
+
+ dicto['language'] = 'xx'
+ params = wikipedia.request(query, dicto)
+ self.assertIn('en', params['url'])
+
+ def test_response(self):
+ dicto = defaultdict(dict)
+ dicto['language'] = 'fr'
+
+ self.assertRaises(AttributeError, wikipedia.response, None)
+ self.assertRaises(AttributeError, wikipedia.response, [])
+ self.assertRaises(AttributeError, wikipedia.response, '')
+ self.assertRaises(AttributeError, wikipedia.response, '[]')
+
+ # page not found
+ json = """
+ {
+ "batchcomplete": "",
+ "query": {
+ "normalized": [],
+ "pages": {
+ "-1": {
+ "ns": 0,
+ "title": "",
+ "missing": ""
+ }
+ }
+ }
+ }"""
+ response = mock.Mock(text=json, search_params=dicto)
+ self.assertEqual(wikipedia.response(response), [])
+
+ # normal case
+ json = """
+ {
+ "batchcomplete": "",
+ "query": {
+ "normalized": [],
+ "pages": {
+ "12345": {
+ "pageid": 12345,
+ "ns": 0,
+ "title": "The Title",
+ "extract": "The Title is...",
+ "thumbnail": {
+ "source": "img_src.jpg"
+ },
+ "pageimage": "img_name.jpg"
+ }
+ }
+ }
+ }"""
+ response = mock.Mock(text=json, search_params=dicto)
+ results = wikipedia.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], u'The Title')
+ self.assertIn('fr.wikipedia.org/wiki/The_Title', results[0]['url'])
+ self.assertEqual(results[1]['infobox'], u'The Title')
+ self.assertIn('fr.wikipedia.org/wiki/The_Title', results[1]['id'])
+ self.assertIn('The Title is...', results[1]['content'])
+ self.assertEqual(results[1]['img_src'], 'img_src.jpg')
+
+ # disambiguation page
+ json = """
+ {
+ "batchcomplete": "",
+ "query": {
+ "normalized": [],
+ "pages": {
+ "12345": {
+ "pageid": 12345,
+ "ns": 0,
+ "title": "The Title",
+ "extract": "The Title can be:\\nThe Title 1\\nThe Title 2\\nThe Title 3\\nThe Title 4......................................................................................................................................." """ # noqa
+ json += """
+ }
+ }
+ }
+ }"""
+ response = mock.Mock(text=json, search_params=dicto)
+ results = wikipedia.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+
+ # no image
+ json = """
+ {
+ "batchcomplete": "",
+ "query": {
+ "normalized": [],
+ "pages": {
+ "12345": {
+ "pageid": 12345,
+ "ns": 0,
+ "title": "The Title",
+ "extract": "The Title is......................................................................................................................................................................................." """ # noqa
+ json += """
+ }
+ }
+ }
+ }"""
+ response = mock.Mock(text=json, search_params=dicto)
+ results = wikipedia.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertIn('The Title is...', results[1]['content'])
+ self.assertEqual(results[1]['img_src'], None)
+
+ # title not in first paragraph
+ json = u"""
+ {
+ "batchcomplete": "",
+ "query": {
+ "normalized": [],
+ "pages": {
+ "12345": {
+ "pageid": 12345,
+ "ns": 0,
+ "title": "披頭四樂隊",
+ "extract": "披头士乐队....................................................................................................................................................................................................\\n披頭四樂隊...", """ # noqa
+ json += """
+ "thumbnail": {
+ "source": "img_src.jpg"
+ },
+ "pageimage": "img_name.jpg"
+ }
+ }
+ }
+ }"""
+ response = mock.Mock(text=json, search_params=dicto)
+ results = wikipedia.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[1]['infobox'], u'披頭四樂隊')
+ self.assertIn(u'披头士乐队...', results[1]['content'])
+
+ def test_fetch_supported_languages(self):
+ html = u"""<html></html>"""
+ response = mock.Mock(text=html)
+ languages = wikipedia._fetch_supported_languages(response)
+ self.assertEqual(type(languages), dict)
+ self.assertEqual(len(languages), 0)
+
+ html = u"""
+ <html>
+ <body>
+ <div>
+ <div>
+ <h3>Table header</h3>
+ <table class="sortable jquery-tablesorter">
+ <thead>
+ <tr>
+ <th>N</th>
+ <th>Language</th>
+ <th>Language (local)</th>
+ <th>Wiki</th>
+ <th>Articles</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>2</td>
+ <td><a>Swedish</a></td>
+ <td><a>Svenska</a></td>
+ <td><a>sv</a></td>
+ <td><a><b>3000000</b></a></td>
+ </tr>
+ <tr>
+ <td>3</td>
+ <td><a>Cebuano</a></td>
+ <td><a>Sinugboanong Binisaya</a></td>
+ <td><a>ceb</a></td>
+ <td><a><b>3000000</b></a></td>
+ </tr>
+ </tbody>
+ </table>
+ <h3>Table header</h3>
+ <table class="sortable jquery-tablesorter">
+ <thead>
+ <tr>
+ <th>N</th>
+ <th>Language</th>
+ <th>Language (local)</th>
+ <th>Wiki</th>
+ <th>Articles</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>2</td>
+ <td><a>Norwegian (Bokmål)</a></td>
+ <td><a>Norsk (Bokmål)</a></td>
+ <td><a>no</a></td>
+ <td><a><b>100000</b></a></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </body>
+ </html>
+ """
+ response = mock.Mock(text=html)
+ languages = wikipedia._fetch_supported_languages(response)
+ self.assertEqual(type(languages), dict)
+ self.assertEqual(len(languages), 3)
+
+ self.assertIn('sv', languages)
+ self.assertIn('ceb', languages)
+ self.assertIn('no', languages)
+
+ self.assertEqual(type(languages['sv']), dict)
+ self.assertEqual(type(languages['ceb']), dict)
+ self.assertEqual(type(languages['no']), dict)
+
+ self.assertIn('name', languages['sv'])
+ self.assertIn('english_name', languages['sv'])
+ self.assertIn('articles', languages['sv'])
+
+ self.assertEqual(languages['sv']['name'], 'Svenska')
+ self.assertEqual(languages['sv']['english_name'], 'Swedish')
+ self.assertEqual(languages['sv']['articles'], 3000000)
+ self.assertEqual(languages['ceb']['name'], 'Sinugboanong Binisaya')
+ self.assertEqual(languages['ceb']['english_name'], 'Cebuano')
+ self.assertEqual(languages['ceb']['articles'], 3000000)
+ self.assertEqual(languages['no']['name'], u'Norsk (Bokmål)')
+ self.assertEqual(languages['no']['english_name'], u'Norwegian (Bokmål)')
+ self.assertEqual(languages['no']['articles'], 100000)
diff --git a/tests/unit/engines/test_wolframalpha_api.py b/tests/unit/engines/test_wolframalpha_api.py
new file mode 100644
index 0000000..30d3376
--- /dev/null
+++ b/tests/unit/engines/test_wolframalpha_api.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from requests import Request
+from searx.engines import wolframalpha_api
+from searx.testing import SearxTestCase
+
+
+class TestWolframAlphaAPIEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ params = wolframalpha_api.request(query, dicto)
+
+ # TODO: test api_key
+ self.assertIn('url', params)
+ self.assertIn('https://api.wolframalpha.com/v2/query?', params['url'])
+ self.assertIn(query, params['url'])
+ self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer'])
+
+ def test_replace_pua_chars(self):
+ self.assertEqual('i', wolframalpha_api.replace_pua_chars(u'\uf74e'))
+
+ def test_response(self):
+ self.assertRaises(AttributeError, wolframalpha_api.response, None)
+ self.assertRaises(AttributeError, wolframalpha_api.response, [])
+ self.assertRaises(AttributeError, wolframalpha_api.response, '')
+ self.assertRaises(AttributeError, wolframalpha_api.response, '[]')
+
+ referer_url = 'referer_url'
+ request = Request(headers={'Referer': referer_url})
+
+ # test failure
+ xml = '''<?xml version='1.0' encoding='UTF-8'?>
+ <queryresult success='false' error='false' />
+ '''
+ response = mock.Mock(text=xml.encode('utf-8'))
+ self.assertEqual(wolframalpha_api.response(response), [])
+
+ # test basic case
+ xml = b"""<?xml version='1.0' encoding='UTF-8'?>
+ <queryresult success='true'
+ error='false'
+ numpods='3'
+ datatypes='Math'
+ id='queryresult_id'
+ host='http://www4c.wolframalpha.com'
+ related='related_url'
+ version='2.6'>
+ <pod title='Input'
+ scanner='Identity'
+ id='Input'
+ numsubpods='1'>
+ <subpod title=''>
+ <img src='input_img_src.gif'
+ alt='input_img_alt'
+ title='input_img_title' />
+ <plaintext>input_plaintext</plaintext>
+ </subpod>
+ </pod>
+ <pod title='Result'
+ scanner='Simplification'
+ id='Result'
+ numsubpods='1'
+ primary='true'>
+ <subpod title=''>
+ <img src='result_img_src.gif'
+ alt='result_img_alt'
+ title='result_img_title' />
+ <plaintext>result_plaintext</plaintext>
+ </subpod>
+ </pod>
+ <pod title='Manipulatives illustration'
+ scanner='Arithmetic'
+ id='Illustration'
+ numsubpods='1'>
+ <subpod title=''>
+ <img src='illustration_img_src.gif'
+ alt='illustration_img_alt' />
+ <plaintext>illustration_plaintext</plaintext>
+ </subpod>
+ </pod>
+ </queryresult>
+ """
+ response = mock.Mock(text=xml, request=request)
+ results = wolframalpha_api.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual('input_plaintext', results[0]['infobox'])
+
+ self.assertEqual(len(results[0]['attributes']), 3)
+ self.assertEqual('Input', results[0]['attributes'][0]['label'])
+ self.assertEqual('input_plaintext', results[0]['attributes'][0]['value'])
+ self.assertEqual('Result', results[0]['attributes'][1]['label'])
+ self.assertEqual('result_plaintext', results[0]['attributes'][1]['value'])
+ self.assertEqual('Manipulatives illustration', results[0]['attributes'][2]['label'])
+ self.assertEqual('illustration_img_src.gif', results[0]['attributes'][2]['image']['src'])
+ self.assertEqual('illustration_img_alt', results[0]['attributes'][2]['image']['alt'])
+
+ self.assertEqual(len(results[0]['urls']), 1)
+
+ self.assertEqual(referer_url, results[0]['urls'][0]['url'])
+ self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
+ self.assertEqual(referer_url, results[1]['url'])
+ self.assertEqual('Wolfram|Alpha (input_plaintext)', results[1]['title'])
+ self.assertIn('result_plaintext', results[1]['content'])
+
+ # test calc
+ xml = b"""<?xml version='1.0' encoding='UTF-8'?>
+ <queryresult success='true'
+ error='false'
+ numpods='2'
+ datatypes=''
+ parsetimedout='false'
+ id='queryresult_id'
+ host='http://www5b.wolframalpha.com'
+ related='related_url'
+ version='2.6' >
+ <pod title='Indefinite integral'
+ scanner='Integral'
+ id='IndefiniteIntegral'
+ error='false'
+ numsubpods='1'
+ primary='true'>
+ <subpod title=''>
+ <img src='integral_image.gif'
+ alt='integral_img_alt'
+ title='integral_img_title' />
+ <plaintext>integral_plaintext</plaintext>
+ </subpod>
+ </pod>
+ <pod title='Plot of the integral'
+ scanner='Integral'
+ id='Plot'
+ error='false'
+ numsubpods='1'>
+ <subpod title=''>
+ <img src='plot.gif'
+ alt='plot_alt'
+ title='' />
+ <plaintext></plaintext>
+ </subpod>
+ </pod>
+ </queryresult>
+ """
+ response = mock.Mock(text=xml, request=request)
+ results = wolframalpha_api.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual('integral_plaintext', results[0]['infobox'])
+
+ self.assertEqual(len(results[0]['attributes']), 2)
+ self.assertEqual('Indefinite integral', results[0]['attributes'][0]['label'])
+ self.assertEqual('integral_plaintext', results[0]['attributes'][0]['value'])
+ self.assertEqual('Plot of the integral', results[0]['attributes'][1]['label'])
+ self.assertEqual('plot.gif', results[0]['attributes'][1]['image']['src'])
+ self.assertEqual('plot_alt', results[0]['attributes'][1]['image']['alt'])
+
+ self.assertEqual(len(results[0]['urls']), 1)
+
+ self.assertEqual(referer_url, results[0]['urls'][0]['url'])
+ self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
+ self.assertEqual(referer_url, results[1]['url'])
+ self.assertEqual('Wolfram|Alpha (integral_plaintext)', results[1]['title'])
+ self.assertIn('integral_plaintext', results[1]['content'])
diff --git a/tests/unit/engines/test_wolframalpha_noapi.py b/tests/unit/engines/test_wolframalpha_noapi.py
new file mode 100644
index 0000000..982edd9
--- /dev/null
+++ b/tests/unit/engines/test_wolframalpha_noapi.py
@@ -0,0 +1,224 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from requests import Request
+from searx.engines import wolframalpha_noapi
+from searx.testing import SearxTestCase
+
+
+class TestWolframAlphaNoAPIEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ params = wolframalpha_noapi.request(query, dicto)
+
+ self.assertIn('url', params)
+ self.assertIn('https://www.wolframalpha.com/input/json.jsp', params['url'])
+ self.assertIn(query, params['url'])
+ self.assertEqual('https://www.wolframalpha.com/input/?i=test_query', params['headers']['Referer'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, wolframalpha_noapi.response, None)
+ self.assertRaises(AttributeError, wolframalpha_noapi.response, [])
+ self.assertRaises(AttributeError, wolframalpha_noapi.response, '')
+ self.assertRaises(AttributeError, wolframalpha_noapi.response, '[]')
+
+ referer_url = 'referer_url'
+ request = Request(headers={'Referer': referer_url})
+
+ # test failure
+ json = r'''
+ {"queryresult" : {
+ "success" : false,
+ "error" : false,
+ "numpods" : 0,
+ "id" : "",
+ "host" : "https:\/\/www5a.wolframalpha.com",
+ "didyoumeans" : {}
+ }}
+ '''
+ response = mock.Mock(text=json, request=request)
+ self.assertEqual(wolframalpha_noapi.response(response), [])
+
+ # test basic case
+ json = r'''
+ {"queryresult" : {
+ "success" : true,
+ "error" : false,
+ "numpods" : 6,
+ "datatypes" : "Math",
+ "id" : "queryresult_id",
+ "host" : "https:\/\/www5b.wolframalpha.com",
+ "related" : "related_url",
+ "version" : "2.6",
+ "pods" : [
+ {
+ "title" : "Input",
+ "scanners" : [
+ "Identity"
+ ],
+ "id" : "Input",
+ "error" : false,
+ "numsubpods" : 1,
+ "subpods" : [
+ {
+ "title" : "",
+ "img" : {
+ "src" : "input_img_src.gif",
+ "alt" : "input_img_alt",
+ "title" : "input_img_title"
+ },
+ "plaintext" : "input_plaintext",
+ "minput" : "input_minput"
+ }
+ ]
+ },
+ {
+ "title" : "Result",
+ "scanners" : [
+ "Simplification"
+ ],
+ "id" : "Result",
+ "error" : false,
+ "numsubpods" : 1,
+ "primary" : true,
+ "subpods" : [
+ {
+ "title" : "",
+ "img" : {
+ "src" : "result_img_src.gif",
+ "alt" : "result_img_alt",
+ "title" : "result_img_title"
+ },
+ "plaintext" : "result_plaintext",
+ "moutput" : "result_moutput"
+ }
+ ]
+ },
+ {
+ "title" : "Manipulatives illustration",
+ "scanners" : [
+ "Arithmetic"
+ ],
+ "id" : "Illustration",
+ "error" : false,
+ "numsubpods" : 1,
+ "subpods" : [
+ {
+ "title" : "",
+ "CDFcontent" : "Resizeable",
+ "img" : {
+ "src" : "illustration_img_src.gif",
+ "alt" : "illustration_img_alt",
+ "title" : "illustration_img_title"
+ },
+ "plaintext" : "illustration_img_plaintext"
+ }
+ ]
+ }
+ ]
+ }}
+ '''
+ response = mock.Mock(text=json, request=request)
+ results = wolframalpha_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual('input_plaintext', results[0]['infobox'])
+
+ self.assertEqual(len(results[0]['attributes']), 3)
+ self.assertEqual('Input', results[0]['attributes'][0]['label'])
+ self.assertEqual('input_plaintext', results[0]['attributes'][0]['value'])
+ self.assertEqual('Result', results[0]['attributes'][1]['label'])
+ self.assertEqual('result_plaintext', results[0]['attributes'][1]['value'])
+ self.assertEqual('Manipulatives illustration', results[0]['attributes'][2]['label'])
+ self.assertEqual('illustration_img_src.gif', results[0]['attributes'][2]['image']['src'])
+ self.assertEqual('illustration_img_alt', results[0]['attributes'][2]['image']['alt'])
+
+ self.assertEqual(len(results[0]['urls']), 1)
+
+ self.assertEqual(referer_url, results[0]['urls'][0]['url'])
+ self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
+ self.assertEqual(referer_url, results[1]['url'])
+ self.assertEqual('Wolfram|Alpha (input_plaintext)', results[1]['title'])
+ self.assertIn('result_plaintext', results[1]['content'])
+
+ # test calc
+ json = r"""
+ {"queryresult" : {
+ "success" : true,
+ "error" : false,
+ "numpods" : 2,
+ "datatypes" : "",
+ "id" : "queryresult_id",
+ "host" : "https:\/\/www4b.wolframalpha.com",
+ "related" : "related_url",
+ "version" : "2.6",
+ "pods" : [
+ {
+ "title" : "Indefinite integral",
+ "scanners" : [
+ "Integral"
+ ],
+ "id" : "IndefiniteIntegral",
+ "error" : false,
+ "numsubpods" : 1,
+ "primary" : true,
+ "subpods" : [
+ {
+ "title" : "",
+ "img" : {
+ "src" : "integral_img_src.gif",
+ "alt" : "integral_img_alt",
+ "title" : "integral_img_title"
+ },
+ "plaintext" : "integral_plaintext",
+ "minput" : "integral_minput",
+ "moutput" : "integral_moutput"
+ }
+ ]
+ },
+ {
+ "title" : "Plot of the integral",
+ "scanners" : [
+ "Integral"
+ ],
+ "id" : "Plot",
+ "error" : false,
+ "numsubpods" : 1,
+ "subpods" : [
+ {
+ "title" : "",
+ "img" : {
+ "src" : "plot.gif",
+ "alt" : "plot_alt",
+ "title" : "plot_title"
+ },
+ "plaintext" : "",
+ "minput" : "plot_minput"
+ }
+ ]
+ }
+ ]
+ }}
+ """
+ response = mock.Mock(text=json, request=request)
+ results = wolframalpha_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual('integral_plaintext', results[0]['infobox'])
+
+ self.assertEqual(len(results[0]['attributes']), 2)
+ self.assertEqual('Indefinite integral', results[0]['attributes'][0]['label'])
+ self.assertEqual('integral_plaintext', results[0]['attributes'][0]['value'])
+ self.assertEqual('Plot of the integral', results[0]['attributes'][1]['label'])
+ self.assertEqual('plot.gif', results[0]['attributes'][1]['image']['src'])
+ self.assertEqual('plot_alt', results[0]['attributes'][1]['image']['alt'])
+
+ self.assertEqual(len(results[0]['urls']), 1)
+
+ self.assertEqual(referer_url, results[0]['urls'][0]['url'])
+ self.assertEqual('Wolfram|Alpha', results[0]['urls'][0]['title'])
+ self.assertEqual(referer_url, results[1]['url'])
+ self.assertEqual('Wolfram|Alpha (integral_plaintext)', results[1]['title'])
+ self.assertIn('integral_plaintext', results[1]['content'])
diff --git a/tests/unit/engines/test_www1x.py b/tests/unit/engines/test_www1x.py
new file mode 100644
index 0000000..9df8de6
--- /dev/null
+++ b/tests/unit/engines/test_www1x.py
@@ -0,0 +1,57 @@
+from collections import defaultdict
+import mock
+from searx.engines import www1x
+from searx.testing import SearxTestCase
+
+
+class TestWww1xEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ params = www1x.request(query, defaultdict(dict))
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('1x.com' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, www1x.response, None)
+ self.assertRaises(AttributeError, www1x.response, [])
+ self.assertRaises(AttributeError, www1x.response, '')
+ self.assertRaises(AttributeError, www1x.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(www1x.response(response), [])
+ html = """
+ <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE characters
+ [
+ <!ELEMENT characters (character*) >
+ <!ELEMENT character (#PCDATA ) >
+
+ <!ENTITY iexcl "&#161;" >
+ <!ENTITY cent "&#162;" >
+ <!ENTITY pound "&#163;" >
+ ]
+ ><root><searchresult><![CDATA[<table border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tr>
+ <td style="min-width: 220px;" valign="top">
+ <div style="font-size: 30px; margin: 0px 0px 20px 0px;">Photos</div>
+ <div>
+ <a href="/photo/123456" class="dynamiclink">
+<img border="0" class="searchresult" src="/images/user/testimage-123456.jpg" style="width: 125px; height: 120px;">
+ </a>
+ <a title="sjoerd lammers street photography" href="/member/sjoerdlammers" class="dynamiclink">
+<img border="0" class="searchresult" src="/images/profile/60c48b394c677d2fa4d9e7d263aabf44-square.jpg">
+ </a>
+ </div>
+ </td>
+ </table>
+ ]]></searchresult></root>
+ """
+ response = mock.Mock(text=html)
+ results = www1x.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['url'], 'https://1x.com/photo/123456')
+ self.assertEqual(results[0]['thumbnail_src'], 'https://1x.com/images/user/testimage-123456.jpg')
+ self.assertEqual(results[0]['content'], '')
+ self.assertEqual(results[0]['template'], 'images.html')
diff --git a/tests/unit/engines/test_www500px.py b/tests/unit/engines/test_www500px.py
new file mode 100644
index 0000000..e50601d
--- /dev/null
+++ b/tests/unit/engines/test_www500px.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import www500px
+from searx.testing import SearxTestCase
+
+
+class TestWww500pxImagesEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = www500px.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertTrue('500px.com' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, www500px.response, None)
+ self.assertRaises(AttributeError, www500px.response, [])
+ self.assertRaises(AttributeError, www500px.response, '')
+ self.assertRaises(AttributeError, www500px.response, '[]')
+
+ json = """
+{"current_page":1,"total_pages":1000,"total_items":862178,"photos":[{"id":64531569,"user_id":111147,"name":"Grand Canyon Afternoon","description":"Looking west on a very windy winter afternoon.","camera":"Canon EOS 5D Mark II","lens":"EF24-105mm f/4L IS USM","focal_length":"28","iso":"200","shutter_speed":"1/1250","aperture":"6.3","times_viewed":4809,"rating":48.5,"status":1,"created_at":"2014-03-22T03:44:46-04:00","category":8,"location":null,"latitude":36.0323916666667,"longitude":-111.85273,"taken_at":"2014-02-27T14:10:43-05:00","hi_res_uploaded":2,"for_sale":true,"width":5476,"height":3651,"votes_count":108,"favorites_count":35,"comments_count":5,"nsfw":false,"sales_count":0,"for_sale_date":null,"highest_rating":91.9,"highest_rating_date":"2014-03-22T22:34:54-04:00","license_type":0,"converted":31,"collections_count":10,"crop_version":0,"privacy":false,"profile":true,"image_url":["https://drscdn.500px.org/photo/64531569/w%3D70_h%3D70/449d50817f28d85395e23bbb415b3cdb?v=0","https://drscdn.500px.org/photo/64531569/q%3D50_w%3D140_h%3D140/3e3e123734a596644ede78105268bdb2?v=0","https://drscdn.500px.org/photo/64531569/q%3D80_h%3D300/2ce2f61714aebdca710967dfdc3efb04","https://drscdn.500px.org/photo/64531569/q%3D80_h%3D450/c8ec030441f2c68b9bd40a114903348a","https://drscdn.500px.org/photo/64531569/q%3D80_h%3D600/ab6562d0581b359679ecc8ef2e939396","https://drscdn.500px.org/photo/64531569/q%3D80_m%3D1000/bd7dbc54a505e041a8c9a70dfa434272","https://drscdn.500px.org/photo/64531569/q%3D80_m%3D1500/eb4d7f8f6a32d3e5c168c2cb55d29c12","https://drscdn.500px.org/photo/64531569/q%3D80_m%3D2000/d519f91b8a568e7357a8a7fa1aabbe74","https://drscdn.500px.org/photo/64531569/m%3D2048/4c52fb18cc2b2b6f91a0d04609786507","https://drscdn.500px.org/photo/64531569/m%3D900/fb620ae39569ab4a421e9170a94b1a0f","https://drscdn.500px.org/photo/64531569/m%3D900_s%3D1_k%3D1_a%3D1/02b95ce64db090c1f94f890960974612?v=0"],"images":[{"size":1,"url":"https://drscdn.500px.org/photo/64531569/w%3D70_h%3D70/449d50817f28d85395e23bbb415b3cdb?v=0","https_url":"https://drscdn.500px.org/photo/64531569/w%3D70_h%3D70/449d50817f28d85395e23bbb415b3cdb?v=0","format":"jpeg"},{"size":2,"url":"https://drscdn.500px.org/photo/64531569/q%3D50_w%3D140_h%3D140/3e3e123734a596644ede78105268bdb2?v=0","https_url":"https://drscdn.500px.org/photo/64531569/q%3D50_w%3D140_h%3D140/3e3e123734a596644ede78105268bdb2?v=0","format":"jpeg"},{"size":4,"url":"https://drscdn.500px.org/photo/64531569/m%3D900/fb620ae39569ab4a421e9170a94b1a0f","https_url":"https://drscdn.500px.org/photo/64531569/m%3D900/fb620ae39569ab4a421e9170a94b1a0f","format":"jpeg"},{"size":14,"url":"https://drscdn.500px.org/photo/64531569/m%3D900_s%3D1_k%3D1_a%3D1/02b95ce64db090c1f94f890960974612?v=0","https_url":"https://drscdn.500px.org/photo/64531569/m%3D900_s%3D1_k%3D1_a%3D1/02b95ce64db090c1f94f890960974612?v=0","format":"jpeg"},{"size":31,"url":"https://drscdn.500px.org/photo/64531569/q%3D80_h%3D450/c8ec030441f2c68b9bd40a114903348a","https_url":"https://drscdn.500px.org/photo/64531569/q%3D80_h%3D450/c8ec030441f2c68b9bd40a114903348a","format":"jpeg"},{"size":32,"url":"https://drscdn.500px.org/photo/64531569/q%3D80_h%3D300/2ce2f61714aebdca710967dfdc3efb04","https_url":"https://drscdn.500px.org/photo/64531569/q%3D80_h%3D300/2ce2f61714aebdca710967dfdc3efb04","format":"jpeg"},{"size":33,"url":"https://drscdn.500px.org/photo/64531569/q%3D80_h%3D600/ab6562d0581b359679ecc8ef2e939396","https_url":"https://drscdn.500px.org/photo/64531569/q%3D80_h%3D600/ab6562d0581b359679ecc8ef2e939396","format":"jpeg"},{"size":34,"url":"https://drscdn.500px.org/photo/64531569/q%3D80_m%3D1000/bd7dbc54a505e041a8c9a70dfa434272","https_url":"https://drscdn.500px.org/photo/64531569/q%3D80_m%3D1000/bd7dbc54a505e041a8c9a70dfa434272","format":"jpeg"},{"size":35,"url":"https://drscdn.500px.org/photo/64531569/q%3D80_m%3D1500/eb4d7f8f6a32d3e5c168c2cb55d29c12","https_url":"https://drscdn.500px.org/photo/64531569/q%3D80_m%3D1500/eb4d7f8f6a32d3e5c168c2cb55d29c12","format":"jpeg"},{"size":36,"url":"https://drscdn.500px.org/photo/64531569/q%3D80_m%3D2000/d519f91b8a568e7357a8a7fa1aabbe74","https_url":"https://drscdn.500px.org/photo/64531569/q%3D80_m%3D2000/d519f91b8a568e7357a8a7fa1aabbe74","format":"jpeg"},{"size":2048,"url":"https://drscdn.500px.org/photo/64531569/m%3D2048/4c52fb18cc2b2b6f91a0d04609786507","https_url":"https://drscdn.500px.org/photo/64531569/m%3D2048/4c52fb18cc2b2b6f91a0d04609786507","format":"jpeg"}],"url":"/photo/64531569/grand-canyon-afternoon-by-todd-hakala","positive_votes_count":108,"converted_bits":31,"tags":["landscape","river","arizona","canyon","grand","colorado","south","southwest","az","west","rim","CanonGetaway"],"watermark":false,"image_format":"jpeg","licensing_requested":false,"licensing_suggested":false,"is_free_photo":false,"user":{"id":111147,"username":"ToddHakala","firstname":"Todd","lastname":"Hakala","city":"Albuquerque","country":"US","usertype":0,"fullname":"Todd Hakala","userpic_url":"https://pacdn.500px.org/111147/ea167926a64ce9b32e44cbec61e3af4f75b762cb/1.jpg?2","userpic_https_url":"https://pacdn.500px.org/111147/ea167926a64ce9b32e44cbec61e3af4f75b762cb/1.jpg?2","cover_url":"https://pacdn.500px.org/111147/ea167926a64ce9b32e44cbec61e3af4f75b762cb/cover_2048.jpg?8","upgrade_status":1,"store_on":true,"affection":5217,"avatars":{"default":{"https":"https://pacdn.500px.org/111147/ea167926a64ce9b32e44cbec61e3af4f75b762cb/1.jpg?2"},"large":{"https":"https://pacdn.500px.org/111147/ea167926a64ce9b32e44cbec61e3af4f75b762cb/2.jpg?2"},"small":{"https":"https://pacdn.500px.org/111147/ea167926a64ce9b32e44cbec61e3af4f75b762cb/3.jpg?2"},"tiny":{"https":"https://pacdn.500px.org/111147/ea167926a64ce9b32e44cbec61e3af4f75b762cb/4.jpg?2"}},"followers_count":171}}]}
+ """ # noqa
+ response = mock.Mock(text=json)
+ results = www500px.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], u'Grand Canyon Afternoon')
+ self.assertEqual(results[0]['url'], 'https://500px.com/photo/64531569/grand-canyon-afternoon-by-todd-hakala')
+ self.assertEqual(results[0]['content'], u'Looking west on a very windy winter afternoon.')
diff --git a/tests/unit/engines/test_yacy.py b/tests/unit/engines/test_yacy.py
new file mode 100644
index 0000000..f49532c
--- /dev/null
+++ b/tests/unit/engines/test_yacy.py
@@ -0,0 +1,96 @@
+from collections import defaultdict
+import mock
+from searx.engines import yacy
+from searx.testing import SearxTestCase
+
+
+class TestYacyEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr_FR'
+ params = yacy.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('localhost', params['url'])
+ self.assertIn('fr', params['url'])
+
+ dicto['language'] = 'all'
+ params = yacy.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertNotIn('lr=lang_', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, yacy.response, None)
+ self.assertRaises(AttributeError, yacy.response, [])
+ self.assertRaises(AttributeError, yacy.response, '')
+ self.assertRaises(AttributeError, yacy.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(yacy.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(yacy.response(response), [])
+
+ json = """
+ {
+ "channels": [
+ {
+ "title": "YaCy P2P-Search for test",
+ "description": "Search for test",
+ "link": "http://search.yacy.de:7001/yacysearch.html?query=test&amp;resource=global&amp;contentdom=0",
+ "image": {
+ "url": "http://search.yacy.de:7001/env/grafics/yacy.png",
+ "title": "Search for test",
+ "link": "http://search.yacy.de:7001/yacysearch.html?query=test&amp;resource=global&amp;contentdom=0"
+ },
+ "totalResults": "249",
+ "startIndex": "0",
+ "itemsPerPage": "5",
+ "searchTerms": "test",
+ "items": [
+ {
+ "title": "This is the title",
+ "link": "http://this.is.the.url",
+ "code": "",
+ "description": "This should be the content",
+ "pubDate": "Sat, 08 Jun 2013 02:00:00 +0200",
+ "size": "44213",
+ "sizename": "43 kbyte",
+ "guid": "lzh_1T_5FP-A",
+ "faviconCode": "XTS4uQ_5FP-A",
+ "host": "www.gamestar.de",
+ "path": "/spiele/city-of-heroes-freedom/47019.html",
+ "file": "47019.html",
+ "urlhash": "lzh_1T_5FP-A",
+ "ranking": "0.20106804"
+ },
+ {
+ "title": "This is the title2",
+ "icon": "/ViewImage.png?maxwidth=96&amp;maxheight=96&amp;code=7EbAbW6BpPOA",
+ "image": "http://image.url/image.png",
+ "cache": "/ViewImage.png?quadratic=&amp;url=http://golem.ivwbox.de/cgi-bin/ivw/CP/G_INET?d=14071378",
+ "url": "http://this.is.the.url",
+ "urlhash": "7EbAbW6BpPOA",
+ "host": "www.golem.de",
+ "width": "-1",
+ "height": "-1"
+ }
+ ]
+ }
+ ]
+ }
+ """
+ response = mock.Mock(text=json)
+ results = yacy.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url')
+ self.assertEqual(results[0]['content'], 'This should be the content')
+ self.assertEqual(results[1]['img_src'], 'http://image.url/image.png')
+ self.assertEqual(results[1]['content'], '')
+ self.assertEqual(results[1]['url'], 'http://this.is.the.url')
+ self.assertEqual(results[1]['title'], 'This is the title2')
diff --git a/tests/unit/engines/test_yahoo.py b/tests/unit/engines/test_yahoo.py
new file mode 100644
index 0000000..82c4d99
--- /dev/null
+++ b/tests/unit/engines/test_yahoo.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import yahoo
+from searx.testing import SearxTestCase
+
+
+class TestYahooEngine(SearxTestCase):
+
+ def test_parse_url(self):
+ test_url = 'http://r.search.yahoo.com/_ylt=A0LEb9JUSKcAEGRXNyoA;_ylu=X3oDMTEzZm1qazYwBHNlYwNzcgRwb3MDMQRjb' +\
+ '2xvA2Jm2dGlkA1NNRTcwM18x/RV=2/RE=1423106085/RO=10/RU=https%3a%2f%2fthis.is.the.url%2f/RK=0/RS=' +\
+ 'dtcJsfP4mEeBOjnVfUQ-'
+ url = yahoo.parse_url(test_url)
+ self.assertEqual('https://this.is.the.url/', url)
+
+ test_url = 'http://r.search.yahoo.com/_ylt=A0LElb9JUSKcAEGRXNyoA;_ylu=X3oDMTEzZm1qazYwBHNlYwNzcgRwb3MDMQRjb' +\
+ '2xvA2Jm2dGlkA1NNRTcwM18x/RV=2/RE=1423106085/RO=10/RU=https%3a%2f%2fthis.is.the.url%2f/RS=' +\
+ 'dtcJsfP4mEeBOjnVfUQ-'
+ url = yahoo.parse_url(test_url)
+ self.assertEqual('https://this.is.the.url/', url)
+
+ test_url = 'https://this.is.the.url/'
+ url = yahoo.parse_url(test_url)
+ self.assertEqual('https://this.is.the.url/', url)
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['time_range'] = ''
+ dicto['language'] = 'fr_FR'
+ params = yahoo.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('search.yahoo.com', params['url'])
+ self.assertIn('fr', params['url'])
+ self.assertIn('cookies', params)
+ self.assertIn('sB', params['cookies'])
+ self.assertIn('fr', params['cookies']['sB'])
+
+ dicto['language'] = 'all'
+ params = yahoo.request(query, dicto)
+ self.assertIn('cookies', params)
+ self.assertIn('sB', params['cookies'])
+ self.assertIn('en', params['cookies']['sB'])
+ self.assertIn('en', params['url'])
+
+ def test_no_url_in_request_year_time_range(self):
+ dicto = defaultdict(dict)
+ query = 'test_query'
+ dicto['time_range'] = 'year'
+ params = yahoo.request(query, dicto)
+ self.assertEqual({}, params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, yahoo.response, None)
+ self.assertRaises(AttributeError, yahoo.response, [])
+ self.assertRaises(AttributeError, yahoo.response, '')
+ self.assertRaises(AttributeError, yahoo.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(yahoo.response(response), [])
+
+ html = """
+<ol class="reg mb-15 searchCenterMiddle">
+ <li class="first">
+ <div class="dd algo fst Sr">
+ <div class="compTitle">
+ <h3 class="title"><a class=" td-u" href="http://r.search.yahoo.com/_ylt=A0LEb9JUSKcAEGRXNyoA;
+ _ylu=X3oDMTEzZm1qazYwBHNlYwNzcgRwb3MDMQRjb2xvA2Jm2dGlkA1NNRTcwM18x/RV=2/RE=1423106085/RO=10
+ /RU=https%3a%2f%2fthis.is.the.url%2f/RK=0/RS=dtcJsfP4mEeBOjnVfUQ-"
+ target="_blank" data-bid="54e712e13671c">
+ <b><b>This is the title</b></b></a>
+ </h3>
+ </div>
+ <div class="compText aAbs">
+ <p class="lh-18"><b><b>This is the </b>content</b>
+ </p>
+ </div>
+ </div>
+ </li>
+ <li>
+ <div class="dd algo lst Sr">
+ <div class="compTitle">
+ </div>
+ <div class="compText aAbs">
+ <p class="lh-18">This is the second content</p>
+ </div>
+ </div>
+ </li>
+</ol>
+<div class="dd assist fst lst AlsoTry" data-bid="54e712e138d04">
+ <div class="compTitle mb-4 h-17">
+ <h3 class="title">Also Try</h3> </div>
+ <table class="compTable m-0 ac-1st td-u fz-ms">
+ <tbody>
+ <tr>
+ <td class="w-50p pr-28"><a href="https://search.yahoo.com/"><B>This is the </B>suggestion<B></B></a>
+ </td>
+ </tr>
+ </table>
+</div>
+ """
+ response = mock.Mock(text=html)
+ results = yahoo.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'https://this.is.the.url/')
+ self.assertEqual(results[0]['content'], 'This is the content')
+ self.assertEqual(results[1]['suggestion'], 'This is the suggestion')
+
+ html = """
+<ol class="reg mb-15 searchCenterMiddle">
+ <li class="first">
+ <div class="dd algo fst Sr">
+ <div class="compTitle">
+ <h3 class="title"><a class=" td-u" href="http://r.search.yahoo.com/_ylt=A0LEb9JUSKcAEGRXNyoA;
+ _ylu=X3oDMTEzZm1qazYwBHNlYwNzcgRwb3MDMQRjb2xvA2Jm2dGlkA1NNRTcwM18x/RV=2/RE=1423106085/RO=10
+ /RU=https%3a%2f%2fthis.is.the.url%2f/RK=0/RS=dtcJsfP4mEeBOjnVfUQ-"
+ target="_blank" data-bid="54e712e13671c">
+ <b><b>This is the title</b></b></a>
+ </h3>
+ </div>
+ <div class="compText aAbs">
+ <p class="lh-18"><b><b>This is the </b>content</b>
+ </p>
+ </div>
+ </div>
+ </li>
+</ol>
+ """
+ response = mock.Mock(text=html)
+ results = yahoo.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title')
+ self.assertEqual(results[0]['url'], 'https://this.is.the.url/')
+ self.assertEqual(results[0]['content'], 'This is the content')
+
+ html = """
+ <li class="b_algo" u="0|5109|4755453613245655|UAGjXgIrPH5yh-o5oNHRx_3Zta87f_QO">
+ </li>
+ """
+ response = mock.Mock(text=html)
+ results = yahoo.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ def test_fetch_supported_languages(self):
+ html = """<html></html>"""
+ response = mock.Mock(text=html)
+ results = yahoo._fetch_supported_languages(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ html = """
+ <html>
+ <div>
+ <div id="yschlang">
+ <span>
+ <label><input value="lang_ar"></input></label>
+ </span>
+ <span>
+ <label><input value="lang_zh_chs"></input></label>
+ <label><input value="lang_zh_cht"></input></label>
+ </span>
+ </div>
+ </div>
+ </html>
+ """
+ response = mock.Mock(text=html)
+ languages = yahoo._fetch_supported_languages(response)
+ self.assertEqual(type(languages), list)
+ self.assertEqual(len(languages), 3)
+ self.assertIn('ar', languages)
+ self.assertIn('zh-chs', languages)
+ self.assertIn('zh-cht', languages)
diff --git a/tests/unit/engines/test_yahoo_news.py b/tests/unit/engines/test_yahoo_news.py
new file mode 100644
index 0000000..4d7fc0a
--- /dev/null
+++ b/tests/unit/engines/test_yahoo_news.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+from datetime import datetime
+import mock
+from searx.engines import yahoo_news
+from searx.testing import SearxTestCase
+
+
+class TestYahooNewsEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ dicto['language'] = 'fr_FR'
+ params = yahoo_news.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('news.search.yahoo.com', params['url'])
+ self.assertIn('fr', params['url'])
+ self.assertIn('cookies', params)
+ self.assertIn('sB', params['cookies'])
+ self.assertIn('fr', params['cookies']['sB'])
+
+ dicto['language'] = 'all'
+ params = yahoo_news.request(query, dicto)
+ self.assertIn('cookies', params)
+ self.assertIn('sB', params['cookies'])
+ self.assertIn('en', params['cookies']['sB'])
+ self.assertIn('en', params['url'])
+
+ def test_sanitize_url(self):
+ url = "test.url"
+ self.assertEqual(url, yahoo_news.sanitize_url(url))
+
+ url = "www.yahoo.com/;_ylt=test"
+ self.assertEqual("www.yahoo.com/", yahoo_news.sanitize_url(url))
+
+ def test_response(self):
+ self.assertRaises(AttributeError, yahoo_news.response, None)
+ self.assertRaises(AttributeError, yahoo_news.response, [])
+ self.assertRaises(AttributeError, yahoo_news.response, '')
+ self.assertRaises(AttributeError, yahoo_news.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(yahoo_news.response(response), [])
+
+ html = """
+ <ol class=" reg searchCenterMiddle">
+ <li class="first">
+ <div class="compTitle">
+ <h3>
+ <a class="yschttl spt" href="http://this.is.the.url" target="_blank">
+ This is
+ the <b>title</b>...
+ </a>
+ </h3>
+ </div>
+ <div>
+ <span class="cite">Business via Yahoo!</span>
+ <span class="tri fc-2nd ml-10">May 01 10:00 AM</span>
+ </div>
+ <div class="compText">
+ This is the content
+ </div>
+ </li>
+ <li class="first">
+ <div class="compTitle">
+ <h3>
+ <a class="yschttl spt" target="_blank">
+ </a>
+ </h3>
+ </div>
+ <div class="compText">
+ </div>
+ </li>
+ </ol>
+ """
+ response = mock.Mock(text=html)
+ results = yahoo_news.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'This is the title...')
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url/')
+ self.assertEqual(results[0]['content'], 'This is the content')
+
+ html = """
+ <ol class=" reg searchCenterMiddle">
+ <li class="first">
+ <div class="compTitle">
+ <h3>
+ <a class="yschttl spt" href="http://this.is.the.url" target="_blank">
+ This is
+ the <b>title</b>...
+ </a>
+ </h3>
+ </div>
+ <div>
+ <span class="cite">Business via Yahoo!</span>
+ <span class="tri fc-2nd ml-10">2 hours, 22 minutes ago</span>
+ </div>
+ <div class="compText">
+ This is the content
+ </div>
+ </li>
+ <li>
+ <div class="compTitle">
+ <h3>
+ <a class="yschttl spt" href="http://this.is.the.url" target="_blank">
+ This is
+ the <b>title</b>...
+ </a>
+ </h3>
+ </div>
+ <div>
+ <span class="cite">Business via Yahoo!</span>
+ <span class="tri fc-2nd ml-10">22 minutes ago</span>
+ </div>
+ <div class="compText">
+ This is the content
+ </div>
+ </li>
+ <li>
+ <div class="compTitle">
+ <h3>
+ <a class="yschttl spt" href="http://this.is.the.url" target="_blank">
+ This is
+ the <b>title</b>...
+ </a>
+ </h3>
+ </div>
+ <div>
+ <span class="cite">Business via Yahoo!</span>
+ <span class="tri fc-2nd ml-10">Feb 03 09:45AM 1900</span>
+ </div>
+ <div class="compText">
+ This is the content
+ </div>
+ </li>
+ </ol>
+ """
+ response = mock.Mock(text=html)
+ results = yahoo_news.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 3)
+ self.assertEqual(results[0]['title'], 'This is the title...')
+ self.assertEqual(results[0]['url'], 'http://this.is.the.url/')
+ self.assertEqual(results[0]['content'], 'This is the content')
+ self.assertEqual(results[2]['publishedDate'].year, datetime.now().year)
diff --git a/tests/unit/engines/test_youtube_api.py b/tests/unit/engines/test_youtube_api.py
new file mode 100644
index 0000000..0d4d478
--- /dev/null
+++ b/tests/unit/engines/test_youtube_api.py
@@ -0,0 +1,111 @@
+from collections import defaultdict
+import mock
+from searx.engines import youtube_api
+from searx.testing import SearxTestCase
+
+
+class TestYoutubeAPIEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ dicto['language'] = 'fr_FR'
+ params = youtube_api.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue(query in params['url'])
+ self.assertIn('googleapis.com', params['url'])
+ self.assertIn('youtube', params['url'])
+ self.assertIn('fr', params['url'])
+
+ dicto['language'] = 'all'
+ params = youtube_api.request(query, dicto)
+ self.assertFalse('fr' in params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, youtube_api.response, None)
+ self.assertRaises(AttributeError, youtube_api.response, [])
+ self.assertRaises(AttributeError, youtube_api.response, '')
+ self.assertRaises(AttributeError, youtube_api.response, '[]')
+
+ response = mock.Mock(text='{}')
+ self.assertEqual(youtube_api.response(response), [])
+
+ response = mock.Mock(text='{"data": []}')
+ self.assertEqual(youtube_api.response(response), [])
+
+ json = """
+ {
+ "kind": "youtube#searchListResponse",
+ "etag": "xmg9xJZuZD438sF4hb-VcBBREXc/YJQDcTBCDcaBvl-sRZJoXdvy1ME",
+ "nextPageToken": "CAUQAA",
+ "pageInfo": {
+ "totalResults": 1000000,
+ "resultsPerPage": 20
+ },
+ "items": [
+ {
+ "kind": "youtube#searchResult",
+ "etag": "xmg9xJZuZD438sF4hb-VcBBREXc/IbLO64BMhbHIgWLwLw7MDYe7Hs4",
+ "id": {
+ "kind": "youtube#video",
+ "videoId": "DIVZCPfAOeM"
+ },
+ "snippet": {
+ "publishedAt": "2015-05-29T22:41:04.000Z",
+ "channelId": "UCNodmx1ERIjKqvcJLtdzH5Q",
+ "title": "Title",
+ "description": "Description",
+ "thumbnails": {
+ "default": {
+ "url": "https://i.ytimg.com/vi/DIVZCPfAOeM/default.jpg"
+ },
+ "medium": {
+ "url": "https://i.ytimg.com/vi/DIVZCPfAOeM/mqdefault.jpg"
+ },
+ "high": {
+ "url": "https://i.ytimg.com/vi/DIVZCPfAOeM/hqdefault.jpg"
+ }
+ },
+ "channelTitle": "MinecraftUniverse",
+ "liveBroadcastContent": "none"
+ }
+ }
+ ]
+ }
+ """
+ response = mock.Mock(text=json)
+ results = youtube_api.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title')
+ self.assertEqual(results[0]['url'], 'https://www.youtube.com/watch?v=DIVZCPfAOeM')
+ self.assertEqual(results[0]['content'], 'Description')
+ self.assertEqual(results[0]['thumbnail'], 'https://i.ytimg.com/vi/DIVZCPfAOeM/hqdefault.jpg')
+ self.assertTrue('DIVZCPfAOeM' in results[0]['embedded'])
+
+ json = """
+ {
+ "kind": "youtube#searchListResponse",
+ "etag": "xmg9xJZuZD438sF4hb-VcBBREXc/YJQDcTBCDcaBvl-sRZJoXdvy1ME",
+ "nextPageToken": "CAUQAA",
+ "pageInfo": {
+ "totalResults": 1000000,
+ "resultsPerPage": 20
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ results = youtube_api.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
+
+ json = """
+ {"toto":{"entry":[]
+ }
+ }
+ """
+ response = mock.Mock(text=json)
+ results = youtube_api.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/engines/test_youtube_noapi.py b/tests/unit/engines/test_youtube_noapi.py
new file mode 100644
index 0000000..41dcbb7
--- /dev/null
+++ b/tests/unit/engines/test_youtube_noapi.py
@@ -0,0 +1,174 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import youtube_noapi
+from searx.testing import SearxTestCase
+
+
+class TestYoutubeNoAPIEngine(SearxTestCase):
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 0
+ dicto['time_range'] = ''
+ params = youtube_noapi.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('youtube.com', params['url'])
+
+ def test_time_range_search(self):
+ dicto = defaultdict(dict)
+ query = 'test_query'
+ dicto['time_range'] = 'year'
+ params = youtube_noapi.request(query, dicto)
+ self.assertIn('&sp=EgIIBQ%253D%253D', params['url'])
+
+ dicto['time_range'] = 'month'
+ params = youtube_noapi.request(query, dicto)
+ self.assertIn('&sp=EgIIBA%253D%253D', params['url'])
+
+ dicto['time_range'] = 'week'
+ params = youtube_noapi.request(query, dicto)
+ self.assertIn('&sp=EgIIAw%253D%253D', params['url'])
+
+ dicto['time_range'] = 'day'
+ params = youtube_noapi.request(query, dicto)
+ self.assertIn('&sp=EgIIAg%253D%253D', params['url'])
+
+ def test_response(self):
+ self.assertRaises(AttributeError, youtube_noapi.response, None)
+ self.assertRaises(AttributeError, youtube_noapi.response, [])
+ self.assertRaises(AttributeError, youtube_noapi.response, '')
+ self.assertRaises(AttributeError, youtube_noapi.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(youtube_noapi.response(response), [])
+
+ html = """
+ <ol id="item-section-063864" class="item-section">
+ <li>
+ <div class="yt-lockup yt-lockup-tile yt-lockup-video vve-check clearfix yt-uix-tile"
+ data-context-item-id="DIVZCPfAOeM"
+ data-visibility-tracking="CBgQ3DAYACITCPGXnYau6sUCFZEIHAod-VQASCj0JECx_-GK5uqMpcIB">
+ <div class="yt-lockup-dismissable"><div class="yt-lockup-thumbnail contains-addto">
+ <a aria-hidden="true" href="/watch?v=DIVZCPfAOeM" class=" yt-uix-sessionlink pf-link"
+ data-sessionlink="itct=CBgQ3DAYACITCPGXnYau6sUCFZEIHAod-VQASCj0JFIEdGVzdA">
+ <div class="yt-thumb video-thumb"><img src="//i.ytimg.com/vi/DIVZCPfAOeM/mqdefault.jpg"
+ width="196" height="110"/></div><span class="video-time" aria-hidden="true">11:35</span></a>
+ <span class="thumb-menu dark-overflow-action-menu video-actions">
+ </span>
+ </div>
+ <div class="yt-lockup-content">
+ <h3 class="yt-lockup-title">
+ <a href="/watch?v=DIVZCPfAOeM"
+ class="yt-uix-tile-link yt-ui-ellipsis yt-ui-ellipsis-2 yt-uix-sessionlink spf-link"
+ data-sessionlink="itct=CBgQ3DAYACITCPGXnYau6sUCFZEIHAod-VQASCj0JFIEdGVzdA"
+ title="Top Speed Test Kawasaki Ninja H2 (Thailand) By. MEHAY SUPERBIKE"
+ aria-describedby="description-id-259079" rel="spf-prefetch" dir="ltr">
+ Title
+ </a>
+ <span class="accessible-description" id="description-id-259079"> - Durée : 11:35.</span>
+ </h3>
+ <div class="yt-lockup-byline">de
+ <a href="/user/mheejapan" class=" yt-uix-sessionlink spf-link g-hovercard"
+ data-sessionlink="itct=CBgQ3DAYACITCPGXnYau6sUCFZEIHAod-VQASCj0JA" data-ytid="UCzEesu54Hjs0uRKmpy66qeA"
+ data-name="">MEHAY SUPERBIKE</a></div><div class="yt-lockup-meta">
+ <ul class="yt-lockup-meta-info">
+ <li>il y a 20 heures</li>
+ <li>8 424 vues</li>
+ </ul>
+ </div>
+ <div class="yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2" dir="ltr">
+ Description
+ </div>
+ <div class="yt-lockup-badges">
+ <ul class="yt-badge-list ">
+ <li class="yt-badge-item" >
+ <span class="yt-badge">Nouveauté</span>
+ </li>
+ <li class="yt-badge-item" ><span class="yt-badge " >HD</span></li>
+ </ul>
+ </div>
+ <div class="yt-lockup-action-menu yt-uix-menu-container">
+ <div class="yt-uix-menu yt-uix-videoactionmenu hide-until-delayloaded"
+ data-video-id="DIVZCPfAOeM" data-menu-content-id="yt-uix-videoactionmenu-menu">
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ </ol>
+ """
+ response = mock.Mock(text=html)
+ results = youtube_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'Title')
+ self.assertEqual(results[0]['url'], 'https://www.youtube.com/watch?v=DIVZCPfAOeM')
+ self.assertEqual(results[0]['content'], 'Description')
+ self.assertEqual(results[0]['thumbnail'], 'https://i.ytimg.com/vi/DIVZCPfAOeM/hqdefault.jpg')
+ self.assertTrue('DIVZCPfAOeM' in results[0]['embedded'])
+
+ html = """
+ <ol id="item-section-063864" class="item-section">
+ <li>
+ <div class="yt-lockup yt-lockup-tile yt-lockup-video vve-check clearfix yt-uix-tile"
+ data-context-item-id="DIVZCPfAOeM"
+ data-visibility-tracking="CBgQ3DAYACITCPGXnYau6sUCFZEIHAod-VQASCj0JECx_-GK5uqMpcIB">
+ <div class="yt-lockup-dismissable"><div class="yt-lockup-thumbnail contains-addto">
+ <a aria-hidden="true" href="/watch?v=DIVZCPfAOeM" class=" yt-uix-sessionlink pf-link"
+ data-sessionlink="itct=CBgQ3DAYACITCPGXnYau6sUCFZEIHAod-VQASCj0JFIEdGVzdA">
+ <div class="yt-thumb video-thumb"><img src="//i.ytimg.com/vi/DIVZCPfAOeM/mqdefault.jpg"
+ width="196" height="110"/></div><span class="video-time" aria-hidden="true">11:35</span></a>
+ <span class="thumb-menu dark-overflow-action-menu video-actions">
+ </span>
+ </div>
+ <div class="yt-lockup-content">
+ <h3 class="yt-lockup-title">
+ <span class="accessible-description" id="description-id-259079"> - Durée : 11:35.</span>
+ </h3>
+ <div class="yt-lockup-byline">de
+ <a href="/user/mheejapan" class=" yt-uix-sessionlink spf-link g-hovercard"
+ data-sessionlink="itct=CBgQ3DAYACITCPGXnYau6sUCFZEIHAod-VQASCj0JA" data-ytid="UCzEesu54Hjs0uRKmpy66qeA"
+ data-name="">MEHAY SUPERBIKE</a></div><div class="yt-lockup-meta">
+ <ul class="yt-lockup-meta-info">
+ <li>il y a 20 heures</li>
+ <li>8 424 vues</li>
+ </ul>
+ </div>
+ <div class="yt-lockup-badges">
+ <ul class="yt-badge-list ">
+ <li class="yt-badge-item" >
+ <span class="yt-badge">Nouveauté</span>
+ </li>
+ <li class="yt-badge-item" ><span class="yt-badge " >HD</span></li>
+ </ul>
+ </div>
+ <div class="yt-lockup-action-menu yt-uix-menu-container">
+ <div class="yt-uix-menu yt-uix-videoactionmenu hide-until-delayloaded"
+ data-video-id="DIVZCPfAOeM" data-menu-content-id="yt-uix-videoactionmenu-menu">
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+ </ol>
+ """
+ response = mock.Mock(text=html)
+ results = youtube_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+
+ html = """
+ <ol id="item-section-063864" class="item-section">
+ <li>
+ </li>
+ </ol>
+ """
+ response = mock.Mock(text=html)
+ results = youtube_noapi.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 0)
diff --git a/tests/unit/test_answerers.py b/tests/unit/test_answerers.py
new file mode 100644
index 0000000..bd8789a
--- /dev/null
+++ b/tests/unit/test_answerers.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+
+from mock import Mock
+
+from searx.answerers import answerers
+from searx.testing import SearxTestCase
+
+
+class AnswererTest(SearxTestCase):
+
+ def test_unicode_input(self):
+ query = Mock()
+ unicode_payload = u'árvíztűrő tükörfúrógép'
+ for answerer in answerers:
+ query.query = u'{} {}'.format(answerer.keywords[0], unicode_payload)
+ self.assertTrue(isinstance(answerer.answer(query), list))
diff --git a/tests/unit/test_plugins.py b/tests/unit/test_plugins.py
new file mode 100644
index 0000000..e497371
--- /dev/null
+++ b/tests/unit/test_plugins.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+
+from searx.testing import SearxTestCase
+from searx import plugins
+from mock import Mock
+
+
+def get_search_mock(query, **kwargs):
+ return Mock(search_query=Mock(query=query, **kwargs),
+ result_container=Mock(answers=set()))
+
+
+class PluginStoreTest(SearxTestCase):
+
+ def test_PluginStore_init(self):
+ store = plugins.PluginStore()
+ self.assertTrue(isinstance(store.plugins, list) and len(store.plugins) == 0)
+
+ def test_PluginStore_register(self):
+ store = plugins.PluginStore()
+ testplugin = plugins.Plugin()
+ store.register(testplugin)
+
+ self.assertTrue(len(store.plugins) == 1)
+
+ def test_PluginStore_call(self):
+ store = plugins.PluginStore()
+ testplugin = plugins.Plugin()
+ store.register(testplugin)
+ setattr(testplugin, 'asdf', Mock())
+ request = Mock()
+ store.call([], 'asdf', request, Mock())
+
+ self.assertFalse(testplugin.asdf.called)
+
+ store.call([testplugin], 'asdf', request, Mock())
+ self.assertTrue(testplugin.asdf.called)
+
+
+class SelfIPTest(SearxTestCase):
+
+ def test_PluginStore_init(self):
+ store = plugins.PluginStore()
+ store.register(plugins.self_info)
+
+ self.assertTrue(len(store.plugins) == 1)
+
+ # IP test
+ request = Mock(remote_addr='127.0.0.1')
+ request.headers.getlist.return_value = []
+ search = get_search_mock(query=b'ip', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('127.0.0.1' in search.result_container.answers)
+
+ search = get_search_mock(query=b'ip', pageno=2)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertFalse('127.0.0.1' in search.result_container.answers)
+
+ # User agent test
+ request = Mock(user_agent='Mock')
+ request.headers.getlist.return_value = []
+
+ search = get_search_mock(query=b'user-agent', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('Mock' in search.result_container.answers)
+
+ search = get_search_mock(query=b'user-agent', pageno=2)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertFalse('Mock' in search.result_container.answers)
+
+ search = get_search_mock(query=b'user-agent', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('Mock' in search.result_container.answers)
+
+ search = get_search_mock(query=b'user-agent', pageno=2)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertFalse('Mock' in search.result_container.answers)
+
+ search = get_search_mock(query=b'What is my User-Agent?', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('Mock' in search.result_container.answers)
+
+ search = get_search_mock(query=b'What is my User-Agent?', pageno=2)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertFalse('Mock' in search.result_container.answers)
diff --git a/tests/unit/test_preferences.py b/tests/unit/test_preferences.py
new file mode 100644
index 0000000..885c515
--- /dev/null
+++ b/tests/unit/test_preferences.py
@@ -0,0 +1,124 @@
+from searx.preferences import (EnumStringSetting, MapSetting, MissingArgumentException, SearchLanguageSetting,
+ MultipleChoiceSetting, PluginsSetting, ValidationException)
+from searx.testing import SearxTestCase
+
+
+class PluginStub(object):
+
+ def __init__(self, id, default_on):
+ self.id = id
+ self.default_on = default_on
+
+
+class TestSettings(SearxTestCase):
+ # map settings
+
+ def test_map_setting_invalid_initialization(self):
+ with self.assertRaises(MissingArgumentException):
+ setting = MapSetting(3, wrong_argument={'0': 0})
+
+ def test_map_setting_invalid_default_value(self):
+ with self.assertRaises(ValidationException):
+ setting = MapSetting(3, map={'dog': 1, 'bat': 2})
+
+ def test_map_setting_invalid_choice(self):
+ setting = MapSetting(2, map={'dog': 1, 'bat': 2})
+ with self.assertRaises(ValidationException):
+ setting.parse('cat')
+
+ def test_map_setting_valid_default(self):
+ setting = MapSetting(3, map={'dog': 1, 'bat': 2, 'cat': 3})
+ self.assertEquals(setting.get_value(), 3)
+
+ def test_map_setting_valid_choice(self):
+ setting = MapSetting(3, map={'dog': 1, 'bat': 2, 'cat': 3})
+ self.assertEquals(setting.get_value(), 3)
+ setting.parse('bat')
+ self.assertEquals(setting.get_value(), 2)
+
+ def test_enum_setting_invalid_initialization(self):
+ with self.assertRaises(MissingArgumentException):
+ setting = EnumStringSetting('cat', wrong_argument=[0, 1, 2])
+
+ # enum settings
+ def test_enum_setting_invalid_initialization(self):
+ with self.assertRaises(MissingArgumentException):
+ setting = EnumStringSetting('cat', wrong_argument=[0, 1, 2])
+
+ def test_enum_setting_invalid_default_value(self):
+ with self.assertRaises(ValidationException):
+ setting = EnumStringSetting(3, choices=[0, 1, 2])
+
+ def test_enum_setting_invalid_choice(self):
+ setting = EnumStringSetting(0, choices=[0, 1, 2])
+ with self.assertRaises(ValidationException):
+ setting.parse(3)
+
+ def test_enum_setting_valid_default(self):
+ setting = EnumStringSetting(3, choices=[1, 2, 3])
+ self.assertEquals(setting.get_value(), 3)
+
+ def test_enum_setting_valid_choice(self):
+ setting = EnumStringSetting(3, choices=[1, 2, 3])
+ self.assertEquals(setting.get_value(), 3)
+ setting.parse(2)
+ self.assertEquals(setting.get_value(), 2)
+
+ # multiple choice settings
+ def test_multiple_setting_invalid_initialization(self):
+ with self.assertRaises(MissingArgumentException):
+ setting = MultipleChoiceSetting(['2'], wrong_argument=['0', '1', '2'])
+
+ def test_multiple_setting_invalid_default_value(self):
+ with self.assertRaises(ValidationException):
+ setting = MultipleChoiceSetting(['3', '4'], choices=['0', '1', '2'])
+
+ def test_multiple_setting_invalid_choice(self):
+ setting = MultipleChoiceSetting(['1', '2'], choices=['0', '1', '2'])
+ with self.assertRaises(ValidationException):
+ setting.parse('4, 3')
+
+ def test_multiple_setting_valid_default(self):
+ setting = MultipleChoiceSetting(['3'], choices=['1', '2', '3'])
+ self.assertEquals(setting.get_value(), ['3'])
+
+ def test_multiple_setting_valid_choice(self):
+ setting = MultipleChoiceSetting(['3'], choices=['1', '2', '3'])
+ self.assertEquals(setting.get_value(), ['3'])
+ setting.parse('2')
+ self.assertEquals(setting.get_value(), ['2'])
+
+ # search language settings
+ def test_lang_setting_valid_choice(self):
+ setting = SearchLanguageSetting('all', choices=['all', 'de', 'en'])
+ setting.parse('de')
+ self.assertEquals(setting.get_value(), 'de')
+
+ def test_lang_setting_invalid_choice(self):
+ setting = SearchLanguageSetting('all', choices=['all', 'de', 'en'])
+ setting.parse('xx')
+ self.assertEquals(setting.get_value(), 'all')
+
+ def test_lang_setting_old_cookie_choice(self):
+ setting = SearchLanguageSetting('all', choices=['all', 'es', 'es-ES'])
+ setting.parse('es_XA')
+ self.assertEquals(setting.get_value(), 'es')
+
+ def test_lang_setting_old_cookie_format(self):
+ setting = SearchLanguageSetting('all', choices=['all', 'es', 'es-ES'])
+ setting.parse('es_ES')
+ self.assertEquals(setting.get_value(), 'es-ES')
+
+ # plugins settings
+ def test_plugins_setting_all_default_enabled(self):
+ plugin1 = PluginStub('plugin1', True)
+ plugin2 = PluginStub('plugin2', True)
+ setting = PluginsSetting(['3'], choices=[plugin1, plugin2])
+ self.assertEquals(setting.get_enabled(), set(['plugin1', 'plugin2']))
+
+ def test_plugins_setting_few_default_enabled(self):
+ plugin1 = PluginStub('plugin1', True)
+ plugin2 = PluginStub('plugin2', False)
+ plugin3 = PluginStub('plugin3', True)
+ setting = PluginsSetting('name', choices=[plugin1, plugin2, plugin3])
+ self.assertEquals(setting.get_enabled(), set(['plugin1', 'plugin3']))
diff --git a/tests/unit/test_results.py b/tests/unit/test_results.py
new file mode 100644
index 0000000..274b5b3
--- /dev/null
+++ b/tests/unit/test_results.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+from searx.results import ResultContainer
+from searx.testing import SearxTestCase
+
+
+def fake_result(url='https://aa.bb/cc?dd=ee#ff',
+ title='aaa',
+ content='bbb',
+ engine='wikipedia', **kwargs):
+ result = {'url': url,
+ 'title': title,
+ 'content': content,
+ 'engine': engine}
+ result.update(kwargs)
+ return result
+
+
+# TODO
+class ResultContainerTestCase(SearxTestCase):
+
+ def test_empty(self):
+ c = ResultContainer()
+ self.assertEqual(c.get_ordered_results(), [])
+
+ def test_one_result(self):
+ c = ResultContainer()
+ c.extend('wikipedia', [fake_result()])
+ self.assertEqual(c.results_length(), 1)
+
+ def test_one_suggestion(self):
+ c = ResultContainer()
+ c.extend('wikipedia', [fake_result(suggestion=True)])
+ self.assertEqual(len(c.suggestions), 1)
+ self.assertEqual(c.results_length(), 0)
+
+ def test_result_merge(self):
+ c = ResultContainer()
+ c.extend('wikipedia', [fake_result()])
+ c.extend('wikidata', [fake_result(), fake_result(url='https://example.com/')])
+ self.assertEqual(c.results_length(), 2)
diff --git a/tests/unit/test_search.py b/tests/unit/test_search.py
new file mode 100644
index 0000000..af5fffd
--- /dev/null
+++ b/tests/unit/test_search.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+
+from searx.testing import SearxTestCase
+
+
+# TODO
+class SearchTestCase(SearxTestCase):
+
+ def test_(self):
+ pass
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
new file mode 100644
index 0000000..eb40e62
--- /dev/null
+++ b/tests/unit/test_utils.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+import mock
+import sys
+from searx.testing import SearxTestCase
+from searx import utils
+
+if sys.version_info[0] == 3:
+ unicode = str
+
+
+class TestUtils(SearxTestCase):
+
+ def test_gen_useragent(self):
+ self.assertIsInstance(utils.gen_useragent(), str)
+ self.assertIsNotNone(utils.gen_useragent())
+ self.assertTrue(utils.gen_useragent().startswith('Mozilla'))
+
+ def test_searx_useragent(self):
+ self.assertIsInstance(utils.searx_useragent(), str)
+ self.assertIsNotNone(utils.searx_useragent())
+ self.assertTrue(utils.searx_useragent().startswith('searx'))
+
+ def test_highlight_content(self):
+ self.assertEqual(utils.highlight_content(0, None), None)
+ self.assertEqual(utils.highlight_content(None, None), None)
+ self.assertEqual(utils.highlight_content('', None), None)
+ self.assertEqual(utils.highlight_content(False, None), None)
+
+ contents = [
+ '<html></html>'
+ 'not<'
+ ]
+ for content in contents:
+ self.assertEqual(utils.highlight_content(content, None), content)
+
+ content = 'a'
+ query = b'test'
+ self.assertEqual(utils.highlight_content(content, query), content)
+ query = b'a test'
+ self.assertEqual(utils.highlight_content(content, query), content)
+
+ def test_html_to_text(self):
+ html = """
+ <a href="/testlink" class="link_access_account">
+ <span class="toto">
+ <span>
+ <img src="test.jpg" />
+ </span>
+ </span>
+ <span class="titi">
+ Test text
+ </span>
+ </a>
+ """
+ self.assertIsInstance(utils.html_to_text(html), unicode)
+ self.assertIsNotNone(utils.html_to_text(html))
+ self.assertEqual(utils.html_to_text(html), "Test text")
+
+ def test_prettify_url(self):
+ data = (('https://searx.me/', 'https://searx.me/'),
+ (u'https://searx.me/ű', u'https://searx.me/ű'),
+ ('https://searx.me/' + (100 * 'a'), 'https://searx.me/[...]aaaaaaaaaaaaaaaaa'),
+ (u'https://searx.me/' + (100 * u'ű'), u'https://searx.me/[...]űűűűűűűűűűűűűűűűű'))
+
+ for test_url, expected in data:
+ self.assertEqual(utils.prettify_url(test_url, max_length=32), expected)
+
+
+class TestHTMLTextExtractor(SearxTestCase):
+
+ def setUp(self):
+ self.html_text_extractor = utils.HTMLTextExtractor()
+
+ def test__init__(self):
+ self.assertEqual(self.html_text_extractor.result, [])
+
+ def test_handle_charref(self):
+ self.html_text_extractor.handle_charref('xF')
+ self.assertIn(u'\x0f', self.html_text_extractor.result)
+ self.html_text_extractor.handle_charref('XF')
+ self.assertIn(u'\x0f', self.html_text_extractor.result)
+
+ self.html_text_extractor.handle_charref('97')
+ self.assertIn(u'a', self.html_text_extractor.result)
+
+ def test_handle_entityref(self):
+ entity = 'test'
+ self.html_text_extractor.handle_entityref(entity)
+ self.assertIn(entity, self.html_text_extractor.result)
+
+
+class TestUnicodeWriter(SearxTestCase):
+
+ def setUp(self):
+ self.unicode_writer = utils.UnicodeWriter(mock.MagicMock())
+
+ def test_write_row(self):
+ row = [1, 2, 3]
+ self.assertEqual(self.unicode_writer.writerow(row), None)
+
+ def test_write_rows(self):
+ self.unicode_writer.writerow = mock.MagicMock()
+ rows = [1, 2, 3]
+ self.unicode_writer.writerows(rows)
+ self.assertEqual(self.unicode_writer.writerow.call_count, len(rows))
diff --git a/tests/unit/test_webapp.py b/tests/unit/test_webapp.py
new file mode 100644
index 0000000..45a08c1
--- /dev/null
+++ b/tests/unit/test_webapp.py
@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+
+import json
+from mock import Mock
+from searx import webapp
+from searx.testing import SearxTestCase
+from searx.search import Search
+from searx.url_utils import ParseResult
+
+
+class ViewsTestCase(SearxTestCase):
+
+ def setUp(self):
+ webapp.app.config['TESTING'] = True # to get better error messages
+ self.app = webapp.app.test_client()
+
+ # set some defaults
+ self.test_results = [
+ {
+ 'content': 'first test content',
+ 'title': 'First Test',
+ 'url': 'http://first.test.xyz',
+ 'engines': ['youtube', 'startpage'],
+ 'engine': 'startpage',
+ 'parsed_url': ParseResult(scheme='http', netloc='first.test.xyz', path='/', params='', query='', fragment=''), # noqa
+ }, {
+ 'content': 'second test content',
+ 'title': 'Second Test',
+ 'url': 'http://second.test.xyz',
+ 'engines': ['youtube', 'startpage'],
+ 'engine': 'youtube',
+ 'parsed_url': ParseResult(scheme='http', netloc='second.test.xyz', path='/', params='', query='', fragment=''), # noqa
+ },
+ ]
+
+ def search_mock(search_self, *args):
+ search_self.result_container = Mock(get_ordered_results=lambda: self.test_results,
+ answers=set(),
+ corrections=set(),
+ suggestions=set(),
+ infoboxes=[],
+ results=self.test_results,
+ results_number=lambda: 3,
+ results_length=lambda: len(self.test_results))
+
+ Search.search = search_mock
+
+ def get_current_theme_name_mock(override=None):
+ if override:
+ return override
+ return 'legacy'
+
+ webapp.get_current_theme_name = get_current_theme_name_mock
+
+ self.maxDiff = None # to see full diffs
+
+ def test_index_empty(self):
+ result = self.app.post('/')
+ self.assertEqual(result.status_code, 200)
+ self.assertIn(b'<div class="title"><h1>searx</h1></div>', result.data)
+
+ def test_index_html(self):
+ result = self.app.post('/', data={'q': 'test'})
+ self.assertIn(
+ b'<h3 class="result_title"><img width="14" height="14" class="favicon" src="/static/themes/legacy/img/icons/icon_youtube.ico" alt="youtube" /><a href="http://second.test.xyz" rel="noreferrer">Second <span class="highlight">Test</span></a></h3>', # noqa
+ result.data
+ )
+ self.assertIn(
+ b'<p class="content">first <span class="highlight">test</span> content<br class="last"/></p>', # noqa
+ result.data
+ )
+
+ def test_index_json(self):
+ result = self.app.post('/', data={'q': 'test', 'format': 'json'})
+
+ result_dict = json.loads(result.data.decode('utf-8'))
+
+ self.assertEqual('test', result_dict['query'])
+ self.assertEqual(result_dict['results'][0]['content'], 'first test content')
+ self.assertEqual(result_dict['results'][0]['url'], 'http://first.test.xyz')
+
+ def test_index_csv(self):
+ result = self.app.post('/', data={'q': 'test', 'format': 'csv'})
+
+ self.assertEqual(
+ b'title,url,content,host,engine,score\r\n'
+ b'First Test,http://first.test.xyz,first test content,first.test.xyz,startpage,\r\n' # noqa
+ b'Second Test,http://second.test.xyz,second test content,second.test.xyz,youtube,\r\n', # noqa
+ result.data
+ )
+
+ def test_index_rss(self):
+ result = self.app.post('/', data={'q': 'test', 'format': 'rss'})
+
+ self.assertIn(
+ b'<description>Search results for "test" - searx</description>',
+ result.data
+ )
+
+ self.assertIn(
+ b'<opensearch:totalResults>3</opensearch:totalResults>',
+ result.data
+ )
+
+ self.assertIn(
+ b'<title>First Test</title>',
+ result.data
+ )
+
+ self.assertIn(
+ b'<link>http://first.test.xyz</link>',
+ result.data
+ )
+
+ self.assertIn(
+ b'<description>first test content</description>',
+ result.data
+ )
+
+ def test_about(self):
+ result = self.app.get('/about')
+ self.assertEqual(result.status_code, 200)
+ self.assertIn(b'<h1>About <a href="/">searx</a></h1>', result.data)
+
+ def test_preferences(self):
+ result = self.app.get('/preferences')
+ self.assertEqual(result.status_code, 200)
+ self.assertIn(
+ b'<form method="post" action="/preferences" id="search_form">',
+ result.data
+ )
+ self.assertIn(
+ b'<legend>Default categories</legend>',
+ result.data
+ )
+ self.assertIn(
+ b'<legend>Interface language</legend>',
+ result.data
+ )
+
+ def test_stats(self):
+ result = self.app.get('/stats')
+ self.assertEqual(result.status_code, 200)
+ self.assertIn(b'<h2>Engine stats</h2>', result.data)
+
+ def test_robots_txt(self):
+ result = self.app.get('/robots.txt')
+ self.assertEqual(result.status_code, 200)
+ self.assertIn(b'Allow: /', result.data)
+
+ def test_opensearch_xml(self):
+ result = self.app.get('/opensearch.xml')
+ self.assertEqual(result.status_code, 200)
+ self.assertIn(b'<Description>a privacy-respecting, hackable metasearch engine</Description>', result.data)
+
+ def test_favicon(self):
+ result = self.app.get('/favicon.ico')
+ self.assertEqual(result.status_code, 200)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..6deafc2
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 120
diff --git a/utils/fabfile.py b/utils/fabfile.py
new file mode 100644
index 0000000..559e2ab
--- /dev/null
+++ b/utils/fabfile.py
@@ -0,0 +1,117 @@
+from fabric.api import cd, run, sudo, put
+from cStringIO import StringIO
+
+
+base_dir = '/usr/local'
+hostname = 'searx.me'
+searx_dir = base_dir + '/searx'
+searx_ve_dir = searx_dir + '/searx-ve'
+current_user = run('whoami').stdout.strip()
+
+uwsgi_file = '''
+[uwsgi]
+# Who will run the code
+uid = {user}
+gid = {user}
+
+# Number of workers
+workers = 8
+
+# The right granted on the created socket
+chmod-socket = 666
+
+# Plugin to use and interpretor config
+single-interpreter = true
+master = true
+plugin = python
+
+# Module to import
+module = searx.webapp
+
+# Virtualenv and python path
+virtualenv = {searx_ve_dir}
+pythonpath = {searx_dir}
+chdir = {searx_dir}/searx
+'''.format(user=current_user,
+ searx_dir=searx_dir,
+ searx_ve_dir=searx_ve_dir)
+
+nginx_config = '''
+server {{
+ listen 80;
+ server_name {hostname};
+ server_name www.{hostname};
+ root /usr/local/searx;
+
+ location / {{
+ include uwsgi_params;
+ uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+ }}
+}}
+'''.format(hostname=hostname)
+
+
+def stop():
+ sudo('/etc/init.d/uwsgi stop')
+
+
+def start():
+ sudo('/etc/init.d/uwsgi start')
+
+
+def restart():
+ sudo('/etc/init.d/uwsgi restart')
+
+
+def init():
+ if not run('test -d ' + searx_dir, warn_only=True).failed:
+ return
+
+ sudo('apt-get update')
+
+ sudo('apt-get install git'
+ ' build-essential'
+ ' libxslt-dev'
+ ' python-dev'
+ ' python-virtualenv'
+ ' python-pybabel'
+ ' zlib1g-dev'
+ ' uwsgi'
+ ' uwsgi-plugin-python'
+ ' nginx')
+
+ sudo('mkdir -p ' + base_dir)
+
+ put(StringIO(nginx_config), '/etc/nginx/sites-enabled/searx', use_sudo=True)
+ sudo('/etc/init.d/nginx restart')
+
+ with cd(base_dir):
+ sudo('git clone https://github.com/asciimoo/searx')
+
+ sudo('chown -R {user}:{user} {searx_dir}'.format(user=current_user, searx_dir=searx_dir))
+ put(StringIO(uwsgi_file), searx_dir + '/uwsgi.ini')
+ sudo('ln -s {0}/uwsgi.ini /etc/uwsgi/apps-enabled/searx.ini'.format(searx_dir))
+
+ run('virtualenv {0}'.format(searx_ve_dir))
+
+ with cd(searx_dir):
+ run('source {0}/bin/activate && pip install -r requirements.txt'.format(searx_ve_dir))
+
+ start()
+
+
+def deploy():
+ init()
+
+ with cd(searx_dir):
+ run("git stash", warn_only=True)
+ run("git pull origin master")
+ run("git stash pop", warn_only=True)
+
+ restart()
+
+
+def clean():
+ sudo('rm -rf {searx_dir}'.format(searx_dir=searx_dir), warn_only=True)
+ sudo('rm /etc/uwsgi/apps-enabled/searx.ini', warn_only=True)
+ sudo('rm /etc/nginx/sites-enabled/searx', warn_only=True)
diff --git a/utils/fetch_currencies.py b/utils/fetch_currencies.py
new file mode 100644
index 0000000..716b505
--- /dev/null
+++ b/utils/fetch_currencies.py
@@ -0,0 +1,161 @@
+# -*- coding: utf-8 -*-
+import json
+import re
+import unicodedata
+import string
+from urllib import urlencode
+from requests import get
+
+languages = {'de', 'en', 'es', 'fr', 'hu', 'it', 'nl', 'jp'}
+
+url_template = 'https://www.wikidata.org/w/api.php?action=wbgetentities&format=json&{query}&props=labels%7Cdatatype%7Cclaims%7Caliases&languages=' + '|'.join(languages)
+url_wmflabs_template = 'http://wdq.wmflabs.org/api?q='
+url_wikidata_search_template = 'http://www.wikidata.org/w/api.php?action=query&list=search&format=json&srnamespace=0&srprop=sectiontitle&{query}'
+
+wmflabs_queries = [
+ 'CLAIM[31:8142]', # all devise
+]
+
+db = {
+ 'iso4217': {
+ },
+ 'names': {
+ }
+}
+
+
+def remove_accents(data):
+ return unicodedata.normalize('NFKD', data).lower()
+
+
+def normalize_name(name):
+ return re.sub(' +', ' ', remove_accents(name.lower()).replace('-', ' '))
+
+
+def add_currency_name(name, iso4217):
+ global db
+
+ db_names = db['names']
+
+ if not isinstance(iso4217, basestring):
+ print "problem", name, iso4217
+ return
+
+ name = normalize_name(name)
+
+ if name == '':
+ print "name empty", iso4217
+ return
+
+ iso4217_set = db_names.get(name, None)
+ if iso4217_set is not None and iso4217 not in iso4217_set:
+ db_names[name].append(iso4217)
+ else:
+ db_names[name] = [iso4217]
+
+
+def add_currency_label(label, iso4217, language):
+ global db
+
+ db['iso4217'][iso4217] = db['iso4217'].get(iso4217, {})
+ db['iso4217'][iso4217][language] = label
+
+
+def get_property_value(data, name):
+ prop = data.get('claims', {}).get(name, {})
+ if len(prop) == 0:
+ return None
+
+ value = prop[0].get('mainsnak', {}).get('datavalue', {}).get('value', '')
+ if value == '':
+ return None
+
+ return value
+
+
+def parse_currency(data):
+ iso4217 = get_property_value(data, 'P498')
+
+ if iso4217 is not None:
+ unit = get_property_value(data, 'P558')
+ if unit is not None:
+ add_currency_name(unit, iso4217)
+
+ labels = data.get('labels', {})
+ for language in languages:
+ name = labels.get(language, {}).get('value', None)
+ if name is not None:
+ add_currency_name(name, iso4217)
+ add_currency_label(name, iso4217, language)
+
+ aliases = data.get('aliases', {})
+ for language in aliases:
+ for i in range(0, len(aliases[language])):
+ alias = aliases[language][i].get('value', None)
+ add_currency_name(alias, iso4217)
+
+
+def fetch_data(wikidata_ids):
+ url = url_template.format(query=urlencode({'ids': '|'.join(wikidata_ids)}))
+ htmlresponse = get(url)
+ jsonresponse = json.loads(htmlresponse.content)
+ entities = jsonresponse.get('entities', {})
+
+ for pname in entities:
+ pvalue = entities.get(pname)
+ parse_currency(pvalue)
+
+
+def add_q(i):
+ return "Q" + str(i)
+
+
+def fetch_data_batch(wikidata_ids):
+ while len(wikidata_ids) > 0:
+ if len(wikidata_ids) > 50:
+ fetch_data(wikidata_ids[0:49])
+ wikidata_ids = wikidata_ids[50:]
+ else:
+ fetch_data(wikidata_ids)
+ wikidata_ids = []
+
+
+def wdq_query(query):
+ url = url_wmflabs_template + query
+ htmlresponse = get(url)
+ jsonresponse = json.loads(htmlresponse.content)
+ qlist = map(add_q, jsonresponse.get('items', {}))
+ error = jsonresponse.get('status', {}).get('error', None)
+ if error is not None and error != 'OK':
+ print "error for query '" + query + "' :" + error
+
+ fetch_data_batch(qlist)
+
+
+def wd_query(query, offset=0):
+ qlist = []
+
+ url = url_wikidata_search_template.format(query=urlencode({'srsearch': query, 'srlimit': 50, 'sroffset': offset}))
+ htmlresponse = get(url)
+ jsonresponse = json.loads(htmlresponse.content)
+ for r in jsonresponse.get('query', {}).get('search', {}):
+ qlist.append(r.get('title', ''))
+ fetch_data_batch(qlist)
+
+
+# fetch #
+for q in wmflabs_queries:
+ wdq_query(q)
+
+# static
+add_currency_name(u"euro", 'EUR')
+add_currency_name(u"euros", 'EUR')
+add_currency_name(u"dollar", 'USD')
+add_currency_name(u"dollars", 'USD')
+add_currency_name(u"peso", 'MXN')
+add_currency_name(u"pesos", 'MXN')
+
+# write
+f = open("currencies.json", "wb")
+json.dump(db, f, indent=4, encoding="utf-8")
+f.close()
diff --git a/utils/fetch_languages.py b/utils/fetch_languages.py
new file mode 100644
index 0000000..3241370
--- /dev/null
+++ b/utils/fetch_languages.py
@@ -0,0 +1,189 @@
+# -*- coding: utf-8 -*-
+
+# This script generates languages.py from intersecting each engine's supported languages.
+#
+# The country names are obtained from http://api.geonames.org which requires registering as a user.
+#
+# Output files (engines_languages.json and languages.py)
+# are written in current directory to avoid overwriting in case something goes wrong.
+
+from requests import get
+from urllib import urlencode
+from lxml.html import fromstring
+from json import loads, dumps
+import io
+from sys import path
+path.append('../searx') # noqa
+from searx import settings
+from searx.engines import initialize_engines, engines
+
+# Geonames API for country names.
+geonames_user = '' # ADD USER NAME HERE
+country_names_url = 'http://api.geonames.org/countryInfoJSON?{parameters}'
+
+# Output files.
+engines_languages_file = 'engines_languages.json'
+languages_file = 'languages.py'
+
+engines_languages = {}
+
+
+# To filter out invalid codes and dialects.
+def valid_code(lang_code):
+ # filter invalid codes
+ # sl-SL is technically not invalid, but still a mistake
+ invalid_codes = ['sl-SL', 'wt-WT', 'jw']
+ invalid_countries = ['UK', 'XA', 'XL']
+ if lang_code[:2] == 'xx'\
+ or lang_code in invalid_codes\
+ or lang_code[-2:] in invalid_countries\
+ or is_dialect(lang_code):
+ return False
+
+ return True
+
+
+# Language codes with any additional tags other than language and country.
+def is_dialect(lang_code):
+ lang_code = lang_code.split('-')
+ if len(lang_code) > 2 or len(lang_code[0]) > 3:
+ return True
+ if len(lang_code) == 2 and len(lang_code[1]) > 2:
+ return True
+
+ return False
+
+
+# Get country name in specified language.
+def get_country_name(locale):
+ if geonames_user is '':
+ return ''
+
+ locale = locale.split('-')
+ if len(locale) != 2:
+ return ''
+
+ url = country_names_url.format(parameters=urlencode({'lang': locale[0],
+ 'country': locale[1],
+ 'username': geonames_user}))
+ response = get(url)
+ json = loads(response.text)
+ content = json.get('geonames', None)
+ if content is None or len(content) != 1:
+ print "No country name found for " + locale[0] + "-" + locale[1]
+ return ''
+
+ return content[0].get('countryName', '')
+
+
+# Fetchs supported languages for each engine and writes json file with those.
+def fetch_supported_languages():
+ initialize_engines(settings['engines'])
+ for engine_name in engines:
+ if hasattr(engines[engine_name], 'fetch_supported_languages'):
+ try:
+ engines_languages[engine_name] = engines[engine_name].fetch_supported_languages()
+ except Exception as e:
+ print e
+
+ # write json file
+ with io.open(engines_languages_file, "w", encoding="utf-8") as f:
+ f.write(unicode(dumps(engines_languages, ensure_ascii=False, encoding="utf-8")))
+
+
+# Join all language lists.
+# Iterate all languages supported by each engine.
+def join_language_lists():
+ global languages
+ # include wikipedia first for more accurate language names
+ languages = {code: lang for code, lang
+ in engines_languages['wikipedia'].iteritems()
+ if valid_code(code)}
+
+ for engine_name in engines_languages:
+ for locale in engines_languages[engine_name]:
+ if valid_code(locale):
+ # if language is not on list or if it has no name yet
+ if locale not in languages or not languages[locale].get('name'):
+ if isinstance(engines_languages[engine_name], dict):
+ languages[locale] = engines_languages[engine_name][locale]
+ else:
+ languages[locale] = {}
+
+ # add to counter of engines that support given language
+ lang = locale.split('-')[0]
+ if lang in languages:
+ if 'counter' not in languages[lang]:
+ languages[lang]['counter'] = [engine_name]
+ elif engine_name not in languages[lang]['counter']:
+ languages[lang]['counter'].append(engine_name)
+
+ # filter list to include only languages supported by most engines
+ min_supported_engines = int(0.70 * len(engines_languages))
+ languages = {code: lang for code, lang
+ in languages.iteritems()
+ if len(lang.get('counter', [])) >= min_supported_engines or
+ len(languages.get(code.split('-')[0], {}).get('counter', [])) >= min_supported_engines}
+
+ # get locales that have no name or country yet
+ for locale in languages.keys():
+ # try to get language names
+ if not languages[locale].get('name'):
+ name = languages.get(locale.split('-')[0], {}).get('name', None)
+ if name:
+ languages[locale]['name'] = name
+ else:
+ # filter out locales with no name
+ del languages[locale]
+ continue
+
+ # try to get language name in english
+ if not languages[locale].get('english_name'):
+ languages[locale]['english_name'] = languages.get(locale.split('-')[0], {}).get('english_name', '')
+
+ # try to get country name
+ if locale.find('-') > 0 and not languages[locale].get('country'):
+ languages[locale]['country'] = get_country_name(locale) or ''
+
+
+# Remove countryless language if language is featured in only one country.
+def filter_single_country_languages():
+ prev_lang = None
+ prev_code = None
+ for code in sorted(languages):
+ lang = code.split('-')[0]
+ if lang == prev_lang:
+ countries += 1
+ else:
+ if prev_lang is not None and countries == 1:
+ del languages[prev_lang]
+ languages[prev_code]['country'] = ''
+ countries = 0
+ prev_lang = lang
+ prev_code = code
+
+
+# Write languages.py.
+def write_languages_file():
+ new_file = open(languages_file, 'w')
+ file_content = '# -*- coding: utf-8 -*-\n'\
+ + '# list of language codes\n'\
+ + '# this file is generated automatically by utils/update_search_languages.py\n'\
+ + '\nlanguage_codes = ('
+ for code in sorted(languages):
+ file_content += '\n (u"' + code + '"'\
+ + ', u"' + languages[code]['name'].split(' (')[0] + '"'\
+ + ', u"' + languages[code].get('country', '') + '"'\
+ + ', u"' + languages[code].get('english_name', '').split(' (')[0] + '"),'
+ # remove last comma
+ file_content = file_content[:-1]
+ file_content += '\n)\n'
+ new_file.write(file_content.encode('utf8'))
+ new_file.close()
+
+
+if __name__ == "__main__":
+ fetch_supported_languages()
+ join_language_lists()
+ filter_single_country_languages()
+ write_languages_file()
diff --git a/utils/google_search.py b/utils/google_search.py
new file mode 100644
index 0000000..cad32ee
--- /dev/null
+++ b/utils/google_search.py
@@ -0,0 +1,35 @@
+from sys import argv, exit
+
+if not len(argv) > 1:
+ print('search query required')
+ exit(1)
+
+import requests
+from json import dumps
+from searx.engines import google
+from searx.search import default_request_params
+
+request_params = default_request_params()
+# Possible params
+# request_params['headers']['User-Agent'] = ''
+# request_params['category'] = ''
+request_params['pageno'] = 1
+request_params['language'] = 'en_us'
+request_params['time_range'] = ''
+
+params = google.request(argv[1], request_params)
+
+request_args = dict(
+ headers=request_params['headers'],
+ cookies=request_params['cookies'],
+)
+
+if request_params['method'] == 'GET':
+ req = requests.get
+else:
+ req = requests.post
+ request_args['data'] = request_params['data']
+
+resp = req(request_params['url'], **request_args)
+resp.search_params = request_params
+print(dumps(google.response(resp)))
diff --git a/utils/standalone_searx.py b/utils/standalone_searx.py
new file mode 100755
index 0000000..b19df4b
--- /dev/null
+++ b/utils/standalone_searx.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2016- by Alexandre Flament, <alex@al-f.net>
+'''
+
+# set path
+from sys import path
+from os.path import realpath, dirname
+path.append(realpath(dirname(realpath(__file__)) + '/../'))
+
+# initialization
+from json import dumps
+from searx import settings
+import searx.query
+import searx.search
+import searx.engines
+import searx.preferences
+import argparse
+
+searx.engines.initialize_engines(settings['engines'])
+
+# command line parsing
+parser = argparse.ArgumentParser(description='Standalone searx.')
+parser.add_argument('query', type=str,
+ help='Text query')
+parser.add_argument('--category', type=str, nargs='?',
+ choices=searx.engines.categories.keys(),
+ default='general',
+ help='Search category')
+parser.add_argument('--lang', type=str, nargs='?',default='all',
+ help='Search language')
+parser.add_argument('--pageno', type=int, nargs='?', default=1,
+ help='Page number starting from 1')
+parser.add_argument('--safesearch', type=str, nargs='?', choices=['0', '1', '2'], default='0',
+ help='Safe content filter from none to strict')
+parser.add_argument('--timerange', type=str, nargs='?', choices=['day', 'week', 'month', 'year'],
+ help='Filter by time range')
+args = parser.parse_args()
+
+# search results for the query
+form = {
+ "q":args.query,
+ "categories":args.category.decode('utf-8'),
+ "pageno":str(args.pageno),
+ "language":args.lang,
+ "time_range":args.timerange
+}
+preferences = searx.preferences.Preferences(['oscar'], searx.engines.categories.keys(), searx.engines.engines, [])
+preferences.key_value_settings['safesearch'].parse(args.safesearch)
+
+search_query = searx.search.get_search_query_from_webapp(preferences, form)
+search = searx.search.Search(search_query)
+result_container = search.search()
+
+# output
+from datetime import datetime
+
+def no_parsed_url(results):
+ for result in results:
+ del result['parsed_url']
+ return results
+
+def json_serial(obj):
+ """JSON serializer for objects not serializable by default json code"""
+ if isinstance(obj, datetime):
+ serial = obj.isoformat()
+ return serial
+ raise TypeError ("Type not serializable")
+
+result_container_json = {
+ "search": {
+ "q": search_query.query,
+ "pageno": search_query.pageno,
+ "lang": search_query.lang,
+ "safesearch": search_query.safesearch,
+ "timerange": search_query.time_range,
+ "engines": search_query.engines
+ },
+ "results": no_parsed_url(result_container.get_ordered_results()),
+ "infoboxes": result_container.infoboxes,
+ "suggestions": list(result_container.suggestions),
+ "answers": list(result_container.answers),
+ "paging": result_container.paging,
+ "results_number": result_container.results_number()
+}
+
+print(dumps(result_container_json, sort_keys=True, indent=4, ensure_ascii=False, encoding="utf-8", default=json_serial))
diff --git a/utils/update-translations.sh b/utils/update-translations.sh
new file mode 100755
index 0000000..00e7fb1
--- /dev/null
+++ b/utils/update-translations.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# script to easily update translation language files
+
+# add new language:
+# pybabel init -i messages.pot -d searx/translations -l en
+
+SEARX_DIR='searx'
+
+pybabel extract -F babel.cfg -o messages.pot $SEARX_DIR
+for f in `ls $SEARX_DIR'/translations/'`; do
+ pybabel update -N -i messages.pot -d $SEARX_DIR'/translations/' -l $f
+done
+
+echo '[!] update done, edit .po files if required and run pybabel compile -d searx/translations/'