diff --git a/README.md b/README.md index b6015ac..6f914ad 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,11 @@ Contents 1. [Set Primary Search Engine](#set-whoogle-as-your-primary-search-engine) 2. [Prevent Downtime (Heroku Only)](#prevent-downtime-heroku-only) 3. [Manual HTTPS Enforcement](#https-enforcement) -7. [FAQ](#faq) -8. [Public Instances](#public-instances) -9. [Screenshots](#screenshots) -10. Mirrors (read-only) +7. [Contributing](#contributing) +8. [FAQ](#faq) +9. [Public Instances](#public-instances) +10. [Screenshots](#screenshots) +11. Mirrors (read-only) 1. [GitLab](https://gitlab.com/benbusby/whoogle-search) 2. [Gogs](https://gogs.benbusby.com/benbusby/whoogle-search) @@ -365,6 +366,56 @@ Note: You should have your own domain name and [an https certificate](https://le - Pip/Pipx: Add the `--https-only` flag to the end of the `whoogle-search` command - Default `run` script: Modify the script locally to include the `--https-only` flag at the end of the python run command +## Contributing + +Under the hood, Whoogle is a basic Flask app with the following structure: + +- `app/` + - `routes.py`: Primary app entrypoint, contains all API routes + - `request.py`: Handles all outbound requests, including proxied/Tor connectivity + - `filter.py`: Functions and utilities used for filtering out content from upstream Google search results + - `utils/` + - `bangs.py`: All logic related to handling DDG-style "bang" queries + - `results.py`: Utility functions for interpreting/modifying individual search results + - `search.py`: Creates and handles new search queries + - `session.py`: Miscellaneous methods related to user sessions + - `templates/` + - `index.html`: The home page template + - `display.html`: The search results template + - `header.html`: A general "top of the page" query header for desktop and mobile + - `search.html`: An iframe-able search page + - `logo.html`: A template consisting mostly of the Whoogle logo as an SVG (separated to help keep `index.html` a bit cleaner) + - `opensearch.xml`: A template used for supporting [OpenSearch](https://developer.mozilla.org/en-US/docs/Web/OpenSearch). + - `imageresults.html`: An "exprimental" template used for supporting the "Full Size" image feature on desktop. + - `static/` + - CSS/Javascript files, should be self-explanatory + - `static/settings` + - Key-value JSON files for establishing valid configuration values + + +If you're new to the project, the easiest way to get started would be to try fixing [an open bug report](https://github.com/benbusby/whoogle-search/issues?q=is%3Aissue+is%3Aopen+label%3Abug). If there aren't any open, or if the open ones are too stale, try taking on a [feature request](https://github.com/benbusby/whoogle-search/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement). Generally speaking, if you can write something that has any potential of breaking down in the future, you should write a test for it. + +The project follows the [PEP 8 Style Guide](https://www.python.org/dev/peps/pep-0008/), but is liable to change. Static typing should always be used when possible. Function documentation is greatly appreciated, and typically follows the below format: + +```python +def contains(x: list, y: int) -> bool: + """Check a list (x) for the presence of an element (y) + + Args: + x: The list to inspect + y: The int to look for + + Returns: + bool: True if the list contains the item, otherwise False + """ + + return y in x +``` + +#### Translating + +Whoogle currently supports translations using [`translations.json`](https://github.com/benbusby/whoogle-search/blob/main/app/static/settings/languages.json). Language values in this file need to match the "value" of the according language in [`languages.json`](https://github.com/benbusby/whoogle-search/blob/main/app/static/settings/languages.json) (i.e. "lang_en" for English, "lang_es" for Spanish, etc). After you add a new set of translations to `translations.json`, open a PR with your changes and they will be merged in as soon as possible. + ## FAQ **What's the difference between this and [Searx](https://github.com/asciimoo/searx)?** diff --git a/app/__init__.py b/app/__init__.py index c3d781b..06d9306 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -33,6 +33,8 @@ app.config['LANGUAGES'] = json.load(open( os.path.join(app.config['STATIC_FOLDER'], 'settings/languages.json'))) app.config['COUNTRIES'] = json.load(open( os.path.join(app.config['STATIC_FOLDER'], 'settings/countries.json'))) +app.config['TRANSLATIONS'] = json.load(open( + os.path.join(app.config['STATIC_FOLDER'], 'settings/translations.json'))) app.config['CONFIG_PATH'] = os.getenv( 'CONFIG_VOLUME', os.path.join(app.config['STATIC_FOLDER'], 'config')) diff --git a/app/models/config.py b/app/models/config.py index 5b2f192..8413a65 100644 --- a/app/models/config.py +++ b/app/models/config.py @@ -77,6 +77,19 @@ class Config: return key in self.safe_keys + def get_localization_lang(self): + """Returns the correct language to use for localization, but falls + back to english if not set. + + Returns: + str -- the localization language string + """ + if (self.lang_interface and + self.lang_interface in current_app.config['TRANSLATIONS']): + return self.lang_interface + + return 'lang_en' + def from_params(self, params) -> 'Config': """Modify user config with search parameters. This is primarily used for specifying configuration on a search-by-search basis on diff --git a/app/routes.py b/app/routes.py index 2e8152a..4409901 100644 --- a/app/routes.py +++ b/app/routes.py @@ -130,6 +130,9 @@ def index(): return render_template('index.html', languages=app.config['LANGUAGES'], countries=app.config['COUNTRIES'], + translation=app.config['TRANSLATIONS'][ + g.user_config.get_localization_lang() + ], logo=render_template( 'logo.html', dark=g.user_config.dark), @@ -235,6 +238,9 @@ def search(): query=urlparse.unquote(query), search_type=search_util.search_type, config=g.user_config, + translation=app.config['TRANSLATIONS'][ + g.user_config.get_localization_lang() + ], response=response, version_number=app.config['VERSION_NUMBER'], search_header=(render_template( diff --git a/app/static/settings/countries.json b/app/static/settings/countries.json index 57c4619..da5ce2f 100644 --- a/app/static/settings/countries.json +++ b/app/static/settings/countries.json @@ -1,5 +1,5 @@ [ - {"name": "Default (none)", "value": ""}, + {"name": "-------", "value": ""}, {"name": "Afghanistan", "value": "countryAF"}, {"name": "Albania", "value": "countryAL"}, {"name": "Algeria", "value": "countryDZ"}, diff --git a/app/static/settings/languages.json b/app/static/settings/languages.json index 4666b7c..7056afa 100644 --- a/app/static/settings/languages.json +++ b/app/static/settings/languages.json @@ -1,49 +1,49 @@ [ - {"name": "Default (none specified)", "value": ""}, + {"name": "-------", "value": ""}, {"name": "English", "value": "lang_en"}, - {"name": "Afrikaans", "value": "lang_af"}, - {"name": "Arabic", "value": "lang_ar"}, - {"name": "Armenian", "value": "lang_hy"}, - {"name": "Belarusian", "value": "lang_be"}, - {"name": "Bulgarian", "value": "lang_bg"}, - {"name": "Catalan", "value": "lang_ca"}, - {"name": "Chinese (Simplified)", "value": "lang_zh-CN"}, - {"name": "Chinese (Traditional)", "value": "lang_zh-TW"}, - {"name": "Croatian", "value": "lang_hr"}, - {"name": "Czech", "value": "lang_cs"}, - {"name": "Danish", "value": "lang_da"}, - {"name": "Dutch", "value": "lang_nl"}, - {"name": "Esperanto", "value": "lang_eo"}, - {"name": "Estonian", "value": "lang_et"}, - {"name": "Filipino", "value": "lang_tl"}, - {"name": "Finnish", "value": "lang_fi"}, - {"name": "French", "value": "lang_fr"}, - {"name": "German", "value": "lang_de"}, - {"name": "Greek", "value": "lang_el"}, - {"name": "Hebrew", "value": "lang_iw"}, - {"name": "Hindi", "value": "lang_hi"}, - {"name": "Hungarian", "value": "lang_hu"}, - {"name": "Icelandic", "value": "lang_is"}, - {"name": "Indonesian", "value": "lang_id"}, - {"name": "Italian", "value": "lang_it"}, - {"name": "Japanese", "value": "lang_ja"}, - {"name": "Korean", "value": "lang_ko"}, - {"name": "Latvian", "value": "lang_lv"}, - {"name": "Lithuanian", "value": "lang_lt"}, - {"name": "Norwegian", "value": "lang_no"}, - {"name": "Persian", "value": "lang_fa"}, - {"name": "Polish", "value": "lang_pl"}, - {"name": "Portuguese", "value": "lang_pt"}, - {"name": "Romanian", "value": "lang_ro"}, - {"name": "Russian", "value": "lang_ru"}, - {"name": "Serbian", "value": "lang_sr"}, - {"name": "Slovak", "value": "lang_sk"}, - {"name": "Slovenian", "value": "lang_sl"}, - {"name": "Spanish", "value": "lang_es"}, - {"name": "Swahili", "value": "lang_sw"}, - {"name": "Swedish", "value": "lang_sv"}, - {"name": "Thai", "value": "lang_th"}, - {"name": "Turkish", "value": "lang_tr"}, - {"name": "Ukrainian", "value": "lang_uk"}, - {"name": "Vietnamese", "value": "lang_vi"} + {"name": "Afrikaans (Afrikaans)", "value": "lang_af"}, + {"name": "Arabic (عربى)", "value": "lang_ar"}, + {"name": "Armenian (հայերեն)", "value": "lang_hy"}, + {"name": "Belarusian (Беларуская)", "value": "lang_be"}, + {"name": "Bulgarian (български)", "value": "lang_bg"}, + {"name": "Catalan (Català)", "value": "lang_ca"}, + {"name": "Chinese, Simplified (简体中文)", "value": "lang_zh-CN"}, + {"name": "Chinese, Traditional (繁体中文)", "value": "lang_zh-TW"}, + {"name": "Croatian (Hrvatski)", "value": "lang_hr"}, + {"name": "Czech (čeština)", "value": "lang_cs"}, + {"name": "Danish (Dansk)", "value": "lang_da"}, + {"name": "Dutch (Nederlands)", "value": "lang_nl"}, + {"name": "Esperanto (Esperanto)", "value": "lang_eo"}, + {"name": "Estonian (Eestlane)", "value": "lang_et"}, + {"name": "Filipino (Pilipino)", "value": "lang_tl"}, + {"name": "Finnish (Suomalainen)", "value": "lang_fi"}, + {"name": "French (Français)", "value": "lang_fr"}, + {"name": "German (Deutsche)", "value": "lang_de"}, + {"name": "Greek (Ελληνικά)", "value": "lang_el"}, + {"name": "Hebrew (עִברִית)", "value": "lang_iw"}, + {"name": "Hindi (हिंदी)", "value": "lang_hi"}, + {"name": "Hungarian (Magyar)", "value": "lang_hu"}, + {"name": "Icelandic (Íslenska)", "value": "lang_is"}, + {"name": "Indonesian (Indonesian)", "value": "lang_id"}, + {"name": "Italian (Italiano)", "value": "lang_it"}, + {"name": "Japanese (日本語)", "value": "lang_ja"}, + {"name": "Korean (한국어)", "value": "lang_ko"}, + {"name": "Latvian (Latvietis)", "value": "lang_lv"}, + {"name": "Lithuanian (Lietuvis)", "value": "lang_lt"}, + {"name": "Norwegian (Norwegian)", "value": "lang_no"}, + {"name": "Persian (فارسی)", "value": "lang_fa"}, + {"name": "Polish (Polskie)", "value": "lang_pl"}, + {"name": "Portugese (Português)", "value": "lang_pt"}, + {"name": "Romanian (Română)", "value": "lang_ro"}, + {"name": "Russian (русский)", "value": "lang_ru"}, + {"name": "Serbian (Српски)", "value": "lang_sr"}, + {"name": "Slovak (Slovák)", "value": "lang_sk"}, + {"name": "Slovenian (Slovenščina)", "value": "lang_sl"}, + {"name": "Spanish (Español)", "value": "lang_es"}, + {"name": "Swahili (Kiswahili)", "value": "lang_sw"}, + {"name": "Swedish (Svenska)", "value": "lang_sv"}, + {"name": "Thai (ไทย)", "value": "lang_th"}, + {"name": "Turkish (Türk)", "value": "lang_tr"}, + {"name": "Ukranian (Український)", "value": "lang_uk"}, + {"name": "Vietnamese (Tiếng Việt)", "value": "lang_vi"} ] diff --git a/app/static/settings/translations.json b/app/static/settings/translations.json new file mode 100644 index 0000000..98e8030 --- /dev/null +++ b/app/static/settings/translations.json @@ -0,0 +1,58 @@ +{ + "lang_en": { + "search": "Search", + "config": "Configuration", + "config-country": "Filter Results by Country", + "config-country-help": "Note: If enabled, a website will only appear in the search results if it is *hosted* in the selected country.", + "config-lang": "Interface Language", + "config-lang-search": "Search Language", + "config-near": "Near", + "config-near-help": "City Name", + "config-block": "Block", + "config-block-help": "Comma-separated site list", + "config-nojs": "Show NoJS Links", + "config-dark": "Dark Mode", + "config-safe": "Safe Search", + "config-alts": "Replace Social Media Links", + "config-alts-help": "Replaces Twitter/YouTube/Instagram/etc links with privacy respecting alternatives.", + "config-new-tab": "Open Links in New Tab", + "config-images": "Full Size Image Search", + "config-images-help": "(Experimental) Adds the 'View Image' option to desktop image searches. This will cause image result thumbnails to be lower resolution.", + "config-tor": "Use Tor", + "config-get-only": "GET Requests Only", + "config-url": "Root URL", + "config-css": "Custom CSS", + "load": "Load", + "apply": "Apply", + "save-as": "Save As...", + "github-link": "View on GitHub" + }, + "lang_es": { + "search": "Buscar", + "config": "Configuración", + "config-country": "Filtrar Resultados por País", + "config-country-help": "Nota: Si está habilitado, un sitio web solo aparecerá en los resultados de búsqueda si está alojado en ese país.", + "config-lang": "Idioma de Interfaz", + "config-lang-search": "Idioma de Búsqueda", + "config-near": "Cerca", + "config-near-help": "Nombre de la Ciudad", + "config-block": "Bloquear", + "config-block-help": "Lista de sitios separados por comas", + "config-nojs": "Mostrar Enlaces NoJS", + "config-dark": "Modo Oscuro", + "config-safe": "Búsqueda Segura", + "config-alts": "Reemplazar Enlaces de Redes Sociales", + "config-alts-help": "Reemplaza los enlaces de Twitter/YouTube/Instagram/etc con alternativas que respetan la privacidad.", + "config-new-tab": "Abrir enlaces en una pestaña nueva", + "config-images": "Búsqueda de imágenes a tamaño completo", + "config-images-help": "(Experimental) Agrega la opción 'Ver imagen' a las búsquedas de imágenes de escritorio. Esto hará que las miniaturas de los resultados de la imagen aparezcan con una resolución más baja.", + "config-tor": "Usa Tor", + "config-get-only": "GET solo solicitudes", + "config-url": "URL raíz", + "config-css": "CSS personalizado", + "load": "Cargar", + "apply": "Aplicar", + "save-as": "Guardar como...", + "github-link": "Ver en GitHub" + } +} diff --git a/app/templates/display.html b/app/templates/display.html index 398e276..8c30f6e 100644 --- a/app/templates/display.html +++ b/app/templates/display.html @@ -20,7 +20,7 @@ diff --git a/app/templates/index.html b/app/templates/index.html index d063ba5..5cba56c 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -53,17 +53,17 @@ autocorrect="off" autocomplete="off"> - + {% if not config_disabled %}
- +
- + -
— Note: If enabled, a website will only appear in the results if it is *hosted* in the selected country.
+
— {{ translation['config-country-help'] }}
- + {% for lang in languages %}
- +
- +
- +
- +
- +
- + -
— Replaces Twitter/YouTube/Instagram/Reddit links - with Nitter/Invidious/Bibliogram/Libreddit links.
+
— {{ translation['config-alts-help'] }}
- +
- + -
— (Experimental) Adds the "View Image" option on desktop to view full size images in search results. - This will cause image result thumbnails to be lower resolution.
+
— {{ translation['config-images-help'] }}
- +
- +
- +
- +