diff --git a/public/js/docs-app.js b/public/js/docs-app.js index 0411a0d8..9aa6b098 100644 --- a/public/js/docs-app.js +++ b/public/js/docs-app.js @@ -3,6 +3,192 @@ let currentDocument = null; let documentCards = null; let currentLanguage = 'en'; // Default language +// UI translations for full page i18n +const UI_TRANSLATIONS = { + en: { + pageTitle: 'Framework Documentation', + pageSubtitle: 'Technical specifications, guides, and reference materials', + documentsHeading: 'Documents', + searchButton: 'Search', + backToDocuments: 'Back to Documents', + selectDocument: 'Select a Document', + selectDocumentDesc: 'Choose a document from the sidebar to begin reading', + loadingDocument: 'Loading document...', + errorLoadingDoc: 'Error loading document', + tableOfContents: 'Table of Contents', + downloadPdf: 'Download PDF', + github: 'GitHub', + publicRepository: 'Public Repository', + publicRepositoryDesc: 'Source code, examples & contributions', + readmeQuickStart: 'README & Quick Start', + readmeQuickStartDesc: 'Installation and getting started guide', + categories: { + 'getting-started': 'Getting Started', + 'resources': 'Resources', + 'research-theory': 'Research & Theory', + 'technical-reference': 'Technical Reference', + 'advanced-topics': 'Advanced Topics', + 'business-leadership': 'Business & Leadership' + } + }, + de: { + pageTitle: 'Framework-Dokumentation', + pageSubtitle: 'Technische Spezifikationen, Leitfäden und Referenzmaterialien', + documentsHeading: 'Dokumente', + searchButton: 'Suchen', + backToDocuments: 'Zurück zu Dokumenten', + selectDocument: 'Dokument auswählen', + selectDocumentDesc: 'Wählen Sie ein Dokument aus der Seitenleiste, um mit dem Lesen zu beginnen', + loadingDocument: 'Dokument wird geladen...', + errorLoadingDoc: 'Fehler beim Laden des Dokuments', + tableOfContents: 'Inhaltsverzeichnis', + downloadPdf: 'PDF herunterladen', + github: 'GitHub', + publicRepository: 'Öffentliches Repository', + publicRepositoryDesc: 'Quellcode, Beispiele und Beiträge', + readmeQuickStart: 'README & Schnellstart', + readmeQuickStartDesc: 'Installation und Einstiegsanleitung', + categories: { + 'getting-started': 'Erste Schritte', + 'resources': 'Ressourcen', + 'research-theory': 'Forschung & Theorie', + 'technical-reference': 'Technische Referenz', + 'advanced-topics': 'Fortgeschrittene Themen', + 'business-leadership': 'Business & Führung' + } + }, + fr: { + pageTitle: 'Documentation du Framework', + pageSubtitle: 'Spécifications techniques, guides et matériels de référence', + documentsHeading: 'Documents', + searchButton: 'Rechercher', + backToDocuments: 'Retour aux documents', + selectDocument: 'Sélectionner un document', + selectDocumentDesc: 'Choisissez un document dans la barre latérale pour commencer la lecture', + loadingDocument: 'Chargement du document...', + errorLoadingDoc: 'Erreur lors du chargement du document', + tableOfContents: 'Table des matières', + downloadPdf: 'Télécharger PDF', + github: 'GitHub', + publicRepository: 'Dépôt public', + publicRepositoryDesc: 'Code source, exemples et contributions', + readmeQuickStart: 'README & Démarrage rapide', + readmeQuickStartDesc: 'Guide d\'installation et de démarrage', + categories: { + 'getting-started': 'Premiers pas', + 'resources': 'Ressources', + 'research-theory': 'Recherche & Théorie', + 'technical-reference': 'Référence technique', + 'advanced-topics': 'Sujets avancés', + 'business-leadership': 'Business & Leadership' + } + } +}; + +// Get current UI translations +function getUITranslations(lang = currentLanguage) { + return UI_TRANSLATIONS[lang] || UI_TRANSLATIONS.en; +} + +// Update all page UI elements with current language translations +function updatePageUI(lang = currentLanguage) { + const t = getUITranslations(lang); + + // Update page header + const pageTitle = document.querySelector('h1'); + if (pageTitle && pageTitle.textContent === UI_TRANSLATIONS.en.pageTitle || + pageTitle.textContent === UI_TRANSLATIONS.de.pageTitle || + pageTitle.textContent === UI_TRANSLATIONS.fr.pageTitle) { + pageTitle.textContent = t.pageTitle; + } + + const pageSubtitle = document.querySelector('.text-gray-600.mt-2'); + if (pageSubtitle && (pageSubtitle.textContent === UI_TRANSLATIONS.en.pageSubtitle || + pageSubtitle.textContent === UI_TRANSLATIONS.de.pageSubtitle || + pageSubtitle.textContent === UI_TRANSLATIONS.fr.pageSubtitle)) { + pageSubtitle.textContent = t.pageSubtitle; + } + + // Update search button + const searchBtn = document.querySelector('#open-search-modal-btn span'); + if (searchBtn) { + searchBtn.textContent = t.searchButton; + } + + // Update sidebar Documents heading + const docsHeading = document.querySelector('aside h3'); + if (docsHeading && (docsHeading.textContent === UI_TRANSLATIONS.en.documentsHeading || + docsHeading.textContent === UI_TRANSLATIONS.de.documentsHeading || + docsHeading.textContent === UI_TRANSLATIONS.fr.documentsHeading)) { + docsHeading.textContent = t.documentsHeading; + } + + // Update GitHub section heading + const githubHeadings = document.querySelectorAll('aside h3'); + githubHeadings.forEach(heading => { + if (heading.textContent.trim() === UI_TRANSLATIONS.en.github || + heading.textContent.trim() === UI_TRANSLATIONS.de.github || + heading.textContent.trim() === UI_TRANSLATIONS.fr.github) { + // Keep the SVG icon, just update text + const textNode = Array.from(heading.childNodes).find(node => node.nodeType === Node.TEXT_NODE); + if (textNode) { + textNode.textContent = t.github; + } + } + }); + + // Update GitHub links + const githubLinks = document.querySelectorAll('aside a[href*="github.com"]'); + githubLinks.forEach(link => { + const titleDiv = link.querySelector('.text-sm.font-medium'); + const descDiv = link.querySelector('.text-xs.text-gray-500'); + + if (titleDiv) { + if (titleDiv.textContent === UI_TRANSLATIONS.en.publicRepository || + titleDiv.textContent === UI_TRANSLATIONS.de.publicRepository || + titleDiv.textContent === UI_TRANSLATIONS.fr.publicRepository) { + titleDiv.textContent = t.publicRepository; + } else if (titleDiv.textContent === UI_TRANSLATIONS.en.readmeQuickStart || + titleDiv.textContent === UI_TRANSLATIONS.de.readmeQuickStart || + titleDiv.textContent === UI_TRANSLATIONS.fr.readmeQuickStart) { + titleDiv.textContent = t.readmeQuickStart; + } + } + + if (descDiv) { + if (descDiv.textContent === UI_TRANSLATIONS.en.publicRepositoryDesc || + descDiv.textContent === UI_TRANSLATIONS.de.publicRepositoryDesc || + descDiv.textContent === UI_TRANSLATIONS.fr.publicRepositoryDesc) { + descDiv.textContent = t.publicRepositoryDesc; + } else if (descDiv.textContent === UI_TRANSLATIONS.en.readmeQuickStartDesc || + descDiv.textContent === UI_TRANSLATIONS.de.readmeQuickStartDesc || + descDiv.textContent === UI_TRANSLATIONS.fr.readmeQuickStartDesc) { + descDiv.textContent = t.readmeQuickStartDesc; + } + } + }); + + // Update Back to Documents button + const backBtn = document.querySelector('#back-to-docs-btn span'); + if (backBtn) { + backBtn.textContent = t.backToDocuments; + } + + // Update Select Document placeholder (if visible) + const selectDocHeading = document.getElementById('select-document-heading'); + if (selectDocHeading) { + selectDocHeading.textContent = t.selectDocument; + } + + const selectDocDesc = document.getElementById('select-document-desc'); + if (selectDocDesc) { + selectDocDesc.textContent = t.selectDocumentDesc; + } + + // Update page title tag + document.title = `${t.pageTitle} | Tractatus AI Safety`; +} + // Initialize card-based viewer if (typeof DocumentCards !== 'undefined') { documentCards = new DocumentCards('document-content'); @@ -65,7 +251,6 @@ if (typeof window !== 'undefined') { // Document categorization - Final 5 categories (curated for public docs) const CATEGORIES = { 'getting-started': { - label: '📚 Getting Started', icon: '📚', description: 'Introduction, core concepts, and glossary', order: 1, @@ -76,7 +261,6 @@ const CATEGORIES = { collapsed: false }, 'resources': { - label: '📖 Resources', icon: '📖', description: 'Implementation guides and reference materials', order: 2, @@ -87,7 +271,6 @@ const CATEGORIES = { collapsed: false }, 'research-theory': { - label: '🔬 Research & Theory', icon: '🔬', description: 'Research papers, case studies, theoretical foundations', order: 3, @@ -98,7 +281,6 @@ const CATEGORIES = { collapsed: true }, 'technical-reference': { - label: '🔌 Technical Reference', icon: '🔌', description: 'API documentation, code examples, architecture', order: 4, @@ -109,7 +291,6 @@ const CATEGORIES = { collapsed: true }, 'advanced-topics': { - label: '🎓 Advanced Topics', icon: '🎓', description: 'Value pluralism, organizational theory, advanced concepts', order: 5, @@ -120,7 +301,6 @@ const CATEGORIES = { collapsed: true }, 'business-leadership': { - label: '💼 Business & Leadership', icon: '💼', description: 'Business cases, ROI analysis, executive briefs', order: 6, @@ -188,6 +368,12 @@ function groupDocuments(docs) { function renderDocLink(doc, isHighlighted = false) { const highlightClass = isHighlighted ? 'text-blue-700 bg-blue-50 border border-blue-200' : ''; + // Get translated title if available and language is not English + let displayTitle = doc.title; + if (currentLanguage !== 'en' && doc.translations && doc.translations[currentLanguage]) { + displayTitle = doc.translations[currentLanguage].title || doc.title; + } + // Determine if PDF download is available and get PDF path // First check if document has explicit download_formats.pdf let pdfPath = null; @@ -213,7 +399,7 @@ function renderDocLink(doc, isHighlighted = false) {
${hasPDF ? ` a[1].order - b[1].order); + const t = getUITranslations(currentLanguage); + sortedCategories.forEach(([categoryId, category]) => { const docs = grouped[categoryId] || []; if (docs.length === 0) return; const isCollapsed = category.collapsed || false; + const categoryLabel = t.categories[categoryId] || categoryId; // Category header html += ` @@ -267,7 +456,7 @@ async function loadDocuments() { data-collapsed="${isCollapsed}"> ${category.icon} - ${category.label.replace(category.icon, '').trim()} + ${categoryLabel} @@ -420,6 +609,8 @@ async function loadDocument(slug, lang = null) { try { isLoading = true; + const t = getUITranslations(language); + // Show loading state const contentEl = document.getElementById('document-content'); contentEl.innerHTML = ` @@ -428,7 +619,7 @@ async function loadDocument(slug, lang = null) { -

Loading document...

+

${t.loadingDocument}

`; @@ -566,12 +757,13 @@ async function loadDocument(slug, lang = null) { } catch (error) { console.error('Error loading document:', error); + const t = getUITranslations(currentLanguage); document.getElementById('document-content').innerHTML = `
-

Error loading document

+

${t.errorLoadingDoc}

${error.message}

`; @@ -641,7 +833,7 @@ function closeToCModal() { // Initialize loadDocuments(); -// Initialize language selector +// Initialize language selector and detect initial language (function initLanguageSelector() { const selector = document.getElementById('language-selector'); if (!selector) return; @@ -651,8 +843,11 @@ loadDocuments(); selector.value = initialLang; currentLanguage = initialLang; + // Update page UI with initial language + updatePageUI(initialLang); + // Handle language change - selector.addEventListener('change', (e) => { + selector.addEventListener('change', async (e) => { const newLang = e.target.value; // Save to localStorage @@ -661,16 +856,16 @@ loadDocuments(); // Update current language currentLanguage = newLang; - // Reload current document in new language - if (currentDocument) { - loadDocument(currentDocument.slug, newLang); - } else { - // If no document loaded yet, just update URL - const urlParams = new URLSearchParams(window.location.search); - const currentDoc = urlParams.get('doc'); - if (currentDoc) { - loadDocument(currentDoc, newLang); - } + // Update all page UI elements + updatePageUI(newLang); + + // Reload document list to show translated category labels and document titles + const currentSlug = currentDocument ? currentDocument.slug : null; + await loadDocuments(); + + // If a document was loaded, reload it in the new language + if (currentSlug) { + loadDocument(currentSlug, newLang); } }); })();