jekyll-theme-zer0 1.25.0 → 1.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +201 -0
- data/README.md +11 -28
- data/_data/authors.yml +4 -3
- data/_data/backlog.yml +54 -1
- data/_data/features.yml +101 -61
- data/_data/feedback_types.yml +148 -0
- data/_data/i18n/languages.yml +36 -0
- data/_data/navigation/README.md +1 -1
- data/_data/theme-manifest.yml +0 -2
- data/_data/ui-text.yml +36 -246
- data/_includes/README.md +4 -0
- data/_includes/components/README.md +1 -0
- data/_includes/components/author-avatar-url.html +4 -2
- data/_includes/components/env-switcher.html +3 -1
- data/_includes/components/language-toggle.html +81 -0
- data/_includes/components/page-feedback.html +252 -0
- data/_includes/components/search-modal.html +2 -2
- data/_includes/components/shortcuts-modal.html +1 -1
- data/_includes/components/translation-notice.html +27 -0
- data/_includes/content/intro.html +35 -76
- data/_includes/core/console-capture.html +89 -0
- data/_includes/core/footer.html +9 -4
- data/_includes/core/head.html +13 -0
- data/_includes/core/header.html +9 -4
- data/_includes/core/hreflang.html +33 -0
- data/_includes/core/i18n.html +36 -0
- data/_includes/navigation/breadcrumbs.html +1 -1
- data/_includes/navigation/navbar.html +5 -4
- data/_includes/navigation/sidebar-right.html +3 -2
- data/_includes/navigation/unified-drawer.html +1 -1
- data/_layouts/article.html +7 -1
- data/_layouts/default.html +5 -3
- data/_layouts/news.html +4 -2
- data/_layouts/root.html +17 -8
- data/_layouts/section.html +4 -2
- data/_sass/components/_page-feedback.scss +276 -0
- data/_sass/core/_docs-layout.scss +2 -78
- data/_sass/core/_obsidian.scss +9 -1
- data/_sass/layouts/_navbar-extras.scss +6 -1
- data/_sass/tokens/_layers.scss +4 -0
- data/assets/css/main.scss +1 -0
- data/assets/js/obsidian-graph.js +5 -1
- data/assets/js/page-feedback.js +660 -0
- data/scripts/README.md +20 -26
- data/scripts/bin/audit-consumer +1 -1
- data/scripts/bin/manifest +0 -1
- data/scripts/bin/sync-plugins +0 -1
- data/scripts/dev/rasterize-svg.js +65 -0
- data/scripts/features/generate-preview-images +49 -1390
- data/scripts/features/install-preview-generator +55 -33
- data/scripts/install/README.md +9 -20
- data/scripts/install/ai/prompts/wizard.system.md +8 -17
- data/scripts/lib/README.md +1 -5
- data/scripts/lib/install/deploy/README.md +3 -9
- data/scripts/lib/preview_generator.py +2261 -1341
- data/scripts/translate.rb +1114 -0
- metadata +14 -4
- data/_data/prompts.yml +0 -302
- data/_plugins/preview_image_generator.rb +0 -351
data/_data/ui-text.yml
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
# User interface text and labels
|
|
2
|
-
# Used by:
|
|
3
|
-
#
|
|
1
|
+
# User interface text and labels — ENGLISH SOURCE OF TRUTH
|
|
2
|
+
# Used by: _includes/core/i18n.html (and through it, layouts/includes)
|
|
3
|
+
#
|
|
4
|
+
# Multilingual model (ZER0-078): this file holds ONLY the English strings.
|
|
5
|
+
# Alternate languages are NOT hand-maintained here anymore — they are
|
|
6
|
+
# machine-generated into _data/i18n/<lang>.yml by scripts/translate.rb
|
|
7
|
+
# (Claude Code OAuth) via .github/workflows/translate.yml. To change a
|
|
8
|
+
# translated label, edit the English string here and let the translate
|
|
9
|
+
# workflow regenerate the alternates. Languages are configured under
|
|
10
|
+
# `translation.languages` in _config.yml.
|
|
11
|
+
#
|
|
12
|
+
# (The former hand-written es/fr/de/ar blocks were removed in favor of the
|
|
13
|
+
# generated pipeline — see docs/systems/multilingual-translation.md.)
|
|
4
14
|
|
|
5
15
|
# English (default)
|
|
6
16
|
# -----------------
|
|
@@ -8,6 +18,7 @@ en: &DEFAULT_EN
|
|
|
8
18
|
skip_links : "Skip links"
|
|
9
19
|
skip_primary_nav : "Skip to primary navigation"
|
|
10
20
|
skip_content : "Skip to content"
|
|
21
|
+
skip_main_content : "Skip to main content"
|
|
11
22
|
skip_footer : "Skip to footer"
|
|
12
23
|
page : "Page"
|
|
13
24
|
pagination_previous : "Previous"
|
|
@@ -79,7 +90,28 @@ en: &DEFAULT_EN
|
|
|
79
90
|
shortcuts_toggle_sidebar : "Toggle sidebar"
|
|
80
91
|
shortcuts_toggle_toc : "Toggle table of contents"
|
|
81
92
|
shortcuts_show_help : "Show this help"
|
|
82
|
-
|
|
93
|
+
# Header / navbar chrome (ZER0-078)
|
|
94
|
+
home_label : "Home"
|
|
95
|
+
main_nav_label : "Main Navigation"
|
|
96
|
+
search_button_label : "Search"
|
|
97
|
+
settings_button_label : "Settings"
|
|
98
|
+
toc_title : "Table of Contents"
|
|
99
|
+
# Article intro / metadata chrome (ZER0-078)
|
|
100
|
+
published_label : "Published"
|
|
101
|
+
updated_label : "Updated"
|
|
102
|
+
reading_time_label : "Estimated reading time:"
|
|
103
|
+
reading_time_minute : "minute"
|
|
104
|
+
share_label : "Share"
|
|
105
|
+
copy_link_label : "Copy Link"
|
|
106
|
+
improve_label : "Improve"
|
|
107
|
+
edit_on_github : "Edit on GitHub"
|
|
108
|
+
# Language toggle & machine translation (ZER0-078)
|
|
109
|
+
lang_toggle_label : "Language"
|
|
110
|
+
lang_toggle_aria : "Change language"
|
|
111
|
+
lang_not_available : "Not yet translated"
|
|
112
|
+
machine_translation_notice : "This page was translated automatically from the English original."
|
|
113
|
+
machine_translation_view_source : "View the original"
|
|
114
|
+
|
|
83
115
|
en-US:
|
|
84
116
|
<<: *DEFAULT_EN
|
|
85
117
|
en-CA:
|
|
@@ -88,245 +120,3 @@ en-GB:
|
|
|
88
120
|
<<: *DEFAULT_EN
|
|
89
121
|
en-AU:
|
|
90
122
|
<<: *DEFAULT_EN
|
|
91
|
-
|
|
92
|
-
# Spanish
|
|
93
|
-
# -------
|
|
94
|
-
es: &DEFAULT_ES
|
|
95
|
-
skip_links : "Saltar enlaces"
|
|
96
|
-
skip_primary_nav : "Saltar a navegación principal"
|
|
97
|
-
skip_content : "Saltar a contenido"
|
|
98
|
-
skip_footer : "Saltar a pie"
|
|
99
|
-
page : "Página"
|
|
100
|
-
pagination_previous : "Anterior"
|
|
101
|
-
pagination_next : "Siguiente"
|
|
102
|
-
breadcrumb_home_label : "Inicio"
|
|
103
|
-
breadcrumb_separator : "/"
|
|
104
|
-
menu_label : "Alternar menú"
|
|
105
|
-
search_label : "Alternar búsqueda"
|
|
106
|
-
toc_label : "En esta página"
|
|
107
|
-
ext_link_label : "Enlace directo"
|
|
108
|
-
less_than : "menos de"
|
|
109
|
-
minute_read : "minuto(s) de lectura"
|
|
110
|
-
share_on_label : "Compartir en"
|
|
111
|
-
meta_label :
|
|
112
|
-
tags_label : "Etiquetas:"
|
|
113
|
-
categories_label : "Categorías:"
|
|
114
|
-
date_label : "Actualizado:"
|
|
115
|
-
author_label : "Autor:"
|
|
116
|
-
comments_label : "Deja un comentario"
|
|
117
|
-
comments_title : "Comentarios"
|
|
118
|
-
more_label : "Ver más"
|
|
119
|
-
related_label : "Puede que también te interese"
|
|
120
|
-
follow_label : "Seguir:"
|
|
121
|
-
feed_label : "Feed"
|
|
122
|
-
powered_by : "Funciona con"
|
|
123
|
-
website_label : "Sitio web"
|
|
124
|
-
email_label : "Correo electrónico"
|
|
125
|
-
recent_posts : "Entradas recientes"
|
|
126
|
-
undefined_wpm : "El parámetro words_per_minute (palabras por minuto) no está definido en _config.yml"
|
|
127
|
-
comment_form_info : "Tu dirección de correo electrónico no se publicará. Los campos obligatorios están marcados"
|
|
128
|
-
comment_form_comment_label : "Comentario"
|
|
129
|
-
comment_form_md_info : "Puedes utilizar Markdown"
|
|
130
|
-
comment_form_name_label : "Nombre"
|
|
131
|
-
comment_form_email_label : "Dirección de correo electrónico"
|
|
132
|
-
comment_form_website_label : "Sitio web (opcional)"
|
|
133
|
-
comment_btn_submit : "Enviar comentario"
|
|
134
|
-
comment_btn_submitted : "Enviado"
|
|
135
|
-
comment_success_msg : "¡Gracias por tu comentario! Se mostrará en el sitio una vez que haya sido aprobado."
|
|
136
|
-
comment_error_msg : "Lo sentimos, ha habido un error con tu envío. Por favor, asegúrate de que todos los campos obligatorios se han completado e inténtalo de nuevo."
|
|
137
|
-
loading_label : "Cargando..."
|
|
138
|
-
search_label_text : "Introduce tu término de búsqueda..."
|
|
139
|
-
search_placeholder_text : "Introduce tu término de búsqueda..."
|
|
140
|
-
search_no_results : "Sin resultados"
|
|
141
|
-
results_found : "Resultado(s) encontrado(s)"
|
|
142
|
-
back_to_top : "Volver arriba"
|
|
143
|
-
# Statistics labels
|
|
144
|
-
stats_total_posts : "Total de Publicaciones"
|
|
145
|
-
stats_total_pages : "Total de Páginas"
|
|
146
|
-
stats_categories : "Categorías"
|
|
147
|
-
stats_tags : "Etiquetas"
|
|
148
|
-
stats_last_updated : "Última Actualización"
|
|
149
|
-
stats_overview : "Resumen"
|
|
150
|
-
stats_content_breakdown : "Desglose de Contenido"
|
|
151
|
-
# Theme-specific labels
|
|
152
|
-
theme_toggle : "Cambiar tema"
|
|
153
|
-
dark_mode : "Modo oscuro"
|
|
154
|
-
light_mode : "Modo claro"
|
|
155
|
-
view_source : "Ver código fuente"
|
|
156
|
-
edit_page : "Editar esta página"
|
|
157
|
-
report_issue : "Reportar un problema"
|
|
158
|
-
|
|
159
|
-
es-ES:
|
|
160
|
-
<<: *DEFAULT_ES
|
|
161
|
-
es-MX:
|
|
162
|
-
<<: *DEFAULT_ES
|
|
163
|
-
|
|
164
|
-
# French
|
|
165
|
-
# ------
|
|
166
|
-
fr: &DEFAULT_FR
|
|
167
|
-
skip_links : "Liens de saut"
|
|
168
|
-
skip_primary_nav : "Aller à la navigation principale"
|
|
169
|
-
skip_content : "Aller au contenu"
|
|
170
|
-
skip_footer : "Aller au pied de page"
|
|
171
|
-
page : "Page"
|
|
172
|
-
pagination_previous : "Précédent"
|
|
173
|
-
pagination_next : "Suivant"
|
|
174
|
-
breadcrumb_home_label : "Accueil"
|
|
175
|
-
breadcrumb_separator : "/"
|
|
176
|
-
menu_label : "Basculer le menu"
|
|
177
|
-
search_label : "Basculer la recherche"
|
|
178
|
-
toc_label : "Sur cette page"
|
|
179
|
-
ext_link_label : "Lien direct"
|
|
180
|
-
less_than : "moins de"
|
|
181
|
-
minute_read : "minute(s) de lecture"
|
|
182
|
-
share_on_label : "Partager sur"
|
|
183
|
-
meta_label :
|
|
184
|
-
tags_label : "Tags :"
|
|
185
|
-
categories_label : "Catégories :"
|
|
186
|
-
date_label : "Mis à jour :"
|
|
187
|
-
author_label : "Auteur :"
|
|
188
|
-
comments_label : "Laisser un commentaire"
|
|
189
|
-
comments_title : "Commentaires"
|
|
190
|
-
more_label : "En savoir plus"
|
|
191
|
-
related_label : "Vous pourriez aussi aimer"
|
|
192
|
-
follow_label : "Suivre :"
|
|
193
|
-
feed_label : "Flux"
|
|
194
|
-
powered_by : "Propulsé par"
|
|
195
|
-
website_label : "Site web"
|
|
196
|
-
email_label : "Email"
|
|
197
|
-
recent_posts : "Articles récents"
|
|
198
|
-
back_to_top : "Retour en haut"
|
|
199
|
-
# Statistics labels
|
|
200
|
-
stats_total_posts : "Total des Publications"
|
|
201
|
-
stats_total_pages : "Total des Pages"
|
|
202
|
-
stats_categories : "Catégories"
|
|
203
|
-
stats_tags : "Tags"
|
|
204
|
-
stats_last_updated : "Dernière Mise à Jour"
|
|
205
|
-
stats_overview : "Aperçu"
|
|
206
|
-
stats_content_breakdown : "Répartition du Contenu"
|
|
207
|
-
# Theme-specific labels
|
|
208
|
-
theme_toggle : "Changer de thème"
|
|
209
|
-
dark_mode : "Mode sombre"
|
|
210
|
-
light_mode : "Mode clair"
|
|
211
|
-
view_source : "Voir la source"
|
|
212
|
-
edit_page : "Modifier cette page"
|
|
213
|
-
report_issue : "Signaler un problème"
|
|
214
|
-
|
|
215
|
-
fr-FR:
|
|
216
|
-
<<: *DEFAULT_FR
|
|
217
|
-
fr-CA:
|
|
218
|
-
<<: *DEFAULT_FR
|
|
219
|
-
|
|
220
|
-
# German
|
|
221
|
-
# ------
|
|
222
|
-
de: &DEFAULT_DE
|
|
223
|
-
skip_links : "Links überspringen"
|
|
224
|
-
skip_primary_nav : "Zur Hauptnavigation springen"
|
|
225
|
-
skip_content : "Zum Inhalt springen"
|
|
226
|
-
skip_footer : "Zur Fußzeile springen"
|
|
227
|
-
page : "Seite"
|
|
228
|
-
pagination_previous : "Zurück"
|
|
229
|
-
pagination_next : "Weiter"
|
|
230
|
-
breadcrumb_home_label : "Startseite"
|
|
231
|
-
breadcrumb_separator : "/"
|
|
232
|
-
menu_label : "Menü umschalten"
|
|
233
|
-
search_label : "Suche umschalten"
|
|
234
|
-
toc_label : "Auf dieser Seite"
|
|
235
|
-
ext_link_label : "Direktlink"
|
|
236
|
-
less_than : "weniger als"
|
|
237
|
-
minute_read : "Minute(n) Lesezeit"
|
|
238
|
-
share_on_label : "Teilen auf"
|
|
239
|
-
meta_label :
|
|
240
|
-
tags_label : "Tags:"
|
|
241
|
-
categories_label : "Kategorien:"
|
|
242
|
-
date_label : "Aktualisiert:"
|
|
243
|
-
author_label : "Autor:"
|
|
244
|
-
comments_label : "Einen Kommentar hinterlassen"
|
|
245
|
-
comments_title : "Kommentare"
|
|
246
|
-
more_label : "Mehr erfahren"
|
|
247
|
-
related_label : "Das könnte Sie auch interessieren"
|
|
248
|
-
follow_label : "Folgen:"
|
|
249
|
-
feed_label : "Feed"
|
|
250
|
-
powered_by : "Betrieben mit"
|
|
251
|
-
website_label : "Webseite"
|
|
252
|
-
email_label : "E-Mail"
|
|
253
|
-
recent_posts : "Neueste Beiträge"
|
|
254
|
-
back_to_top : "Nach oben"
|
|
255
|
-
# Statistics labels
|
|
256
|
-
stats_total_posts : "Gesamte Beiträge"
|
|
257
|
-
stats_total_pages : "Gesamte Seiten"
|
|
258
|
-
stats_categories : "Kategorien"
|
|
259
|
-
stats_tags : "Tags"
|
|
260
|
-
stats_last_updated : "Zuletzt Aktualisiert"
|
|
261
|
-
stats_overview : "Übersicht"
|
|
262
|
-
stats_content_breakdown : "Inhaltsaufschlüsselung"
|
|
263
|
-
# Theme-specific labels
|
|
264
|
-
theme_toggle : "Thema wechseln"
|
|
265
|
-
dark_mode : "Dunkler Modus"
|
|
266
|
-
light_mode : "Heller Modus"
|
|
267
|
-
view_source : "Quelle ansehen"
|
|
268
|
-
edit_page : "Diese Seite bearbeiten"
|
|
269
|
-
report_issue : "Ein Problem melden"
|
|
270
|
-
|
|
271
|
-
de-DE:
|
|
272
|
-
<<: *DEFAULT_DE
|
|
273
|
-
de-AT:
|
|
274
|
-
<<: *DEFAULT_DE
|
|
275
|
-
de-CH:
|
|
276
|
-
<<: *DEFAULT_DE
|
|
277
|
-
|
|
278
|
-
# Arabic
|
|
279
|
-
# ------
|
|
280
|
-
ar: &DEFAULT_AR
|
|
281
|
-
skip_links : "تخطي الروابط"
|
|
282
|
-
skip_primary_nav : "انتقل إلى التنقل الرئيسي"
|
|
283
|
-
skip_content : "انتقل إلى المحتوى"
|
|
284
|
-
skip_footer : "انتقل إلى التذييل"
|
|
285
|
-
page : "صفحة"
|
|
286
|
-
pagination_previous : "السابق"
|
|
287
|
-
pagination_next : "التالي"
|
|
288
|
-
breadcrumb_home_label : "الرئيسية"
|
|
289
|
-
breadcrumb_separator : "/"
|
|
290
|
-
menu_label : "تبديل القائمة"
|
|
291
|
-
search_label : "تبديل البحث"
|
|
292
|
-
toc_label : "في هذه الصفحة"
|
|
293
|
-
ext_link_label : "رابط مباشر"
|
|
294
|
-
less_than : "أقل من"
|
|
295
|
-
minute_read : "دقيقة للقراءة"
|
|
296
|
-
share_on_label : "مشاركة على"
|
|
297
|
-
meta_label :
|
|
298
|
-
tags_label : "الوسوم:"
|
|
299
|
-
categories_label : "الأقسام:"
|
|
300
|
-
date_label : "تاريخ التحديث:"
|
|
301
|
-
author_label : "الكاتب:"
|
|
302
|
-
comments_label : "اترك تعليقاً"
|
|
303
|
-
comments_title : "التعليقات"
|
|
304
|
-
more_label : "اقرأ المزيد"
|
|
305
|
-
related_label : "قد يعجبك أيضاً"
|
|
306
|
-
follow_label : "تابعنا:"
|
|
307
|
-
feed_label : "الخلاصة"
|
|
308
|
-
powered_by : "يعمل بواسطة"
|
|
309
|
-
website_label : "الموقع"
|
|
310
|
-
email_label : "البريد الإلكتروني"
|
|
311
|
-
recent_posts : "المنشورات الأخيرة"
|
|
312
|
-
back_to_top : "العودة للأعلى"
|
|
313
|
-
# Statistics labels
|
|
314
|
-
stats_total_posts : "إجمالي المنشورات"
|
|
315
|
-
stats_total_pages : "إجمالي الصفحات"
|
|
316
|
-
stats_categories : "الأقسام"
|
|
317
|
-
stats_tags : "الوسوم"
|
|
318
|
-
stats_last_updated : "آخر تحديث"
|
|
319
|
-
stats_overview : "نظرة عامة"
|
|
320
|
-
stats_content_breakdown : "تفصيل المحتوى"
|
|
321
|
-
# Theme-specific labels
|
|
322
|
-
theme_toggle : "تبديل المظهر"
|
|
323
|
-
dark_mode : "الوضع الداكن"
|
|
324
|
-
light_mode : "الوضع الفاتح"
|
|
325
|
-
view_source : "عرض المصدر"
|
|
326
|
-
edit_page : "تحرير هذه الصفحة"
|
|
327
|
-
report_issue : "الإبلاغ عن مشكلة"
|
|
328
|
-
|
|
329
|
-
ar-SA:
|
|
330
|
-
<<: *DEFAULT_AR
|
|
331
|
-
ar-EG:
|
|
332
|
-
<<: *DEFAULT_AR
|
data/_includes/README.md
CHANGED
|
@@ -12,6 +12,8 @@ Essential layout components that form the foundation of the site:
|
|
|
12
12
|
- `header.html` - Main site header with navigation
|
|
13
13
|
- `footer.html` - Site footer (if exists)
|
|
14
14
|
- `branding.html` - Site branding and title display
|
|
15
|
+
- `i18n.html` - Resolves the per-page-language UI-string set into the `ui` variable (generated translations fall back to English)
|
|
16
|
+
- `hreflang.html` - `<link rel="alternate" hreflang>` tags for pages with machine-generated translations
|
|
15
17
|
|
|
16
18
|
### `navigation/`
|
|
17
19
|
|
|
@@ -40,6 +42,8 @@ Analytics and tracking integrations:
|
|
|
40
42
|
Reusable UI components and widgets:
|
|
41
43
|
|
|
42
44
|
- `searchbar.html` - Deprecated search stub (superseded by `search-modal.html`)
|
|
45
|
+
- `language-toggle.html` - Navbar dropdown switching between the English original and its machine-generated translations
|
|
46
|
+
- `translation-notice.html` - Disclosure banner on machine-translated pages linking back to the English original
|
|
43
47
|
- `powered-by.html` - "Powered by" credits display
|
|
44
48
|
- `quick-index.html` - Quick page index
|
|
45
49
|
- `dev-shortcuts.html` - Developer shortcuts
|
|
@@ -91,6 +91,7 @@ If a component needs a token that doesn't exist yet, propose it in `_sass/tokens
|
|
|
91
91
|
| `info-section.html` | Settings offcanvas — Appearance / Site / Developer. |
|
|
92
92
|
| `js-cdn.html` | Body-end script bundle. |
|
|
93
93
|
| `search-modal.html` | Global search modal. |
|
|
94
|
+
| `page-feedback.html` | "Improve this page" capture widget — request type + live page context + captured console logs → GitHub issue (base prefill + optional AI enrichment). Pairs with `assets/js/page-feedback.js` and `core/console-capture.html`; types from `_data/feedback_types.yml`. |
|
|
94
95
|
|
|
95
96
|
## Adding a new component
|
|
96
97
|
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
reference an external avatar, e.g. a GitHub one:
|
|
11
11
|
avatar: "https://avatars.githubusercontent.com/u/10567847?v=4"
|
|
12
12
|
2. `avatar` is a relative path (e.g. /images/authors/me.png) — prefixed with
|
|
13
|
-
the site's
|
|
13
|
+
the site's public_folder and passed through relative_url, so baseurl is
|
|
14
|
+
applied and the slashes never double into a protocol-relative //… URL
|
|
15
|
+
(even when public_folder and/or baseurl are empty or unset).
|
|
14
16
|
3. No `avatar`, but a `github` handle is set — falls back to that account's
|
|
15
17
|
GitHub avatar: https://github.com/<handle>.png
|
|
16
18
|
4. Otherwise — empty output (the caller renders a fallback icon).
|
|
@@ -22,7 +24,7 @@
|
|
|
22
24
|
{%- endcomment -%}
|
|
23
25
|
{%- assign _avatar = include.data.avatar -%}
|
|
24
26
|
{%- if _avatar and _avatar != "" -%}
|
|
25
|
-
{%- if _avatar contains "://" -%}{{ _avatar }}{%- else -%}{
|
|
27
|
+
{%- if _avatar contains "://" -%}{{ _avatar }}{%- else -%}{%- capture _avatar_path -%}/{{ site.public_folder }}{{ _avatar }}{%- endcapture -%}{{ _avatar_path | replace: "//", "/" | relative_url }}{%- endif -%}
|
|
26
28
|
{%- elsif include.data.github and include.data.github != "" -%}
|
|
27
29
|
https://github.com/{{ include.data.github }}.png
|
|
28
30
|
{%- endif -%}
|
|
@@ -132,7 +132,8 @@
|
|
|
132
132
|
</div>
|
|
133
133
|
</li>
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
{% unless is_production %}
|
|
136
|
+
<!-- Development URL (dev builds only — never leak localhost into production HTML) -->
|
|
136
137
|
<li class="list-group-item d-flex justify-content-between align-items-center gap-2">
|
|
137
138
|
<div class="d-flex align-items-center min-w-0">
|
|
138
139
|
<span class="badge bg-warning text-dark me-2">Dev</span>
|
|
@@ -162,6 +163,7 @@
|
|
|
162
163
|
</button>
|
|
163
164
|
</div>
|
|
164
165
|
</li>
|
|
166
|
+
{% endunless %}
|
|
165
167
|
|
|
166
168
|
{% if site.repository %}
|
|
167
169
|
<!-- GitHub Source -->
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{% comment %} Feature: ZER0-078 {% endcomment %}
|
|
2
|
+
{%- comment -%}
|
|
3
|
+
Component: language-toggle
|
|
4
|
+
Path: _includes/components/language-toggle.html
|
|
5
|
+
Purpose: Navbar dropdown that switches between the English original and
|
|
6
|
+
its machine-generated translations (scripts/translate.rb).
|
|
7
|
+
Shows the source language plus every configured target
|
|
8
|
+
language; targets without a generated translation for the
|
|
9
|
+
current page render as disabled entries.
|
|
10
|
+
Params: none — reads page.lang / page.url / page.translation_source_url
|
|
11
|
+
and the generated _data/i18n/manifest.yml.
|
|
12
|
+
Depends on: site.translation.* config, site.data.i18n.manifest,
|
|
13
|
+
_data/i18n/languages.yml (display names), core/i18n.html,
|
|
14
|
+
Bootstrap 5 dropdown, Bootstrap Icons (bi-translate)
|
|
15
|
+
Notes: Renders nothing when translation is disabled or no target
|
|
16
|
+
languages are configured. The chosen language is remembered in
|
|
17
|
+
localStorage("zer0-lang") for future UX (no auto-redirect).
|
|
18
|
+
Must NOT be include_cached — the menu is page-dependent.
|
|
19
|
+
{%- endcomment -%}
|
|
20
|
+
{%- assign _lt_langs = site.translation.languages -%}
|
|
21
|
+
{%- if site.translation.enabled and _lt_langs and _lt_langs.size > 0 -%}
|
|
22
|
+
{%- include core/i18n.html -%}
|
|
23
|
+
{%- assign _lt_source = site.translation.source_lang | default: 'en' -%}
|
|
24
|
+
{%- assign _lt_current = page.lang | default: _lt_source -%}
|
|
25
|
+
{%- if _lt_current == _lt_source -%}
|
|
26
|
+
{%- assign _lt_src_url = page.url -%}
|
|
27
|
+
{%- else -%}
|
|
28
|
+
{%- assign _lt_src_url = page.translation_source_url -%}
|
|
29
|
+
{%- endif -%}
|
|
30
|
+
{%- assign _lt_entry = site.data.i18n.manifest.pages[_lt_src_url] -%}
|
|
31
|
+
{%- assign _lt_names = site.data.i18n.languages -%}
|
|
32
|
+
{%- assign _lt_source_name = _lt_names[_lt_source].native | default: _lt_source -%}
|
|
33
|
+
<div class="dropdown nav-lang-toggle" id="zer0-lang-toggle">
|
|
34
|
+
<button
|
|
35
|
+
class="btn nav-lang-button dropdown-toggle"
|
|
36
|
+
type="button"
|
|
37
|
+
id="langToggleButton"
|
|
38
|
+
data-bs-toggle="dropdown"
|
|
39
|
+
aria-expanded="false"
|
|
40
|
+
aria-label="{{ ui.lang_toggle_aria | default: 'Change language' }}"
|
|
41
|
+
title="{{ ui.lang_toggle_label | default: 'Language' }}"
|
|
42
|
+
>
|
|
43
|
+
<i class="{{ site.default_icon }} bi-translate" aria-hidden="true"></i>
|
|
44
|
+
<span class="nav-link-text d-none d-xl-inline ms-1 text-uppercase">{{ _lt_current | slice: 0, 2 }}</span>
|
|
45
|
+
</button>
|
|
46
|
+
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="langToggleButton">
|
|
47
|
+
<li>
|
|
48
|
+
{%- if _lt_current == _lt_source %}
|
|
49
|
+
<span class="dropdown-item active" aria-current="true">{{ _lt_source_name }}</span>
|
|
50
|
+
{%- else %}
|
|
51
|
+
<a class="dropdown-item" data-lang="{{ _lt_source }}" hreflang="{{ _lt_source }}" href="{{ _lt_src_url | default: '/' | relative_url }}">{{ _lt_source_name }}</a>
|
|
52
|
+
{%- endif %}
|
|
53
|
+
</li>
|
|
54
|
+
{%- for _lt_lang in _lt_langs -%}
|
|
55
|
+
{%- assign _lt_name = _lt_names[_lt_lang].native | default: _lt_lang -%}
|
|
56
|
+
{%- assign _lt_t = _lt_entry[_lt_lang] %}
|
|
57
|
+
<li>
|
|
58
|
+
{%- if _lt_current == _lt_lang %}
|
|
59
|
+
<span class="dropdown-item active" aria-current="true">{{ _lt_name }}</span>
|
|
60
|
+
{%- elsif _lt_t %}
|
|
61
|
+
<a class="dropdown-item" data-lang="{{ _lt_lang }}" hreflang="{{ _lt_lang }}" href="{{ _lt_t.url | relative_url }}">{{ _lt_name }}</a>
|
|
62
|
+
{%- else %}
|
|
63
|
+
<span class="dropdown-item disabled" aria-disabled="true" title="{{ ui.lang_not_available | default: 'Not yet translated' }}">{{ _lt_name }} <small class="text-body-secondary">({{ ui.lang_not_available | default: 'Not yet translated' }})</small></span>
|
|
64
|
+
{%- endif %}
|
|
65
|
+
</li>
|
|
66
|
+
{%- endfor %}
|
|
67
|
+
</ul>
|
|
68
|
+
</div>
|
|
69
|
+
<script>
|
|
70
|
+
// Remember the visitor's language pick (no auto-redirect — just preference).
|
|
71
|
+
(function () {
|
|
72
|
+
var toggle = document.getElementById('zer0-lang-toggle');
|
|
73
|
+
if (!toggle) return;
|
|
74
|
+
toggle.addEventListener('click', function (event) {
|
|
75
|
+
var item = event.target.closest('a.dropdown-item[data-lang]');
|
|
76
|
+
if (!item) return;
|
|
77
|
+
try { localStorage.setItem('zer0-lang', item.getAttribute('data-lang')); } catch (e) { /* private mode */ }
|
|
78
|
+
});
|
|
79
|
+
})();
|
|
80
|
+
</script>
|
|
81
|
+
{%- endif -%}
|