feat: begin working on localisation

This commit is contained in:
frosty 2026-04-01 00:37:15 +03:00
parent 335b6f4683
commit c41ab84738
16 changed files with 275 additions and 63 deletions

29
locales/ca_ca.ini Normal file
View file

@ -0,0 +1,29 @@
[Meta]
Id = "ca_ca"
Name = "Cat (Demo)"
Direction = "ltr"
[Keys]
search_placeholder = "meow"
search_button = "meow"
surprise_me_button = "meow"
all_tab = "meow"
images_tab = "meow"
settings_tab = "meow"
settings_title = "meow"
theme_label = "meow"
theme_desc = "meow"
theme_system = "meow"
theme_light = "meow"
theme_dark = "meow"
language_label = "meow"
display_language_label = "meow"
language_desc = "meow"
save_settings_button = "meow"
no_results = "meow"
error_images = "meow"
rate_limit = "meow"
read_more = "meow"
view_cached = "meow"
view_image = "meow"
visit_site = "meow"

29
locales/en_uk.ini Normal file
View file

@ -0,0 +1,29 @@
[Meta]
Id = "en_uk"
Name = "English (Traditional)"
Direction = "ltr"
[Keys]
search_placeholder = "Search the web..."
search_button = "Search"
surprise_me_button = "Surprise me"
all_tab = "All"
images_tab = "Images"
settings_tab = "Settings"
settings_title = "Settings"
theme_label = "Appearance"
theme_desc = "Choose your preferred colour scheme."
theme_system = "System"
theme_light = "Light"
theme_dark = "Dark"
language_label = "Language"
display_language_label = "Display Language"
language_desc = "Choose your preferred language."
save_settings_button = "Save Settings"
no_results = "No results found"
error_images = "Error fetching images"
rate_limit = "Slow down! Too many searches from you!"
read_more = "Read More"
view_cached = "Cached"
view_image = "Image"
visit_site = "Site"

29
locales/en_us.ini Normal file
View file

@ -0,0 +1,29 @@
[Meta]
Id = "en_us"
Name = "English (Simplified)"
Direction = "ltr"
[Keys]
search_placeholder = "Search the web..."
search_button = "Search"
surprise_me_button = "Surprise me"
all_tab = "All"
images_tab = "Images"
settings_tab = "Settings"
settings_title = "Settings"
theme_label = "Appearance"
theme_desc = "Choose your preferred color scheme."
theme_system = "System"
theme_light = "Light"
theme_dark = "Dark"
language_label = "Language"
display_language_label = "Display Language"
language_desc = "Choose your preferred language."
save_settings_button = "Save Settings"
no_results = "No results found"
error_images = "Error fetching images"
rate_limit = "Slow down! Too many searches from you!"
read_more = "Read More"
view_cached = "Cached"
view_image = "Image"
visit_site = "Site"

View file

@ -67,6 +67,13 @@ int main() {
global_config = cfg; global_config = cfg;
int loaded = beaker_load_locales();
if (loaded > 0) {
fprintf(stderr, "[INFO] Loaded %d locales\n", loaded);
} else {
fprintf(stderr, "[WARN] No locales loaded (make sure to run from omnisearch directory)\n");
}
apply_engines_config(cfg.engines); apply_engines_config(cfg.engines);
if (cache_init(cfg.cache_dir) != 0) { if (cache_init(cfg.cache_dir) != 0) {
@ -119,6 +126,7 @@ int main() {
curl_global_cleanup(); curl_global_cleanup();
xmlCleanupParser(); xmlCleanupParser();
beaker_free_locales();
free_proxy_list(); free_proxy_list();
cache_shutdown(); cache_shutdown();
return EXIT_SUCCESS; return EXIT_SUCCESS;

View file

@ -1,19 +1,23 @@
#include "Home.h" #include "Home.h"
#include "../Utility/Utility.h" #include "../Utility/Utility.h"
#include <beaker.h>
#include <stdlib.h> #include <stdlib.h>
int home_handler(UrlParams *params) { int home_handler(UrlParams *params) {
(void)params; (void)params;
char *theme = get_theme(""); char *theme = get_theme("");
char *locale = get_locale("en_uk");
TemplateContext ctx = new_context(); TemplateContext ctx = new_context();
context_set(&ctx, "theme", theme); context_set(&ctx, "theme", theme);
beaker_set_locale(&ctx, locale);
char *rendered_html = render_template("home.html", &ctx); char *rendered_html = render_template("home.html", &ctx);
send_response(rendered_html); send_response(rendered_html);
free(rendered_html); free(rendered_html);
free_context(&ctx); free_context(&ctx);
free(theme); free(theme);
free(locale);
return 0; return 0;
} }

View file

@ -5,6 +5,7 @@
#include "../Utility/Unescape.h" #include "../Utility/Unescape.h"
#include "../Utility/Utility.h" #include "../Utility/Utility.h"
#include "Config.h" #include "Config.h"
#include <beaker.h>
static char *build_images_request_cache_key(const char *query, int page, static char *build_images_request_cache_key(const char *query, int page,
const char *client_key) { const char *client_key) {
@ -46,6 +47,10 @@ int images_handler(UrlParams *params) {
context_set(&ctx, "theme", theme); context_set(&ctx, "theme", theme);
free(theme); free(theme);
char *locale = get_locale("en_uk");
beaker_set_locale(&ctx, locale);
free(locale);
context_set(&ctx, "page", page_str); context_set(&ctx, "page", page_str);
context_set(&ctx, "prev_page", prev_str); context_set(&ctx, "prev_page", prev_str);
context_set(&ctx, "next_page", next_str); context_set(&ctx, "next_page", next_str);

View file

@ -419,6 +419,10 @@ int results_handler(UrlParams *params) {
context_set(&ctx, "theme", theme); context_set(&ctx, "theme", theme);
free(theme); free(theme);
char *locale = get_locale("en_uk");
beaker_set_locale(&ctx, locale);
free(locale);
char page_str[16]; char page_str[16];
snprintf(page_str, sizeof(page_str), "%d", page); snprintf(page_str, sizeof(page_str), "%d", page);
context_set(&ctx, "page", page_str); context_set(&ctx, "page", page_str);

View file

@ -1,5 +1,6 @@
#include "Settings.h" #include "Settings.h"
#include "../Utility/Utility.h" #include "../Utility/Utility.h"
#include <beaker.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -9,21 +10,42 @@ int settings_handler(UrlParams *params) {
for (int i = 0; i < params->count; i++) { for (int i = 0; i < params->count; i++) {
if (strcmp(params->params[i].key, "q") == 0) { if (strcmp(params->params[i].key, "q") == 0) {
query = params->params[i].value; query = params->params[i].value;
break;
} }
} }
} }
char *theme = get_theme("system"); char *theme = get_theme("system");
char *locale = get_locale("en_uk");
LocaleInfo locales[32];
int locale_count = beaker_get_all_locales(locales, 32);
char **locale_data[32];
int inner_counts[32];
for (int i = 0; i < locale_count; i++) {
locale_data[i] = malloc(sizeof(char *) * 2);
locale_data[i][0] = locales[i].meta.id;
locale_data[i][1] = locales[i].meta.name;
inner_counts[i] = 2;
}
TemplateContext ctx = new_context(); TemplateContext ctx = new_context();
beaker_set_locale(&ctx, locale);
context_set(&ctx, "query", query); context_set(&ctx, "query", query);
context_set(&ctx, "theme", theme); context_set(&ctx, "theme", theme);
context_set(&ctx, "locale", locale);
context_set_array_of_arrays(&ctx, "locales", locale_data, locale_count, inner_counts);
for (int i = 0; i < locale_count; i++) {
free(locale_data[i]);
}
char *rendered_html = render_template("settings.html", &ctx); char *rendered_html = render_template("settings.html", &ctx);
send_response(rendered_html); send_response(rendered_html);
free(rendered_html); free(rendered_html);
free(theme); free(theme);
free(locale);
free_context(&ctx); free_context(&ctx);
return 0; return 0;

View file

@ -4,12 +4,15 @@
int settings_save_handler(UrlParams *params) { int settings_save_handler(UrlParams *params) {
const char *theme = ""; const char *theme = "";
const char *locale = "";
const char *query = ""; const char *query = "";
if (params) { if (params) {
for (int i = 0; i < params->count; i++) { for (int i = 0; i < params->count; i++) {
if (strcmp(params->params[i].key, "theme") == 0) { if (strcmp(params->params[i].key, "theme") == 0) {
theme = params->params[i].value; theme = params->params[i].value;
} else if (strcmp(params->params[i].key, "locale") == 0) {
locale = params->params[i].value;
} else if (strcmp(params->params[i].key, "q") == 0) { } else if (strcmp(params->params[i].key, "q") == 0) {
query = params->params[i].value; query = params->params[i].value;
} }
@ -19,6 +22,9 @@ int settings_save_handler(UrlParams *params) {
if (strlen(theme) > 0) { if (strlen(theme) > 0) {
set_cookie("theme", theme, "Fri, 31 Dec 2038 23:59:59 GMT", "/", false, false); set_cookie("theme", theme, "Fri, 31 Dec 2038 23:59:59 GMT", "/", false, false);
} }
if (strlen(locale) > 0) {
set_cookie("locale", locale, "Fri, 31 Dec 2038 23:59:59 GMT", "/", false, false);
}
char redirect_url[512]; char redirect_url[512];
snprintf(redirect_url, sizeof(redirect_url), "/settings?q=%s", query); snprintf(redirect_url, sizeof(redirect_url), "/settings?q=%s", query);

View file

@ -23,3 +23,12 @@ char *get_theme(const char *default_theme) {
free(cookie); free(cookie);
return strdup(default_theme); return strdup(default_theme);
} }
char *get_locale(const char *default_locale) {
char *cookie = get_cookie("locale");
if (cookie && beaker_get_locale_meta(cookie) != NULL) {
return cookie;
}
free(cookie);
return strdup(default_locale);
}

View file

@ -1,7 +1,10 @@
#ifndef UTILITY_H #ifndef UTILITY_H
#define UTILITY_H #define UTILITY_H
#include <beaker.h>
int hex_to_int(char c); int hex_to_int(char c);
char *get_theme(const char *default_theme); char *get_theme(const char *default_theme);
char *get_locale(const char *default_locale);
#endif #endif

View file

@ -31,7 +31,7 @@ html {
body { body {
background-color:var(--bg-main); background-color:var(--bg-main);
background-image:radial-gradient(circle at top right, var(--bg-card) 0%, var(--bg-main) 100%); background-image:radial-gradient(circle at top end, var(--bg-card) 0%, var(--bg-main) 100%);
background-attachment:fixed; background-attachment:fixed;
color:var(--text-primary); color:var(--text-primary);
font-family:system-ui,-apple-system,sans-serif; font-family:system-ui,-apple-system,sans-serif;
@ -124,7 +124,7 @@ img[src=""] {
.home-settings-btn { .home-settings-btn {
position:fixed; position:fixed;
top:27px; top:27px;
right:60px; inset-inline-end:60px;
width:24px; width:24px;
height:24px; height:24px;
background-color:var(--text-primary); background-color:var(--text-primary);
@ -139,7 +139,7 @@ img[src=""] {
width:24px; width:24px;
height:24px; height:24px;
flex-shrink:0; flex-shrink:0;
margin-left:auto; margin-inline-start:auto;
margin-top:3px; margin-top:3px;
background-color:var(--text-secondary); background-color:var(--text-secondary);
-webkit-mask-image:url('/static/settings.svg'); -webkit-mask-image:url('/static/settings.svg');
@ -154,13 +154,14 @@ img[src=""] {
} }
.nav-settings-link { .nav-settings-link {
display:none; display:none;
margin-left:auto; margin-inline-start:auto;
} }
header { header {
display:flex; display:flex;
align-items:center; align-items:center;
gap:20px; gap:20px;
padding:15px 60px; padding-block:15px;
padding-inline:60px;
border-bottom:1px solid var(--border); border-bottom:1px solid var(--border);
background:var(--bg-main); background:var(--bg-main);
width:100%; width:100%;
@ -204,7 +205,7 @@ header .logo-link:hover {
box-shadow:0 0 0 4px var(--accent-glow); box-shadow:0 0 0 4px var(--accent-glow);
} }
.nav-tabs { .nav-tabs {
padding:0 60px; padding-inline:60px;
border-bottom:1px solid var(--border); border-bottom:1px solid var(--border);
background:var(--bg-main); background:var(--bg-main);
width:100%; width:100%;
@ -231,7 +232,7 @@ header .logo-link:hover {
border-bottom-color:var(--accent); border-bottom-color:var(--accent);
} }
.nav-right { .nav-right {
margin-left:auto; margin-inline-start:auto;
} }
.image-results-container { .image-results-container {
padding:30px 60px; padding:30px 60px;
@ -335,7 +336,8 @@ header .logo-link:hover {
display:grid; display:grid;
grid-template-columns:140px minmax(0,700px) 450px; grid-template-columns:140px minmax(0,700px) 450px;
gap:60px; gap:60px;
padding:30px 60px; padding-block:30px;
padding-inline:60px;
} }
.result-header { .result-header {
display: flex; display: flex;
@ -351,7 +353,7 @@ header .logo-link:hover {
background-size: cover; background-size: cover;
background-position: center; background-position: center;
position: absolute; position: absolute;
left: -24px; inset-inline-start: -24px;
} }
.url { .url {
color: var(--text-secondary); color: var(--text-secondary);
@ -365,7 +367,7 @@ header .logo-link:hover {
.result-favicon { .result-favicon {
width: 14px; width: 14px;
height: 14px; height: 14px;
left: -20px; inset-inline-start: -20px;
} }
} }
@ -373,7 +375,7 @@ header .logo-link:hover {
.result-favicon { .result-favicon {
width: 12px; width: 12px;
height: 12px; height: 12px;
left: -16px; inset-inline-start: -16px;
} }
} }
.results-container { .results-container {
@ -540,7 +542,8 @@ header .logo-link:hover {
@media (max-width:1200px) { @media (max-width:1200px) {
.content-layout { .content-layout {
grid-template-columns:1fr; grid-template-columns:1fr;
padding:20px 30px; padding-block:20px;
padding-inline:30px;
gap:20px; gap:20px;
} }
header { header {
@ -551,7 +554,8 @@ header .logo-link:hover {
max-width:100%; max-width:100%;
} }
.settings-layout { .settings-layout {
padding:20px 30px; padding-block:20px;
padding-inline:30px;
display:flex; display:flex;
justify-content:center; justify-content:center;
} }
@ -559,10 +563,11 @@ header .logo-link:hover {
order:-1; order:-1;
} }
.nav-tabs,.image-results-container { .nav-tabs,.image-results-container {
padding:0 30px; padding-inline:30px;
} }
header { header {
padding:15px 30px; padding-block:15px;
padding-inline:30px;
} }
} }
@ -576,7 +581,8 @@ header .logo-link:hover {
header { header {
flex-direction:column; flex-direction:column;
gap:12px; gap:12px;
padding:12px 16px; padding-block:12px;
padding-inline:16px;
text-align:center; text-align:center;
} }
h1 { h1 {
@ -592,7 +598,7 @@ header .logo-link:hover {
.nav-tabs { .nav-tabs {
overflow-x:auto; overflow-x:auto;
-webkit-overflow-scrolling:touch; -webkit-overflow-scrolling:touch;
padding:0 16px; padding-inline:16px;
} }
.nav-container { .nav-container {
gap:24px; gap:24px;
@ -603,7 +609,9 @@ header .logo-link:hover {
font-size:0.95rem; font-size:0.95rem;
} }
.content-layout { .content-layout {
padding:16px 16px 16px 40px; padding-inline-start:40px;
padding-inline-end:16px;
padding-block:16px;
gap:16px; gap:16px;
} }
.result { .result {
@ -647,7 +655,7 @@ header .logo-link:hover {
max-width:200px; max-width:200px;
} }
.image-results-container { .image-results-container {
padding:16px; padding-inline:16px;
} }
.pagination { .pagination {
flex-wrap:wrap; flex-wrap:wrap;
@ -692,13 +700,16 @@ header .logo-link:hover {
@media (max-width:600px) { @media (max-width:600px) {
.content-layout { .content-layout {
padding:16px 16px 16px 28px; padding-inline-start:28px;
padding-inline-end:16px;
padding-block:16px;
} }
.settings-layout { .settings-layout {
padding:12px 0; padding:0;
} }
header { header {
padding:12px 12px; padding-inline:12px;
padding-block:12px;
} }
.search-box { .search-box {
font-size:0.95rem; font-size:0.95rem;
@ -735,7 +746,9 @@ header .logo-link:hover {
} }
.settings-layout { .settings-layout {
padding:30px 60px 30px 260px; padding-block: 30px;
padding-inline-start: 260px;
padding-inline-end: 60px;
} }
.settings-container { .settings-container {
@ -875,3 +888,42 @@ header .logo-link:hover {
text-align:center; text-align:center;
} }
} }
[dir="rtl"] {
direction: rtl;
unicode-bidi: embed;
}
[dir="rtl"] header {
flex-direction: row-reverse;
direction: ltr;
}
[dir="rtl"] .nav-container {
flex-direction: row-reverse;
direction: ltr;
}
[dir="rtl"] .search-box {
text-align: right;
direction: rtl;
}
[dir="rtl"] .url {
text-align: end;
}
[dir="rtl"] .nav-settings-icon {
margin-inline-start: unset;
margin-inline-end: auto;
}
[dir="rtl"] .settings-actions .btn-primary {
margin-inline-end: auto;
}
@media (max-width: 768px) {
[dir="rtl"] header {
flex-direction: column;
}
}

View file

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="{{__locale_id}}" dir="{{__locale_direction}}">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@ -24,21 +24,21 @@
</h1> </h1>
<form action="/search" class="home-search-form"> <form action="/search" class="home-search-form">
<div class="search-input-wrapper"> <div class="search-input-wrapper">
<input name="q" type="text" class="search-box" placeholder="Search the web..." <input name="q" type="text" class="search-box" placeholder="{{l("search_placeholder")}}"
autofocus autocomplete="off"> autofocus autocomplete="off">
</div> </div>
<div class="buttons"> <div class="buttons">
<button type="submit" class="btn-primary"> <button type="submit" class="btn-primary">
Search {{l("search_button")}}
</button> </button>
<button type="submit" name="btnI" value="1" class="btn-secondary"> <button type="submit" name="btnI" value="1" class="btn-secondary">
Surprise me {{l("surprise_me_button")}}
</button> </button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<a href="/settings" class="home-settings-btn" title="Settings"></a> <a href="/settings" class="home-settings-btn" title="{{l("settings_tab")}}"></a>
</body> </body>
</html> </html>

View file

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="{{__locale_id}}" dir="{{__locale_direction}}">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@ -19,21 +19,21 @@
Omni<span>Search</span> Omni<span>Search</span>
</h1></a> </h1></a>
<form action="/images" method="GET" class="search-form"> <form action="/images" method="GET" class="search-form">
<input name="q" autocomplete="off"="text" class="search-box" placeholder="Search for images..." <input name="q" autocomplete="off"="text" class="search-box" placeholder="{{l("search_placeholder")}}"
value="{{query}}"> value="{{query}}">
</form> </form>
<a href="/settings?q={{query}}" class="nav-settings-icon" title="Settings"></a> <a href="/settings?q={{query}}" class="nav-settings-icon" title="{{l("settings_tab")}}"></a>
</header> </header>
<nav class="nav-tabs"> <nav class="nav-tabs">
<div class="nav-container"> <div class="nav-container">
<a href="/search?q={{query}}"> <a href="/search?q={{query}}">
All {{l("all_tab")}}
</a> </a>
<a href="/images?q={{query}}" class="active"> <a href="/images?q={{query}}" class="active">
Images {{l("images_tab")}}
</a> </a>
<a href="/settings?q={{query}}" class="nav-settings-link"> <a href="/settings?q={{query}}" class="nav-settings-link">
Settings {{l("settings_tab")}}
</a> </a>
</div> </div>
</nav> </nav>
@ -46,10 +46,10 @@
<div class="image-overlay"> <div class="image-overlay">
<div class="overlay-buttons"> <div class="overlay-buttons">
<a href="{{img[3]}}" target="_blank" class="overlay-btn primary"> <a href="{{img[3]}}" target="_blank" class="overlay-btn primary">
View Image {{l("view_image")}}
</a> </a>
<a href="{{img[2]}}" target="_blank" class="overlay-btn secondary"> <a href="{{img[2]}}" target="_blank" class="overlay-btn secondary">
Visit Site {{l("visit_site")}}
</a> </a>
</div> </div>
</div> </div>

View file

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="{{__locale_id}}" dir="{{__locale_direction}}">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@ -23,21 +23,21 @@
</h1></a> </h1></a>
<form action="/search" method="GET" class="search-form"> <form action="/search" method="GET" class="search-form">
<input name="engine" type="hidden" value="{{selected_engine}}"> <input name="engine" type="hidden" value="{{selected_engine}}">
<input name="q" type="text" class="search-box" autocomplete="off" placeholder="Search the web..." <input name="q" type="text" class="search-box" autocomplete="off" placeholder="{{l("search_placeholder")}}"
value="{{query}}"> value="{{query}}">
</form> </form>
<a href="/settings?q={{query}}" class="nav-settings-icon" title="Settings"></a> <a href="/settings?q={{query}}" class="nav-settings-icon" title="{{l("settings_tab")}}"></a>
</header> </header>
<nav class="nav-tabs"> <nav class="nav-tabs">
<div class="nav-container"> <div class="nav-container">
<a href="{{search_href}}" class="active"> <a href="{{search_href}}" class="active">
All {{l("all_tab")}}
</a> </a>
<a href="/images?q={{query}}"> <a href="/images?q={{query}}">
Images {{l("images_tab")}}
</a> </a>
<a href="/settings?q={{query}}" class="nav-settings-link"> <a href="/settings?q={{query}}" class="nav-settings-link">
Settings {{l("settings_tab")}}
</a> </a>
</div> </div>
</nav> </nav>
@ -88,7 +88,7 @@
{{result[3]}} {{result[3]}}
</p> </p>
<span> <span>
<a class="cached" href="https://web.archive.org/web/{{result[0]|safe}}">View Cached</a> <a class="cached" href="https://web.archive.org/web/{{result[0]|safe}}">{{l("view_cached")}}</a>
</span> </span>
</div> </div>
{{endfor}} {{endfor}}
@ -117,9 +117,9 @@
<div class="infobox-content"> <p class="infobox-summary"> <div class="infobox-content"> <p class="infobox-summary">
{{info[2]|safe}} {{info[2]|safe}}
</p> </p>
<a class="read-more" href="{{info[3]}}"> <a class="read-more" href="{{info[3]}}">
Read More {{l("read_more")}}
</a> </a>
</div> </div>
</div> </div>
</section> </section>

View file

@ -1,11 +1,11 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="{{__locale_id}}" dir="{{__locale_direction}}">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
<title> <title>
OmniSearch - Settings OmniSearch - {{l("settings_title")}}
</title> </title>
<link rel="stylesheet" href="static/main.css"> <link rel="stylesheet" href="static/main.css">
{{if theme == "light"}}<link rel="stylesheet" href="static/theme-light.css">{{endif}} {{if theme == "light"}}<link rel="stylesheet" href="static/theme-light.css">{{endif}}
@ -23,27 +23,27 @@
</h1></a> </h1></a>
{{if query != ""}} {{if query != ""}}
<form action="/search" method="GET" class="search-form"> <form action="/search" method="GET" class="search-form">
<input name="q" type="text" class="search-box" autocomplete="off" placeholder="Search the web..." <input name="q" type="text" class="search-box" autocomplete="off" placeholder="{{l("search_placeholder")}}"
value="{{query}}"> value="{{query}}">
</form> </form>
{{endif}} {{endif}}
{{if query != ""}} {{if query != ""}}
<a href="/search?q={{query}}" class="nav-settings-icon active" title="Settings"></a> <a href="/search?q={{query}}" class="nav-settings-icon active" title="{{l("settings_tab")}}"></a>
{{else}} {{else}}
<a href="/" class="nav-settings-icon active" title="Settings"></a> <a href="/" class="nav-settings-icon active" title="{{l("settings_tab")}}"></a>
{{endif}} {{endif}}
</header> </header>
{{if query != ""}} {{if query != ""}}
<nav class="nav-tabs"> <nav class="nav-tabs">
<div class="nav-container"> <div class="nav-container">
<a href="/search?q={{query}}"> <a href="/search?q={{query}}">
All {{l("all_tab")}}
</a> </a>
<a href="/images?q={{query}}"> <a href="/images?q={{query}}">
Images {{l("images_tab")}}
</a> </a>
<a href="/settings" class="active nav-settings-link"> <a href="/settings" class="active nav-settings-link">
Settings {{l("settings_tab")}}
</a> </a>
</div> </div>
</nav> </nav>
@ -53,23 +53,35 @@
<form action="/save_settings" method="GET"> <form action="/save_settings" method="GET">
<input type="hidden" name="q" value="{{query}}"> <input type="hidden" name="q" value="{{query}}">
<section class="settings-section"> <section class="settings-section">
<h3 class="settings-section-title">Theme</h3> <h3 class="settings-section-title">{{l("theme_label")}}</h3>
<p class="settings-section-desc">Choose your preferred colour scheme.</p> <p class="settings-section-desc">{{l("theme_desc")}}</p>
<div class="settings-field"> <div class="settings-field">
<label class="settings-label" for="theme">Appearance</label> <label class="settings-label" for="theme">{{l("theme_label")}}</label>
<select id="theme" name="theme" class="settings-select"> <select id="theme" name="theme" class="settings-select">
<option value="system" {{if theme == "system"}}selected{{endif}}>System</option> <option value="system" {{if theme == "system"}}selected{{endif}}>{{l("theme_system")}}</option>
<option value="light" {{if theme == "light"}}selected{{endif}}>Light</option> <option value="light" {{if theme == "light"}}selected{{endif}}>{{l("theme_light")}}</option>
<option value="dark" {{if theme == "dark"}}selected{{endif}}>Dark</option> <option value="dark" {{if theme == "dark"}}selected{{endif}}>{{l("theme_dark")}}</option>
</select>
</div>
</section>
<section class="settings-section">
<h3 class="settings-section-title">{{l("language_label")}}</h3>
<p class="settings-section-desc">{{l("language_desc")}}</p>
<div class="settings-field">
<label class="settings-label" for="locale">{{l("display_language_label")}}</label>
<select id="locale" name="locale" class="settings-select">
{{for loc in locales}}
<option value="{{loc[0]}}" {{if __locale_id == loc[0]}}selected{{endif}}>{{loc[1]}}</option>
{{endfor}}
</select> </select>
</div> </div>
</section> </section>
<div class="settings-actions"> <div class="settings-actions">
<button type="submit" class="btn-primary">Save Settings</button> <button type="submit" class="btn-primary">{{l("save_settings_button")}}</button>
</div> </div>
</form> </form>
</main> </main>
</div> </div>
</body> </body>
</html> </html>