i18n+filtering fork - fluent-templates v2

Template Migration Changelog Core Templates Updated:

templates/base.html - Added dynamic locale-based includes
templates/base_fr-ca.html - Updated with dynamic includes
templates/footer.html - Migrated to i18n functions
templates/footer_fr-ca.html - Created French variant
templates/index.html - Full i18n migration with dynamic extends
templates/index_fr-ca.html - Created French homepage

Event Templates:

templates/create-event.html - Updated to use i18n functions
templates/create-event_fr-ca.html - Created French variant
templates/includes/create-event-common.html - Migrated form content
templates/includes/create-event-common_fr-ca.html - French form variant
templates/create_event.en-us.partial.html - NEW Migrated form partials to i18n
templates/create_event.fr-ca.partial.html - NEW Created French form partial

Layout Templates:

templates/bare.html - Added dynamic locale handling
templates/bare_fr-ca.html - French bare layout
templates/partial.html - Updated for locale support
templates/partial_fr-ca.html - French partial layout

Alert Templates:

templates/alert.html - Migrated to i18n
templates/alert_fr-ca.html - French alert template
templates/alert-bare.html - Updated bare alert
templates/alert-bare_fr-ca.html - French bare alert

Translation Files:

locales/en/ui.ftl - Added homepage and form translation keys + NEW form buttons, location types, status labels
locales/fr-ca/ui.ftl - Added French translations + NEW corresponding French form elements
locales/en/forms.ftl - Added event creation form keys + NEW placeholders, help text, form labels
locales/fr-ca/forms.ftl - Added French form translations + NEW French placeholders and form content

Total: 20 files modified/created, expanded form translation coverage with detailed placeholders, help text, and form controls.

docs/COMMIT_SUMMARY.md docs/COMMIT_SUMMARY-V1.md
+342
docs/Claude-TODO-V2.md
··· 1 + # Next Steps for Complete i18n Integration 2 + 3 + **Status**: Template Rendering System Refactoring ✅ **COMPLETE** 4 + **Next Phase**: Template Migration & Full i18n Implementation 5 + **Project**: smokesignal (Event & RSVP Management) 6 + **Date**: May 31, 2025 7 + 8 + --- 9 + 10 + ## Executive Summary 11 + 12 + The foundational i18n infrastructure is now complete with a unified `TemplateRenderer` system successfully implemented. All 16+ compilation errors have been resolved, and the application now has a modern, centralized template rendering architecture. The next phase focuses on migrating existing templates to fully utilize the i18n system and creating comprehensive language support. 13 + 14 + ## Current State ✅ COMPLETED 15 + 16 + ### ✅ Core Infrastructure 17 + - **fluent-templates Integration**: Static loading with zero runtime overhead 18 + - **TemplateRenderer System**: Unified template rendering with automatic context enrichment 19 + - **I18n Template Functions**: `tr()`, `current_locale()`, `has_locale()` available in templates 20 + - **HTMX Integration**: Smart template selection (partial, bare, full) 21 + - **Gender Support**: Full French Canadian gender variant handling 22 + - **Error Handling**: Consistent error templates with context preservation 23 + - **Handler Modernization**: All HTTP handlers converted to TemplateRenderer API 24 + 25 + ### ✅ Files Structure 26 + - **Templates**: 70+ English-only templates (`.en-us.html` variants) 27 + - **Fluent Files**: Complete English and French Canadian .ftl files 28 + - **Template Functions**: Ready for use (`tr()`, gender context, locale detection) 29 + - **Build System**: Compilation working, all tests passing 30 + 31 + ### ✅ PHASE 1 PROGRESS: Template Migration (STARTED) 32 + - **Navigation Templates**: ✅ COMPLETE (`nav.en-us.html`, `nav.fr-ca.html`) 33 + - **Base Templates**: ✅ COMPLETE (`base.en-us.html`, `base.fr-ca.html`) 34 + - **Footer Templates**: ✅ COMPLETE (`footer.en-us.html`, `footer.fr-ca.html`) 35 + - **Bare Templates**: ✅ COMPLETE (`bare.en-us.html`, `bare.fr-ca.html`) 36 + - **Homepage Templates**: ✅ COMPLETE (`index.en-us.html`, `index.fr-ca.html`) 37 + - **Create Event Templates**: ✅ COMPLETE (`create_event.en-us.html`, `create_event.fr-ca.html`, `create_event.partial.en-us.html`, `create_event.partial.fr-ca.html`) 38 + - **Translation Keys Added**: Navigation, footer, homepage, form titles, lables 39 + - **Dynamic Includes**: Templates now use `current_locale()` for includes 40 + 41 + ## Remaining Work 🚧 TODO 42 + 43 + ### Phase 1: Template Content Migration 44 + **Priority**: HIGH | **Effort**: Medium | **Impact**: High 45 + 46 + #### 1.1 Convert Hardcoded Text to i18n Functions 47 + Replace hardcoded English text with `tr()` calls in templates: 48 + 49 + **Current**: 50 + ```html 51 + <a class="navbar-item" href="/" hx-boost="true">Home</a> 52 + <span>Add Event</span> 53 + <span>Your Profile</span> 54 + ``` 55 + 56 + **Target**: 57 + ```html 58 + <a class="navbar-item" href="/" hx-boost="true">{{ tr("ui-home") }}</a> 59 + <span>{{ tr("ui-add-event") }}</span> 60 + <span>{{ tr("ui-your-profile") }}</span> 61 + ``` 62 + 63 + **Files to Update**: 70+ template files 64 + - Navigation templates (`nav.en-us.html`) 65 + - Base templates (`base.en-us.html`, `footer.en-us.html`) 66 + - Form templates (all `create_*.html`, `edit_*.html`) 67 + - Admin templates (`admin*.html`) 68 + - Content pages (`index.en-us.html`, policies, etc.) 69 + 70 + #### 1.2 Extract Translation Keys 71 + **Audit Required**: Review all hardcoded strings and create corresponding .ftl entries 72 + 73 + **Templates with Most Content**: 74 + 1. `nav.en-us.html` - Navigation labels 75 + 2. `create_event.en-us.html` - Event creation form 76 + 3. `admin*.html` - Admin interface 77 + 4. `view_event.en-us.html` - Event display 78 + 5. `settings.en-us.html` - User settings 79 + 80 + ### Phase 2: Translation Completion 81 + **Priority**: HIGH | **Effort**: Medium | **Impact**: High 82 + 83 + #### 2.1 Complete English Fluent Files 84 + **Current**: Basic structure in place 85 + **Needed**: Extract and organize all template strings 86 + 87 + **Action Items**: 88 + - Audit existing `ui.ftl`, `forms.ftl`, `actions.ftl`, `common.ftl`, `errors.ftl` 89 + - Add missing translation keys for all hardcoded template text 90 + - Organize keys by functional area (navigation, forms, errors, etc.) 91 + - Ensure gender-neutral English variants where appropriate 92 + - FTL Language files should not have duplicates between them for the same locale. 93 + - Compare FTL language files to find discrepancies. 94 + 95 + #### 2.2 French Canadian Translation 96 + **Current**: Template structure exists, partial translations 97 + **Needed**: Complete professional French Canadian translations 98 + 99 + **Action Items**: 100 + - Translate all English keys to French Canadian 101 + - Implement gender-aware translations for fr-ca 102 + - Review cultural appropriateness and local terminology 103 + - Test gender context integration (`{{ tr("welcome", gender=user_gender) }}`) 104 + 105 + ### Phase 3: French Template Creation 106 + **Priority**: MEDIUM | **Effort**: High | **Impact**: High 107 + 108 + #### 3.1 Create fr-ca Template Variants 109 + **Current**: Only English templates exist (`.en-us.html`) 110 + **Needed**: French Canadian template variants (`.fr-ca.html`) 111 + 112 + **Template Structure Target**: 113 + ``` 114 + templates/ 115 + ├── nav.en-us.html ← Existing English 116 + ├── nav.fr-ca.html ← NEW French Canadian 117 + ├── base.en-us.html ← Existing English 118 + ├── base.fr-ca.html ← NEW French Canadian 119 + ├── create_event.en-us.html ← Existing English 120 + ├── create_event.fr-ca.html ← NEW French Canadian 121 + └── ... (70+ template pairs) 122 + ``` 123 + 124 + **Considerations**: 125 + - Date/time formatting differences 126 + - Cultural layout preferences 127 + - RTL text handling (if needed) 128 + - Form validation message display 129 + 130 + ### Phase 4: Handler Integration Enhancement 131 + **Priority**: MEDIUM | **Effort**: Low | **Impact**: Medium 132 + 133 + #### 4.1 Complete Handler Migration 134 + **Status**: Mostly complete, some handlers may need updates 135 + 136 + **Action Items**: 137 + - Audit all handlers in `src/http/handle_*.rs` 138 + - Ensure all use `TemplateRenderer` consistently 139 + - Verify gender context passing where relevant 140 + - Test i18n context in all error scenarios 141 + 142 + #### 4.2 Form Validation Integration 143 + - Test locale propagation when making HTMX partial templates updates in forms results. 144 + **Needed**: Ensure form errors display in correct language 145 + 146 + **Current State**: Basic error translation works 147 + **Enhancement**: Context-aware validation messages and updates 148 + 149 + ### Phase 5: Testing & Quality Assurance 150 + **Priority**: HIGH | **Effort**: Medium | **Impact**: Critical 151 + 152 + #### 5.1 Comprehensive i18n Testing 153 + **Test Coverage Needed**: 154 + - FTL Language files do not have duplicates between them for the same locale. 155 + - Compare FTL language files to find discrepancies. 156 + - Template rendering in both languages 157 + - Gender context switching (French) 158 + - Language preference detection 159 + - HTMX partial rendering with i18n 160 + - Error messages in correct language 161 + - Form validation in both languages 162 + 163 + #### 5.2 Language Switching Testing 164 + **Scenarios**: 165 + - Browser language detection 166 + - Cookie preference persistence 167 + - User profile language settings 168 + - HTMX header language passing 169 + - Fallback behavior (missing translations) 170 + 171 + ### Phase 6: Performance & Optimization 172 + **Priority**: LOW | **Effort**: Low | **Impact**: Medium 173 + 174 + #### 6.1 Template Caching 175 + **Current**: Templates loaded on every request 176 + **Optimization**: Implement template compilation caching 177 + 178 + #### 6.2 Translation Performance 179 + **Current**: fluent-templates static loading (already optimized) 180 + **Monitoring**: Add performance metrics for translation lookups 181 + 182 + --- 183 + 184 + ## Implementation Plan 185 + 186 + ### Sprint 1 (Week 1-2): Core Template Migration 187 + 1. **Day 1-3**: Audit all templates, create comprehensive translation key list 188 + 2. **Day 4-7**: Update navigation and base templates with i18n functions 189 + 3. **Day 8-10**: Migrate form templates (create_event, edit_event, etc.) 190 + 4. **Day 11-14**: Complete remaining content templates 191 + 192 + ### Sprint 2 (Week 3-4): Translation & French Templates 193 + 1. **Day 1-7**: Complete English fluent files with all extracted keys 194 + 2. **Day 8-14**: Professional French Canadian translation of all keys 195 + 3. **Day 8-14**: Create French template variants (parallel with translation) 196 + 197 + ### Sprint 3 (Week 5): Testing & Integration 198 + 1. **Day 1-3**: Comprehensive testing of both language variants 199 + 2. **Day 4-5**: Handler integration verification and fixes 200 + 3. **Day 6-7**: Performance testing and optimization 201 + 202 + ### Sprint 4 (Week 6): Launch Preparation 203 + 1. **Day 1-3**: Final testing and bug fixes 204 + 2. **Day 4-5**: Documentation and deployment preparation 205 + 3. **Day 6-7**: Production deployment and monitoring 206 + 207 + --- 208 + 209 + ## Technical Specifications 210 + 211 + ### Template Function Usage Patterns 212 + 213 + #### Basic Translation 214 + ```html 215 + {{ tr("ui-welcome") }} 216 + {{ tr("form-submit") }} 217 + ``` 218 + 219 + #### Parametrized Translation 220 + ```html 221 + {{ tr("event-count", count=events|length) }} 222 + {{ tr("welcome-user", username=current_handle.username) }} 223 + ``` 224 + 225 + #### Gender-Aware Translation (French) 226 + ```html 227 + {{ tr("welcome-message", gender=user_gender) }} 228 + {{ tr("user-status", gender=user_gender, status=current_status) }} 229 + ``` 230 + 231 + #### Conditional Language Display 232 + ```html 233 + {% if current_locale() == "fr-ca" %} 234 + <!-- French-specific content --> 235 + {% endif %} 236 + ``` 237 + 238 + ### File Naming Conventions 239 + 240 + #### Templates 241 + - English: `{template}.en-us.html` 242 + - French: `{template}.fr-ca.html` 243 + - Language-neutral: `{template}.html` (no language suffix) 244 + 245 + #### Fluent Files 246 + - `i18n/en-us/*.ftl` (English US) 247 + - `i18n/fr-ca/*.ftl` (French Canadian) 248 + 249 + ### Translation Key Organization 250 + 251 + #### Prefixes 252 + - `ui-*`: User interface elements (buttons, labels, navigation) 253 + - `form-*`: Form labels, placeholders, validation messages 254 + - `error-*`: Error messages and alerts 255 + - `action-*`: Action buttons and links 256 + - `page-*`: Page titles and headings 257 + - `admin-*`: Admin interface specific 258 + - `event-*`: Event-related content 259 + - `rsvp-*`: RSVP-related content 260 + 261 + --- 262 + 263 + ## Success Criteria 264 + 265 + ### Functional Requirements ✅ 266 + - [ ] All templates use i18n functions instead of hardcoded text 267 + - [ ] Complete English and French Canadian translation coverage 268 + - [ ] Language switching works across all pages 269 + - [ ] Gender-aware translations work correctly in French 270 + - [ ] HTMX requests maintain language context 271 + - [ ] Error messages display in correct language 272 + - [ ] Form validation messages localized 273 + 274 + ### Performance Requirements ✅ 275 + - [ ] Page load time impact < 50ms 276 + - [ ] Translation lookup time < 1ms 277 + - [ ] Template rendering performance maintained 278 + - [ ] Memory usage increase < 10% 279 + 280 + ### Quality Requirements ✅ 281 + - [ ] Zero missing translation keys in production 282 + - [ ] All translations culturally appropriate 283 + - [ ] Gender agreement correct in French variants 284 + - [ ] Consistent terminology across all content 285 + - [ ] Accessibility maintained in both languages 286 + 287 + --- 288 + 289 + ## Dependencies & Blockers 290 + 291 + ### External Dependencies 292 + - **Translation Services**: Professional French Canadian translator 293 + - **Cultural Review**: French Canadian cultural/linguistic review 294 + - **Testing Resources**: Native French speakers for testing 295 + 296 + ### Technical Dependencies 297 + - ✅ fluent-templates integration (COMPLETE) 298 + - ✅ TemplateRenderer system (COMPLETE) 299 + - ✅ HTMX i18n integration (COMPLETE) 300 + - ✅ Gender context system (COMPLETE) 301 + 302 + ### Potential Blockers 303 + - **Translation Quality**: Professional translation availability 304 + - **Cultural Accuracy**: French Canadian cultural review 305 + - **Testing Coverage**: Comprehensive bilingual testing 306 + 307 + --- 308 + 309 + ## Risk Assessment 310 + 311 + ### High Risk 🔴 312 + - **Incomplete Translations**: Missing keys break user experience 313 + - **Cultural Issues**: Inappropriate French translations damage credibility 314 + 315 + ### Medium Risk 🟡 316 + - **Performance Impact**: Extensive template changes affect load times 317 + - **HTMX Integration**: Language switching breaks dynamic functionality 318 + 319 + ### Low Risk 🟢 320 + - **Template Syntax**: Minor template function usage issues 321 + - **Key Organization**: Suboptimal translation key structure 322 + 323 + --- 324 + 325 + ## Conclusion 326 + 327 + The smokesignal application has successfully completed the foundational i18n infrastructure migration. The new `TemplateRenderer` system provides a robust, high-performance foundation for internationalization. The next phase focuses on content migration and comprehensive language support implementation. 328 + 329 + **Immediate Priority**: Begin template content migration (Phase 1) to convert hardcoded strings to i18n function calls. 330 + 331 + **Success Metrics**: 332 + - 100% template i18n function usage 333 + - Complete English/French language coverage 334 + - Maintained performance and user experience 335 + - Professional-quality translations 336 + 337 + **Timeline**: 6 weeks for complete implementation 338 + **Status**: Ready to begin implementation ✅ 339 + 340 + --- 341 + 342 + *This document will be updated as implementation progresses through each phase.*
docs/FINAL_STATUS.md docs/FINAL_STATUS-v1.md
docs/Prompts docs/Claude.prompts.md
docs/Step4-resume docs/Step4-V1-resume.md
docs/copilot-TODO docs/Claude-TODO-V1.md
docs/fluent-template-doc docs/fluent-template-doc.md
+31
docs/knowledgebase-V2.md
··· 1 + Based on my analysis, here are the two main organizational approaches and my recommendation: 2 + 3 + Current Structure (Flat with Language Suffixes) 4 + Alternative: Language-Based Folders 5 + Recommendation: Keep Current Structure ✅ 6 + Here's why I recommend keeping the current flat structure with language suffixes: 7 + 8 + Pros of Current Structure: 9 + Zero Code Changes Required: The TemplateRenderer already works with {base}.{locale}.{variant} naming 10 + Simpler Template Selection: No path logic changes needed in select_template() function 11 + Clear Language Pairing: Easy to see which templates have translations (nav.en-us.html vs nav.fr-ca.html) 12 + Version Control Friendly: Git diffs show language pairs clearly 13 + HTMX Variant Support: Works seamlessly with .bare.html, .partial.html variants 14 + Less Refactoring Risk: No handler updates needed 15 + Cons of Folder Structure: 16 + Breaking Changes: Would require updating TemplateRenderer::select_template() logic 17 + Handler Updates: Many handlers might need path updates 18 + Complex Fallbacks: Harder to implement English fallbacks for missing French templates 19 + HTMX Complications: More complex logic for partial/bare variants across folders 20 + Build System Impact: Might need changes to template loading/compilation 21 + Migration Effort Analysis: 22 + Current Approach: Continue with existing pattern = 0 breaking changes 23 + Folder Approach: Would require significant refactoring = ~2-3 days of work 24 + Recommendation: Proceed with Current Structure 25 + Since we have a working template selection system and 75+ templates, the pragmatic approach is to: 26 + 27 + Keep the existing naming convention: {template}.{locale}.{variant}.html 28 + Focus on content migration: Convert hardcoded text to tr() functions 29 + Create French templates: Follow the established pattern 30 + Optimize later: If folder structure becomes necessary, do it as a separate phase 31 + This approach aligns with our Sprint 1 goal of completing template migration efficiently without introducing unnecessary complexity.
+2 -8
docs/minijinja-reference docs/minijinja-reference.md
··· 1 1 Docs.rs 2 2 minijinja-2.10.2 3 - Platform 4 - Feature flags 5 - docs.rs 6 - Rust 7 - 8 - Find crate 9 - logo 3 + 10 4 minijinja 11 5 2.10.2 12 6 Crate Items ··· 15 9 Enums 16 10 Traits 17 11 Functions 18 - Type ‘S’ or ‘/’ to search, ‘?’ for more options… 12 + 19 13 List of all items 20 14 Structs 21 15 Environment
+29
i18n/en-us/forms.ftl
··· 1 1 # Form labels, placeholders, and help text - English (US) 2 2 3 + # Page titles 4 + form-title-create-event = Create Event 5 + 3 6 # Form field labels 4 7 label-name = Name 5 8 label-text = Text ··· 39 42 placeholder-at-uri-event = at://smokesignal.events/community.lexicon.calendar.event/neat 40 43 placeholder-at-uri-rsvp = at://did:plc:abc123/app.bsky.feed.post/record123 41 44 placeholder-at-uri-admin = at://did:plc:abcdef/community.lexicon.calendar.rsvp/3jizzrxoalv2h 45 + placeholder-awesome-event = My Awesome Event 46 + placeholder-event-description = A helpful, brief description of the event. 42 47 43 48 # Help text 44 49 help-name-length = Must be at least 10 characters and no more than 500 characters ··· 49 54 50 55 # Required field indicators 51 56 optional-field = (optional) 57 + 58 + # Form buttons and actions 59 + form-next = Next 60 + form-create = Create 61 + form-update = Update 62 + form-cancel = Cancel 63 + 64 + # Event location types 65 + label-event-location-type = What type of location? 66 + location-type-address = Physical Address 67 + location-type-online = Online 68 + location-type-other = Other location type 69 + 70 + # Event status labels 71 + label-status-planned = Planned 72 + label-status-scheduled = Scheduled 73 + label-status-cancelled = Cancelled 74 + label-status-postponed = Postponed 75 + label-status-rescheduled = Rescheduled 76 + 77 + # Event mode labels 78 + label-mode-virtual = Virtual 79 + label-mode-hybrid = Hybrid 80 + label-mode-inperson = In Person 52 81 53 82 # Time and date 54 83 not-set = Not Set
+10
i18n/en-us/ui.ftl
··· 11 11 acknowledgement = Acknowledgement 12 12 administration-tools = Administration Tools 13 13 14 + # Homepage content 15 + homepage-title = Smoke Signal 16 + homepage-subtitle = Find events, make connections, and create community. 17 + homepage-quick-start-text = The <a href="https://docs.smokesignal.events/docs/getting-started/quick-start/">Quick Start Guide</a> has a step-by-step guide to getting started! 18 + homepage-recent-events = Recently Updated Events 19 + homepage-description = Smoke Signal is an event and RSVP management system. 20 + 14 21 # Section headings 15 22 what-are-smoke-signals = What are smoke signals? 16 23 why-the-name = Why the name? ··· 94 101 # Success messages 95 102 event-created-success = The event has been created! 96 103 event-updated-success = The event has been updated! 104 + view-event = View Event 97 105 98 106 # Info messages 99 107 events-public-notice = Events are public and can be viewed by anyone that can view the information stored in your PDS. Do not publish personal or sensitive information in your events. 100 108 event-help-link = Event Help 109 + event-help-description = Learn more about events on the <a href="https://docs.smokesignal.events/docs/help/events/" rel="help">Event Help</a> page. 101 110 help-rsvp-aturi = Enter the full AT-URI of the RSVP you want to view 102 111 103 112 # Page titles and headings - English (US) ··· 169 178 nav-login = Log in 170 179 171 180 # Footer navigation 181 + footer-title = Smoke Signal Events 172 182 footer-support = Support 173 183 footer-privacy-policy = Privacy Policy 174 184 footer-cookie-policy = Cookie Policy
+29
i18n/fr-ca/forms.ftl
··· 1 1 # Étiquettes, textes indicatifs et aide pour les formulaires - Français (Canada) 2 2 3 + # Titres de page 4 + form-title-create-event = Créer un événement 5 + 3 6 # Étiquettes de champs de formulaire 4 7 label-name = Nom 5 8 label-text = Texte ··· 39 42 placeholder-at-uri-event = at://smokesignal.events/community.lexicon.calendar.event/neat 40 43 placeholder-at-uri-rsvp = at://did:plc:abc123/app.bsky.feed.post/record123 41 44 placeholder-at-uri-admin = at://did:plc:abcdef/community.lexicon.calendar.rsvp/3jizzrxoalv2h 45 + placeholder-awesome-event = Mon Événement Génial 46 + placeholder-event-description = Une description utile et brève de l'événement. 42 47 43 48 # Textes d'aide 44 49 help-name-length = Doit contenir au moins 10 caractères et pas plus de 500 caractères ··· 49 54 50 55 # Indicateurs de champs obligatoires 51 56 optional-field = (optionnel) 57 + 58 + # Boutons et actions de formulaire 59 + form-next = Suivant 60 + form-create = Créer 61 + form-update = Mettre à jour 62 + form-cancel = Annuler 63 + 64 + # Types d'emplacement d'événement 65 + label-event-location-type = Quel type d'emplacement ? 66 + location-type-address = Adresse physique 67 + location-type-online = En ligne 68 + location-type-other = Autre type d'emplacement 69 + 70 + # Étiquettes de statut d'événement 71 + label-status-planned = Planifié 72 + label-status-scheduled = Programmé 73 + label-status-cancelled = Annulé 74 + label-status-postponed = Reporté 75 + label-status-rescheduled = Reprogrammé 76 + 77 + # Étiquettes de mode d'événement 78 + label-mode-virtual = Virtuel 79 + label-mode-hybrid = Hybride 80 + label-mode-inperson = En personne 52 81 53 82 # Heure et date 54 83 not-set = Non défini
+10
i18n/fr-ca/ui.ftl
··· 11 11 acknowledgement = Reconnaissance 12 12 administration-tools = Outils d'administration 13 13 14 + # Contenu de la page d'accueil 15 + homepage-title = Signaux de fumée 16 + homepage-subtitle = Trouve des événements, fais des connexions et crée de la communauté. 17 + homepage-quick-start-text = Le <a href="https://docs.smokesignal.events/docs/getting-started/quick-start/">Guide de démarrage rapide</a> contient un guide étape par étape pour commencer! 18 + homepage-recent-events = Événements récemment mis à jour 19 + homepage-description = Signaux de fumée est un système de gestion d'événements et de RSVP. 20 + 14 21 # En-têtes de section 15 22 what-are-smoke-signals = Que sont les signaux de fumée? 16 23 why-the-name = Pourquoi ce nom? ··· 94 101 # Messages de succès 95 102 event-created-success = L'événement a été créé! 96 103 event-updated-success = L'événement a été mis à jour! 104 + view-event = Voir l'événement 97 105 98 106 # Messages d'information 99 107 events-public-notice = Les événements sont publics et peuvent être vus par quiconque peut voir les informations stockées dans ton PDS. Ne publie pas d'informations personnelles ou sensibles dans tes événements. 100 108 event-help-link = Aide sur les événements 109 + event-help-description = En apprends plus sur les événements sur la page <a href="https://docs.smokesignal.events/docs/help/events/" rel="help">Aide sur les événements</a>. 101 110 help-rsvp-aturi = Entre l'AT-URI complet de la RSVP que tu veux voir 102 111 103 112 # Titres de page et en-têtes - Français (Canada) ··· 169 178 nav-login = Se connecter 170 179 171 180 # Navigation de pied de page 181 + footer-title = Signaux de fumée Événements 172 182 footer-support = Support 173 183 footer-privacy-policy = Politique de confidentialité 174 184 footer-cookie-policy = Politique des cookies
+2 -2
templates/alert.en-us.bare.html
··· 1 - {% extends "bare.en-us.html" %} 1 + {% extends "bare." + current_locale() + ".html" %} 2 2 {% block content %} 3 3 <section class="section"> 4 4 <div class="container"> 5 - {% include 'alert.en-us.partial.html' %} 5 + {% include 'alert.' + current_locale() + '.partial.html' %} 6 6 </div> 7 7 </section> 8 8 {% endblock %}
+3 -3
templates/alert.en-us.html
··· 1 - {% extends "base.en-us.html" %} 2 - {% block title %}Smoke Signal{% endblock %} 1 + {% extends "base." + current_locale() + ".html" %} 2 + {% block title %}{{ tr("homepage-title") }}{% endblock %} 3 3 {% block head %}{% endblock %} 4 4 {% block content %} 5 5 <section class="section"> 6 6 <div class="container"> 7 - {% include 'alert.en-us.partial.html' %} 7 + {% include 'alert.' + current_locale() + '.partial.html' %} 8 8 </div> 9 9 </section> 10 10 {% endblock %}
+8
templates/alert.fr-ca.bare.html
··· 1 + {% extends "bare." + current_locale() + ".html" %} 2 + {% block content %} 3 + <section class="section"> 4 + <div class="container"> 5 + {% include 'alert.' + current_locale() + '.partial.html' %} 6 + </div> 7 + </section> 8 + {% endblock %}
+10
templates/alert.fr-ca.html
··· 1 + {% extends "base." + current_locale() + ".html" %} 2 + {% block title %}{{ tr("homepage-title") }}{% endblock %} 3 + {% block head %}{% endblock %} 4 + {% block content %} 5 + <section class="section"> 6 + <div class="container"> 7 + {% include 'alert.' + current_locale() + '.partial.html' %} 8 + </div> 9 + </section> 10 + {% endblock %}
+10
templates/alert.fr-ca.partial.html
··· 1 + <article class="message is-{{ message_type | default("danger") }}"> 2 + {% if message_title %} 3 + <div class="message-header"> 4 + <p>{{ message_title }}</p> 5 + </div> 6 + {% endif %} 7 + <div class="message-body"> 8 + {{ message }} 9 + </div> 10 + </article>
+2 -2
templates/bare.en-us.html
··· 1 - {% include 'nav.en-us.html' %} 1 + {% include 'nav.' + current_locale() + '.html' %} 2 2 {% block content %}{% endblock %} 3 - {% include 'footer.en-us.html' %} 3 + {% include 'footer.' + current_locale() + '.html' %}
+3
templates/bare.fr-ca.html
··· 1 + {% include 'nav.' + current_locale() + '.html' %} 2 + {% block content %}{% endblock %} 3 + {% include 'footer.' + current_locale() + '.html' %}
+3 -3
templates/base.en-us.html
··· 1 1 <!doctype html> 2 - <html lang="en"> 2 + <html lang="{{ current_locale() }}"> 3 3 <head> 4 4 <meta charset="utf-8"> 5 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> ··· 19 19 <meta name="theme-color" content="#00d1b2"> 20 20 </head> 21 21 <body hx-ext="loading-states"> 22 - {% include 'nav.en-us.html' %} 22 + {% include 'nav.' + current_locale() + '.html' %} 23 23 {% block content %}{% endblock %} 24 - {% include 'footer.en-us.html' %} 24 + {% include 'footer.' + current_locale() + '.html' %} 25 25 </body> 26 26 27 27 </html>
+27
templates/base.fr-ca.html
··· 1 + <!doctype html> 2 + <html lang="{{ current_locale() }}"> 3 + <head> 4 + <meta charset="utf-8"> 5 + <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 + <meta name="viewport" content="width=device-width, initial-scale=1"> 7 + <title>{% block title %}{{ title }}{% endblock %}</title> 8 + {% if canonical_url %} 9 + <link rel="canonical" href="{{ canonical_url }}"> 10 + {% endif %} 11 + <link rel="stylesheet" href="/static/fontawesome.min.css"> 12 + <link rel="stylesheet" href="/static/bulma.min.css"> 13 + <script src="/static/htmx.js"></script> 14 + <script src="/static/loading-states.js"></script> 15 + <script src="/static/sse.js"></script> 16 + <script src="/static/site.js"></script> 17 + {% block head %} 18 + {% endblock %} 19 + <meta name="theme-color" content="#00d1b2"> 20 + </head> 21 + <body hx-ext="loading-states"> 22 + {% include 'nav.' + current_locale() + '.html' %} 23 + {% block content %}{% endblock %} 24 + {% include 'footer.' + current_locale() + '.html' %} 25 + </body> 26 + 27 + </html>
+4 -9
templates/create_event.en-us.common.html
··· 4 4 5 5 <div class="box content"> 6 6 7 - <h1>Create Event</h1> 7 + <h1>{{ tr("form-title-create-event") }}</h1> 8 8 9 9 <article class="message is-info"> 10 10 <div class="message-body"> 11 11 <p> 12 - Events are public and can be viewed by anyone that can view the information stored in your PDS. Do not 13 - publish personal or sensitive information in your events. 12 + {{ tr("events-public-notice") }} 14 13 </p> 15 14 <p> 16 - Learn more about events on the 17 - <a href="https://docs.smokesignal.events/docs/help/events/" rel="help"> 18 - Event Help 19 - </a> 20 - page. 15 + {{ tr("event-help-description") | safe }} 21 16 </p> 22 17 </div> 23 18 </article> 24 19 25 - {% include 'create_event.en-us.partial.html' %} 20 + {% include 'create_event.' + current_locale() + '.partial.html' %} 26 21 27 22 </div> 28 23
+3 -3
templates/create_event.en-us.html
··· 1 - {% extends "base.en-us.html" %} 2 - {% block title %}Smoke Signal - Create Event{% endblock %} 1 + {% extends "base." + current_locale() + ".html" %} 2 + {% block title %}{{ tr("page-title-create-event") }}{% endblock %} 3 3 {% block head %}{% endblock %} 4 4 {% block content %} 5 - {% include 'create_event.en-us.common.html' %} 5 + {% include 'create_event.' + current_locale() + '.common.html' %} 6 6 {% endblock %}
+20 -19
templates/create_event.en-us.partial.html
··· 2 2 <article class="message is-success"> 3 3 <div class="message-header"> 4 4 {% if create_event %} 5 - <p>The event has been created!</p> 5 + <p>{{ tr("event-created-success") }}</p> 6 6 {% else %} 7 - <p>The event has been updated!</p> 7 + <p>{{ tr("event-updated-success") }}</p> 8 8 {% endif %} 9 9 </div> 10 10 <div class="message-body"> ··· 13 13 <span class="icon"> 14 14 <i class="fas fa-file"></i> 15 15 </span> 16 - <span>View Event</span> 16 + <span>{{ tr("view-event") }}</span> 17 17 </a> 18 18 </p> 19 19 </div> ··· 33 33 34 34 35 35 <div class="field"> 36 - <label class="label" for="createEventNameInput">Name (required)</label> 36 + <label class="label" for="createEventNameInput">{{ tr("label-name") }} (required)</label> 37 37 <div class="control {% if build_event_form.name_error %} has-icons-right{% endif %}" 38 38 data-loading-class="is-loading"> 39 39 <input type="text" class="input {% if build_event_form.name_error %} is-danger{% endif %}" 40 - id="createEventNameInput" name="name" minlength="10" maxlength="500" placeholder="My Awesome Event" {% 40 + id="createEventNameInput" name="name" minlength="10" maxlength="500" placeholder="{{ tr('placeholder-awesome-event') }}" {% 41 41 if build_event_form.name %}value="{{ build_event_form.name }}" {% endif %} required 42 42 data-loading-disable> 43 43 </div> 44 44 {% if build_event_form.name_error %} 45 45 <p class="help is-danger">{{ build_event_form.name_error }}</p> 46 46 {% else %} 47 - <p class="help">Must be at least 10 characters and no more than 500 characters.</p> 47 + <p class="help">{{ tr("help-name-length") }}</p> 48 48 {% endif %} 49 49 </div> 50 50 51 51 <div class="field"> 52 - <label class="label" for="createEventTextInput">Text (required)</label> 52 + <label class="label" for="createEventTextInput">{{ tr("label-description") }} (required)</label> 53 53 <div class="control"> 54 54 <textarea class="textarea{% if build_event_form.description_error %} is-danger{% endif %}" 55 55 id="createEventTextInput" name="description" maxlength="3000" rows="10" 56 - placeholder="A helpful, brief description of the event." required 56 + placeholder="{{ tr('placeholder-event-description') }}" required 57 57 data-loading-disable>{% if build_event_form.description %}{{ build_event_form.description }}{% endif %}</textarea> 58 58 </div> 59 59 {% if build_event_form.description_error %} 60 60 <p class="help is-danger">{{ build_event_form.description_error }}</p> 61 61 {% else %} 62 - <p class="help">Must be at least 10 characters and no more than 3000 characters.</p> 62 + <p class="help">{{ tr("help-description-length") }}</p> 63 63 {% endif %} 64 64 </div> 65 65 66 66 <div class="field"> 67 67 <div class="field-body"> 68 68 <div class="field"> 69 - <label class="label" for="createEventStatus">Status</label> 69 + <label class="label" for="createEventStatus">{{ tr("label-status") }}</label> 70 70 <div class="control"> 71 71 <div class="select"> 72 72 <select id="createEventStatus" name="status" 73 73 class="{% if build_event_form.status_error %}is-danger{% endif %}"> 74 74 <option {% if build_event_form.status=='planned' or not build_event_form.status %} 75 75 selected="selected" {% endif %} value="planned"> 76 - Planned 76 + {{ tr("label-status-planned") }} 77 77 </option> 78 78 <option {% if build_event_form.status=='scheduled' %} selected="selected" {% endif %} 79 79 value="scheduled"> 80 - Scheduled 80 + {{ tr("label-status-scheduled") }} 81 81 </option> 82 82 <option {% if build_event_form.status=='cancelled' %} selected="selected" {% endif %} 83 83 value="cancelled"> 84 - Cancelled 84 + {{ tr("label-status-cancelled") }} 85 85 </option> 86 86 <option {% if build_event_form.status=='postponed' %} selected="selected" {% endif %} 87 87 value="postponed"> 88 - Postponed 88 + {{ tr("label-status-postponed") }} 89 89 </option> 90 90 <option {% if build_event_form.status=='rescheduled' %} selected="selected" {% endif %} 91 91 value="rescheduled"> 92 - Rescheduled 92 + {{ tr("label-status-rescheduled") }} 93 93 </option> 94 94 </select> 95 95 </div> ··· 99 99 {% endif %} 100 100 </div> 101 101 <div class="field pb-5"> 102 - <label class="label" for="createEventMode">Mode</label> 102 + <label class="label" for="createEventMode">{{ tr("label-mode") }}</label> 103 103 <div class="control"> 104 104 <div class="select"> 105 105 <select id="createEventMode" name="mode" 106 106 class="{% if build_event_form.mode_error %}is-danger{% endif %}"> 107 107 <option value="virtual" {% if build_event_form.mode=='virtual' %} selected{% endif %}> 108 - Virtual 108 + {{ tr("label-mode-virtual") }} 109 109 </option> 110 - <option value="hybrid" {% if build_event_form.mode=='hybrid' %} selected{% endif %}>Hybrid 110 + <option value="hybrid" {% if build_event_form.mode=='hybrid' %} selected{% endif %}> 111 + {{ tr("label-mode-hybrid") }} 111 112 </option> 112 113 <option value="inperson" {% if build_event_form.mode=='inperson' or not 113 - build_event_form.mode %} selected{% endif %}>In Person</option> 114 + build_event_form.mode %} selected{% endif %}>{{ tr("label-mode-inperson") }}</option> 114 115 </select> 115 116 </div> 116 117 </div>
+30
templates/create_event.fr-ca.common.html
··· 1 + {% from "form_include.html" import text_input %} 2 + <section class="section is-fullheight"> 3 + <div class="container "> 4 + 5 + <div class="box content"> 6 + 7 + <h1>{{ tr("form-title-create-event") }}</h1> 8 + 9 + <article class="message is-info"> 10 + <div class="message-body"> 11 + <p> 12 + {{ tr("events-public-notice") }} 13 + </p> 14 + <p> 15 + {{ tr("event-help-description") | safe }} 16 + </p> 17 + </div> 18 + </article> 19 + 20 + {% include 'create_event.' + current_locale() + '.partial.html' %} 21 + 22 + </div> 23 + 24 + </div> 25 + </section> 26 + <script> 27 + function checkUserKeydown(event) { 28 + return event instanceof KeyboardEvent 29 + } 30 + </script>
+6
templates/create_event.fr-ca.html
··· 1 + {% extends "base." + current_locale() + ".html" %} 2 + {% block title %}{{ tr("page-title-create-event") }}{% endblock %} 3 + {% block head %}{% endblock %} 4 + {% block content %} 5 + {% include 'create_event.' + current_locale() + '.common.html' %} 6 + {% endblock %}
+152
templates/create_event.fr-ca.partial.html
··· 1 + {% if operation_completed %} 2 + <article class="message is-success"> 3 + <div class="message-header"> 4 + {% if create_event %} 5 + <p>{{ tr("event-created-success") }}</p> 6 + {% else %} 7 + <p>{{ tr("event-updated-success") }}</p> 8 + {% endif %} 9 + </div> 10 + <div class="message-body"> 11 + <p class="buttons"> 12 + <a class="button" href="{{ event_url }}"> 13 + <span class="icon"> 14 + <i class="fas fa-file"></i> 15 + </span> 16 + <span>{{ tr("view-event") }}</span> 17 + </a> 18 + </p> 19 + </div> 20 + </article> 21 + {% else %} 22 + 23 + {% from "form_include.html" import text_input %} 24 + <form hx-post="{{ submit_url }}" hx-swap="outerHTML" class="my-5"> 25 + 26 + {% if build_event_form.build_state == "Reset" %} 27 + <input type="hidden" name="build_state" value="Selecting"> 28 + {% elif build_event_form.build_state == "Selecting" %} 29 + <input type="hidden" name="build_state" value="Selected"> 30 + {% elif build_event_form.build_state == "Selected" %} 31 + <input type="hidden" name="build_state" value="Selected"> 32 + {% endif %} 33 + 34 + 35 + <div class="field"> 36 + <label class="label" for="createEventNameInput">{{ tr("label-name") }} (required)</label> 37 + <div class="control {% if build_event_form.name_error %} has-icons-right{% endif %}" 38 + data-loading-class="is-loading"> 39 + <input type="text" class="input {% if build_event_form.name_error %} is-danger{% endif %}" 40 + id="createEventNameInput" name="name" minlength="10" maxlength="500" placeholder="{{ tr('placeholder-awesome-event') }}" {% 41 + if build_event_form.name %}value="{{ build_event_form.name }}" {% endif %} required 42 + data-loading-disable> 43 + </div> 44 + {% if build_event_form.name_error %} 45 + <p class="help is-danger">{{ build_event_form.name_error }}</p> 46 + {% else %} 47 + <p class="help">{{ tr("help-name-length") }}</p> 48 + {% endif %} 49 + </div> 50 + 51 + <div class="field"> 52 + <label class="label" for="createEventTextInput">{{ tr("label-description") }} (required)</label> 53 + <div class="control"> 54 + <textarea class="textarea{% if build_event_form.description_error %} is-danger{% endif %}" 55 + id="createEventTextInput" name="description" maxlength="3000" rows="10" 56 + placeholder="{{ tr('placeholder-event-description') }}" required 57 + data-loading-disable>{% if build_event_form.description %}{{ build_event_form.description }}{% endif %}</textarea> 58 + </div> 59 + {% if build_event_form.description_error %} 60 + <p class="help is-danger">{{ build_event_form.description_error }}</p> 61 + {% else %} 62 + <p class="help">{{ tr("help-description-length") }}</p> 63 + {% endif %} 64 + </div> 65 + 66 + {% if build_event_form.build_state == "Reset" %} 67 + 68 + <div class="field is-grouped"> 69 + <div class="control"> 70 + <button class="button is-link" type="submit" data-loading-disable> 71 + {{ tr("form-next") }} 72 + </button> 73 + </div> 74 + </div> 75 + 76 + {% elif build_event_form.build_state == "Selecting" %} 77 + 78 + <div class="field"> 79 + <label class="label">{{ tr("label-location") }}</label> 80 + <div class="control"> 81 + <div class="subtitle has-text-weight-normal" style="margin-bottom: 4px"> 82 + {{ tr("label-event-location-type") }} 83 + </div> 84 + 85 + <input type="radio" id="createEventPhysical" name="location_type" value="physical" 86 + {% if build_event_form.location_type == 'physical' %}checked{% endif %} data-loading-disable> 87 + <label for="createEventPhysical">{{ tr("location-type-address") }}</label> 88 + <br> 89 + 90 + <input type="radio" id="createEventOnline" name="location_type" value="online" 91 + {% if build_event_form.location_type == 'online' %}checked{% endif %} data-loading-disable> 92 + <label for="createEventOnline">{{ tr("location-type-online") }}</label> 93 + <br> 94 + 95 + <input type="radio" id="createEventOther" name="location_type" value="other" 96 + {% if build_event_form.location_type == 'other' %}checked{% endif %} data-loading-disable> 97 + <label for="createEventOther">{{ tr("location-type-other") }}</label> 98 + </div> 99 + </div> 100 + 101 + <div class="field is-grouped"> 102 + <div class="control"> 103 + <button class="button is-link" type="submit" data-loading-disable> 104 + {{ tr("form-next") }} 105 + </button> 106 + </div> 107 + </div> 108 + 109 + {% else %} 110 + 111 + {% if build_event_form.location_type == 'online' %} 112 + {% include 'create_event.' + current_locale() + '.link_form.html' %} 113 + {% elif build_event_form.location_type == 'physical' %} 114 + {% include 'create_event.' + current_locale() + '.location_form.html' %} 115 + {% else %} 116 + <div class="field"> 117 + <label class="label" for="createEventLocationName">{{ tr("label-location-name") }}</label> 118 + <div class="control"> 119 + <input class="input" id="createEventLocationName" type="text" name="venue_name" 120 + placeholder="{{ tr('placeholder-venue-name') }}" data-loading-disable 121 + {% if build_event_form.venue_name %}value="{{ build_event_form.venue_name }}"{% endif %}> 122 + </div> 123 + </div> 124 + {% endif %} 125 + 126 + {% include 'create_event.' + current_locale() + '.starts_form.html' %} 127 + 128 + <div class="field is-grouped"> 129 + <div class="control"> 130 + {% if create_event %} 131 + <button class="button is-link" type="submit" data-loading-disable> 132 + {{ tr("form-create") }} 133 + </button> 134 + {% else %} 135 + <button class="button is-link" type="submit" data-loading-disable> 136 + {{ tr("form-update") }} 137 + </button> 138 + {% endif %} 139 + </div> 140 + 141 + <div class="control"> 142 + <a href="{{ cancel_url }}" class="button is-link is-light" hx-boost="true"> 143 + {{ tr("form-cancel") }} 144 + </a> 145 + </div> 146 + </div> 147 + 148 + {% endif %} 149 + 150 + </form> 151 + 152 + {% endif %}
+7 -7
templates/footer.en-us.html
··· 1 1 <footer class="footer"> 2 2 <div class="container content has-text-centered"> 3 3 <p> 4 - <strong>Smoke Signal Events</strong> made by <a href="https://ngerakines.me/">Nick Gerakines</a> 5 - (<a href="https://github.com/ngerakines">Source Code</a>) 4 + <strong>{{ tr("footer-title") }}</strong> {{ tr("footer-made-by") }} <a href="https://ngerakines.me/">Nick Gerakines</a> 5 + (<a href="https://github.com/ngerakines">{{ tr("footer-source-code") }}</a>) 6 6 </p> 7 7 <nav class="level"> 8 8 <div class="level-item has-text-centered"> 9 - <a href="https://docs.smokesignal.events/">Support</a> 9 + <a href="https://docs.smokesignal.events/">{{ tr("footer-support") }}</a> 10 10 </div> 11 11 <div class="level-item has-text-centered"> 12 - <a href="/privacy-policy" hx-boost="true">Privacy Policy</a> 12 + <a href="/privacy-policy" hx-boost="true">{{ tr("footer-privacy-policy") }}</a> 13 13 </div> 14 14 <div class="level-item has-text-centered"> 15 - <a href="/cookie-policy" hx-boost="true">Cookie Policy</a> 15 + <a href="/cookie-policy" hx-boost="true">{{ tr("footer-cookie-policy") }}</a> 16 16 </div> 17 17 <div class="level-item has-text-centered"> 18 - <a href="/terms-of-service" hx-boost="true">Terms of Service</a> 18 + <a href="/terms-of-service" hx-boost="true">{{ tr("footer-terms-of-service") }}</a> 19 19 </div> 20 20 <div class="level-item has-text-centered"> 21 - <a href="/acknowledgement" hx-boost="true">Acknowledgement</a> 21 + <a href="/acknowledgement" hx-boost="true">{{ tr("footer-acknowledgement") }}</a> 22 22 </div> 23 23 </nav> 24 24 </div>
+25
templates/footer.fr-ca.html
··· 1 + <footer class="footer"> 2 + <div class="container content has-text-centered"> 3 + <p> 4 + <strong>{{ tr("footer-title") }}</strong> {{ tr("footer-made-by") }} <a href="https://ngerakines.me/">Nick Gerakines</a> 5 + (<a href="https://github.com/ngerakines">{{ tr("footer-source-code") }}</a>) 6 + </p> 7 + <nav class="level"> 8 + <div class="level-item has-text-centered"> 9 + <a href="https://docs.smokesignal.events/">{{ tr("footer-support") }}</a> 10 + </div> 11 + <div class="level-item has-text-centered"> 12 + <a href="/privacy-policy" hx-boost="true">{{ tr("footer-privacy-policy") }}</a> 13 + </div> 14 + <div class="level-item has-text-centered"> 15 + <a href="/cookie-policy" hx-boost="true">{{ tr("footer-cookie-policy") }}</a> 16 + </div> 17 + <div class="level-item has-text-centered"> 18 + <a href="/terms-of-service" hx-boost="true">{{ tr("footer-terms-of-service") }}</a> 19 + </div> 20 + <div class="level-item has-text-centered"> 21 + <a href="/acknowledgement" hx-boost="true">{{ tr("footer-acknowledgement") }}</a> 22 + </div> 23 + </nav> 24 + </div> 25 + </footer>
+5 -6
templates/index.en-us.common.html
··· 1 1 {%- from "pagination.html" import view_pagination -%} 2 2 <section class="section"> 3 3 <div class="container"> 4 - <h1 class="title is-1">Smoke Signal</h1> 5 - <h2 class="subtitle">Find events, make connections, and create community.</h2> 4 + <h1 class="title is-1">{{ tr("homepage-title") }}</h1> 5 + <h2 class="subtitle">{{ tr("homepage-subtitle") }}</h2> 6 6 <p class="content"> 7 - The <a href="https://docs.smokesignal.events/docs/getting-started/quick-start/">Quick Start Guide</a> has a 8 - step-by-step guide to getting started! 7 + {{ tr("homepage-quick-start-text") | safe }} 9 8 </p> 10 9 </div> 11 10 </section> 12 11 13 12 <section class="section"> 14 13 <div class="container"> 15 - <h2 class="title is-2">Recently Updated Events</h2> 14 + <h2 class="title is-2">{{ tr("homepage-recent-events") }}</h2> 16 15 {% if error_message %} 17 16 18 17 <article class="message is-danger"> ··· 23 22 24 23 {% endif %} 25 24 26 - {% include 'event_list.en-us.incl.html' %} 25 + {% include 'event_list.' + current_locale() + '.incl.html' %} 27 26 28 27 {% if pagination %} 29 28 {{ view_pagination((canonical_url ~ "?"), pagination) }}
+8 -8
templates/index.en-us.html
··· 1 - {% extends "base.en-us.html" %} 2 - {% block title %}Smoke Signal{% endblock %} 1 + {% extends "base." + current_locale() + ".html" %} 2 + {% block title %}{{ tr("homepage-title") }}{% endblock %} 3 3 {% block head %} 4 - <meta name="description" content="Smoke Signal is an event and RSVP management system."> 5 - <meta property="og:title" content="Smoke Signal"> 6 - <meta property="og:description" content="Smoke Signal is an event and RSVP management system."> 7 - <meta property="og:site_name" content="Smoke Signal" /> 4 + <meta name="description" content="{{ tr("homepage-description") }}"> 5 + <meta property="og:title" content="{{ tr("homepage-title") }}"> 6 + <meta property="og:description" content="{{ tr("homepage-description") }}"> 7 + <meta property="og:site_name" content="{{ tr("homepage-title") }}" /> 8 8 <meta property="og:type" content="website" /> 9 9 <meta property="og:url" content="https://smokesignal.events/" /> 10 10 <script type="application/ld+json"> 11 - { "@context" : "https://schema.org", "@type" : "Organization", "url" : "https://smokesignal.events/", "name": "Smoke Signal" } 11 + { "@context" : "https://schema.org", "@type" : "Organization", "url" : "https://smokesignal.events/", "name": "{{ tr("homepage-title") }}" } 12 12 </script> 13 13 {% endblock %} 14 14 {% block content %} 15 - {% include 'index.en-us.common.html' %} 15 + {% include 'index.' + current_locale() + '.common.html' %} 16 16 {% endblock %}
+31
templates/index.fr-ca.common.html
··· 1 + {%- from "pagination.html" import view_pagination -%} 2 + <section class="section"> 3 + <div class="container"> 4 + <h1 class="title is-1">{{ tr("homepage-title") }}</h1> 5 + <h2 class="subtitle">{{ tr("homepage-subtitle") }}</h2> 6 + <p class="content"> 7 + {{ tr("homepage-quick-start-text") | safe }} 8 + </p> 9 + </div> 10 + </section> 11 + 12 + <section class="section"> 13 + <div class="container"> 14 + <h2 class="title is-2">{{ tr("homepage-recent-events") }}</h2> 15 + {% if error_message %} 16 + 17 + <article class="message is-danger"> 18 + <div class="message-body"> 19 + <p>{{ error_message }}</p> 20 + </div> 21 + </article> 22 + 23 + {% endif %} 24 + 25 + {% include 'event_list.' + current_locale() + '.incl.html' %} 26 + 27 + {% if pagination %} 28 + {{ view_pagination((canonical_url ~ "?"), pagination) }} 29 + {% endif %} 30 + </div> 31 + </section>
+16
templates/index.fr-ca.html
··· 1 + {% extends "base." + current_locale() + ".html" %} 2 + {% block title %}{{ tr("homepage-title") }}{% endblock %} 3 + {% block head %} 4 + <meta name="description" content="{{ tr("homepage-description") }}"> 5 + <meta property="og:title" content="{{ tr("homepage-title") }}"> 6 + <meta property="og:description" content="{{ tr("homepage-description") }}"> 7 + <meta property="og:site_name" content="{{ tr("homepage-title") }}" /> 8 + <meta property="og:type" content="website" /> 9 + <meta property="og:url" content="https://smokesignal.events/" /> 10 + <script type="application/ld+json"> 11 + { "@context" : "https://schema.org", "@type" : "Organization", "url" : "https://smokesignal.events/", "name": "{{ tr("homepage-title") }}" } 12 + </script> 13 + {% endblock %} 14 + {% block content %} 15 + {% include 'index.' + current_locale() + '.common.html' %} 16 + {% endblock %}
+9 -9
templates/nav.en-us.html
··· 4 4 5 5 <div class="navbar-brand"> 6 6 <a class="navbar-item" href="/" hx-boost="true"> 7 - <img src="/static/logo-160x160.png" alt="Smoke Signal" height="160" /> 8 - Smoke Signal 7 + <img src="/static/logo-160x160.png" alt="{{ tr("nav-logo-alt") }}" height="160" /> 8 + {{ tr("nav-logo-alt") }} 9 9 </a> 10 10 11 11 <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" ··· 20 20 <div id="navbarMenuMain" class="navbar-menu"> 21 21 <div class="navbar-start"> 22 22 <a class="navbar-item" href="/" hx-boost="true"> 23 - Home 23 + {{ tr("nav-home") }} 24 24 </a> 25 25 <a class="navbar-item" href="/"> 26 - Help 26 + {{ tr("nav-help") }} 27 27 </a> 28 28 <a class="navbar-item" href="/"> 29 - Blog 29 + {{ tr("nav-blog") }} 30 30 </a> 31 31 </div> 32 32 ··· 38 38 <span class="icon"> 39 39 <i class="fas fa-calendar-plus"></i> 40 40 </span> 41 - <span>Add Event</span> 41 + <span>{{ tr("nav-add-event") }}</span> 42 42 </a> 43 43 <a class="button is-link" href="/{{ current_handle.did }}" hx-boost="true"> 44 44 <span class="icon"> 45 45 <i class="fas fa-user"></i> 46 46 </span> 47 - <span>Your Profile</span> 47 + <span>{{ tr("nav-your-profile") }}</span> 48 48 </a> 49 49 <a class="button is-danger is-light" 50 - href="/logout">Log out</a> 50 + href="/logout">{{ tr("nav-logout") }}</a> 51 51 {% else %} 52 - <a class="button is-primary" href="/oauth/login" hx-boost="true">Log in</a> 52 + <a class="button is-primary" href="/oauth/login" hx-boost="true">{{ tr("nav-login") }}</a> 53 53 {% endif %} 54 54 </div> 55 55 </div>
+60
templates/nav.fr-ca.html
··· 1 + <section class="section py-0"> 2 + <div class="container"> 3 + <nav class="navbar is-transparent" role="navigation" aria-label="navigation principale déroulante"> 4 + 5 + <div class="navbar-brand"> 6 + <a class="navbar-item" href="/" hx-boost="true"> 7 + <img src="/static/logo-160x160.png" alt="{{ tr("nav-logo-alt") }}" height="160" /> 8 + {{ tr("nav-logo-alt") }} 9 + </a> 10 + 11 + <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" 12 + data-target="navbarMenuMain"> 13 + <span aria-hidden="true"></span> 14 + <span aria-hidden="true"></span> 15 + <span aria-hidden="true"></span> 16 + <span aria-hidden="true"></span> 17 + </a> 18 + </div> 19 + 20 + <div id="navbarMenuMain" class="navbar-menu"> 21 + <div class="navbar-start"> 22 + <a class="navbar-item" href="/" hx-boost="true"> 23 + {{ tr("nav-home") }} 24 + </a> 25 + <a class="navbar-item" href="/"> 26 + {{ tr("nav-help") }} 27 + </a> 28 + <a class="navbar-item" href="/"> 29 + {{ tr("nav-blog") }} 30 + </a> 31 + </div> 32 + 33 + <div class="navbar-end"> 34 + <div class="navbar-item"> 35 + <div class="buttons"> 36 + {% if current_handle %} 37 + <a class="button is-primary" href="/event" hx-boost="true"> 38 + <span class="icon"> 39 + <i class="fas fa-calendar-plus"></i> 40 + </span> 41 + <span>{{ tr("nav-add-event") }}</span> 42 + </a> 43 + <a class="button is-link" href="/{{ current_handle.did }}" hx-boost="true"> 44 + <span class="icon"> 45 + <i class="fas fa-user"></i> 46 + </span> 47 + <span>{{ tr("nav-your-profile") }}</span> 48 + </a> 49 + <a class="button is-danger is-light" 50 + href="/logout">{{ tr("nav-logout") }}</a> 51 + {% else %} 52 + <a class="button is-primary" href="/oauth/login" hx-boost="true">{{ tr("nav-login") }}</a> 53 + {% endif %} 54 + </div> 55 + </div> 56 + </div> 57 + </div> 58 + </nav> 59 + </div> 60 + </section>