Ver código fonte

Initial translation support

svalavuo 6 dias atrás
pai
commit
2f40fc04d8
6 arquivos alterados com 615 adições e 17 exclusões
  1. 39 0
      css/style.css
  2. 139 0
      includes/translation.php
  3. 199 0
      languages/en.php
  4. 199 0
      languages/fi.php
  5. 25 17
      public/index.php
  6. 14 0
      setup/setup.php

+ 39 - 0
css/style.css

@@ -784,6 +784,39 @@ textarea {
     display: block;
 }
 
+/* Language Switcher */
+.language-switcher {
+    display: flex;
+    align-items: center;
+    gap: 0.5rem;
+    font-size: 0.875rem;
+    margin-left: auto;
+}
+
+.language-label {
+    color: #6c757d;
+    font-weight: 500;
+}
+
+.language-link {
+    padding: 0.25rem 0.5rem;
+    border-radius: 0.25rem;
+    text-decoration: none;
+    color: #6c757d;
+    transition: all 0.2s ease;
+}
+
+.language-link:hover {
+    background-color: #f8f9fa;
+    color: #495057;
+}
+
+.language-link.active {
+    background-color: #007bff;
+    color: white;
+    font-weight: 500;
+}
+
 /* Responsive Design */
 @media (max-width: 768px) {
     .content-layout {
@@ -831,6 +864,12 @@ textarea {
     .setup-form {
         padding: 1rem;
     }
+    
+    .language-switcher {
+        margin-left: 0;
+        margin-top: 0.5rem;
+        justify-content: center;
+    }
 }
 
 @media (max-width: 480px) {

+ 139 - 0
includes/translation.php

@@ -0,0 +1,139 @@
+<?php
+/**
+ * Translation System
+ * Handles multi-language support for Finnish and English
+ */
+
+class Translation {
+    private static $instance = null;
+    private $translations = [];
+    private $currentLanguage = 'en';
+    private $defaultLanguage = 'en';
+    private $supportedLanguages = ['en' => 'English', 'fi' => 'Finnish'];
+    
+    private function __construct() {
+        $this->detectLanguage();
+        $this->loadTranslations();
+    }
+    
+    public static function getInstance() {
+        if (self::$instance === null) {
+            self::$instance = new self();
+        }
+        return self::$instance;
+    }
+    
+    private function detectLanguage() {
+        // Check if language is set in session
+        if (isset($_SESSION['language']) && isset($this->supportedLanguages[$_SESSION['language']])) {
+            $this->currentLanguage = $_SESSION['language'];
+            return;
+        }
+        
+        // Check URL parameter
+        if (isset($_GET['lang']) && isset($this->supportedLanguages[$_GET['lang']])) {
+            $this->currentLanguage = $_GET['lang'];
+            $_SESSION['language'] = $this->currentLanguage;
+            return;
+        }
+        
+        // Check browser language preference
+        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+            $browserLang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
+            if (isset($this->supportedLanguages[$browserLang])) {
+                $this->currentLanguage = $browserLang;
+                $_SESSION['language'] = $this->currentLanguage;
+                return;
+            }
+        }
+        
+        // Check configuration default
+        if (defined('DEFAULT_LANGUAGE') && isset($this->supportedLanguages[DEFAULT_LANGUAGE])) {
+            $this->currentLanguage = DEFAULT_LANGUAGE;
+            return;
+        }
+        
+        // Fall back to default language
+        $this->currentLanguage = $this->defaultLanguage;
+    }
+    
+    private function loadTranslations() {
+        $langFile = __DIR__ . '/../languages/' . $this->currentLanguage . '.php';
+        
+        if (file_exists($langFile)) {
+            $this->translations = include $langFile;
+        } else {
+            // Fall back to default language if current language file doesn't exist
+            $defaultLangFile = __DIR__ . '/../languages/' . $this->defaultLanguage . '.php';
+            if (file_exists($defaultLangFile)) {
+                $this->translations = include $defaultLangFile;
+            }
+        }
+    }
+    
+    public function translate($key, $params = []) {
+        if (isset($this->translations[$key])) {
+            $translation = $this->translations[$key];
+            
+            // Replace parameters in translation
+            foreach ($params as $param => $value) {
+                $translation = str_replace(':' . $param, $value, $translation);
+            }
+            
+            return $translation;
+        }
+        
+        // Return key if translation not found
+        return $key;
+    }
+    
+    public function getCurrentLanguage() {
+        return $this->currentLanguage;
+    }
+    
+    public function getSupportedLanguages() {
+        return $this->supportedLanguages;
+    }
+    
+    public function setLanguage($language) {
+        if (isset($this->supportedLanguages[$language])) {
+            $this->currentLanguage = $language;
+            $_SESSION['language'] = $language;
+            $this->loadTranslations();
+            return true;
+        }
+        return false;
+    }
+    
+    public function getLanguageSwitcher($currentPath = '') {
+        $switcher = '<div class="language-switcher">';
+        $switcher .= '<span class="language-label">' . $this->translate('language') . ':</span>';
+        
+        foreach ($this->supportedLanguages as $code => $name) {
+            $url = $currentPath;
+            $separator = strpos($url, '?') !== false ? '&' : '?';
+            $switcherUrl = $url . $separator . 'lang=' . $code;
+            
+            $class = $code === $this->currentLanguage ? 'active' : '';
+            $switcher .= '<a href="' . htmlspecialchars($switcherUrl) . '" class="language-link ' . $class . '">' . $name . '</a>';
+        }
+        
+        $switcher .= '</div>';
+        return $switcher;
+    }
+}
+
+// Global translation function for convenience
+function t($key, $params = []) {
+    return Translation::getInstance()->translate($key, $params);
+}
+
+// Get current language
+function getCurrentLanguage() {
+    return Translation::getInstance()->getCurrentLanguage();
+}
+
+// Get supported languages
+function getSupportedLanguages() {
+    return Translation::getInstance()->getSupportedLanguages();
+}

+ 199 - 0
languages/en.php

@@ -0,0 +1,199 @@
+<?php
+/**
+ * English Translations
+ */
+
+return [
+    // General
+    'language' => 'Language',
+    'home' => 'Home',
+    'search' => 'Search',
+    'categories' => 'Categories',
+    'login' => 'Login',
+    'logout' => 'Logout',
+    'welcome' => 'Welcome',
+    'back' => 'Back',
+    'cancel' => 'Cancel',
+    'save' => 'Save',
+    'delete' => 'Delete',
+    'edit' => 'Edit',
+    'create' => 'Create',
+    'update' => 'Update',
+    'view' => 'View',
+    'actions' => 'Actions',
+    'status' => 'Status',
+    'author' => 'Author',
+    'date' => 'Date',
+    'title' => 'Title',
+    'content' => 'Content',
+    'summary' => 'Summary',
+    'published' => 'Published',
+    'draft' => 'Draft',
+    'archived' => 'Archived',
+    
+    // Navigation
+    'nav_home' => 'Home',
+    'nav_categories' => 'Categories',
+    'nav_search' => 'Search',
+    'nav_admin' => 'Admin',
+    'nav_dashboard' => 'Dashboard',
+    'nav_publications' => 'Publications',
+    'nav_ldap_users' => 'LDAP Users',
+    
+    // Public Site
+    'site_title' => 'Web Publication System',
+    'search_publications' => 'Search for publications...',
+    'search_placeholder' => 'Search publications...',
+    'no_publications_found' => 'No publications found.',
+    'by_author' => 'By :author',
+    'on_date' => 'on :date',
+    'read_more' => 'Read more',
+    'related_publications' => 'Related Publications',
+    'publication_not_found' => 'Publication not found',
+    'publication_not_found_desc' => 'The requested publication could not be found.',
+    'all_categories' => 'All Categories',
+    'category_overview' => 'Category Overview',
+    'publications_in_category' => 'Publications in :category',
+    'no_publications_category' => 'No publications found in this category.',
+    'publication_count' => 'publication(s)',
+    'total_categories' => 'Total Categories:',
+    'total_publications' => 'Total Publications:',
+    'view_publications' => 'View Publications',
+    'created' => 'Created :date',
+    'results_count' => ':count publications in this category',
+    
+    // Search
+    'search_results' => 'Search Results',
+    'search_query' => 'Search for',
+    'no_search_results' => 'No publications found matching your search.',
+    'search_for' => 'Search for',
+    
+    // Admin Interface
+    'admin_login' => 'Admin Login',
+    'admin_dashboard' => 'Admin Dashboard',
+    'admin_panel' => 'Admin Panel',
+    'login_required' => 'Login required',
+    'invalid_credentials' => 'Invalid username or password',
+    'username_required' => 'Username is required',
+    'password_required' => 'Password is required',
+    'enter_username_password' => 'Please enter both username and password',
+    
+    // Publications Management
+    'manage_publications' => 'Publications',
+    'create_publication' => 'Create New Publication',
+    'edit_publication' => 'Edit Publication',
+    'publication_title' => 'Publication Title',
+    'publication_content' => 'Publication Content',
+    'publication_summary' => 'Publication Summary',
+    'publication_status' => 'Publication Status',
+    'publication_categories' => 'Categories',
+    'title_required' => 'Title is required',
+    'content_required' => 'Content is required',
+    'author_required' => 'Author is required',
+    'select_categories' => 'Select categories',
+    'publication_created' => 'Publication created successfully',
+    'publication_updated' => 'Publication updated successfully',
+    'publication_deleted' => 'Publication deleted successfully',
+    'error_deleting_publication' => 'Error deleting publication: :error',
+    'delete_publication_confirm' => 'Are you sure you want to delete this publication?',
+    
+    // Categories Management
+    'manage_categories' => 'Categories',
+    'create_category' => 'Create New Category',
+    'edit_category' => 'Edit Category',
+    'category_name' => 'Category Name',
+    'category_description' => 'Category Description',
+    'category_name_required' => 'Category name is required',
+    'category_exists' => 'Category name already exists',
+    'category_created' => 'Category created successfully',
+    'category_updated' => 'Category updated successfully',
+    'category_deleted' => 'Category deleted successfully',
+    'error_deleting_category' => 'Error deleting category: :error',
+    'cannot_delete_category' => 'Cannot delete category with associated publications',
+    'delete_category_confirm' => 'Are you sure you want to delete this category?',
+    'existing_categories' => 'Existing Categories',
+    'no_categories_found' => 'No categories found. Create your first category above.',
+    
+    // LDAP
+    'ldap_authentication' => 'LDAP Authentication',
+    'ldap_enabled' => 'LDAP Authentication Enabled',
+    'ldap_notice' => 'Please login with your directory credentials.',
+    'directory_username' => 'Directory Username',
+    'directory_password' => 'Directory Password',
+    'username' => 'Username',
+    'password' => 'Password',
+    'enter_directory_username' => 'Enter your directory username',
+    'enter_directory_password' => 'Enter your directory password',
+    'ldap_users' => 'LDAP Users',
+    'search_ldap_users' => 'Search LDAP users...',
+    'ldap_connection_ok' => 'LDAP connection: OK',
+    'ldap_connection_failed' => 'LDAP connection: Failed',
+    'import_selected_users' => 'Import Selected Users',
+    'import_user' => 'Import',
+    'already_imported' => 'Already Imported',
+    'users_imported' => ':count users imported successfully.',
+    'no_ldap_users_found' => 'No users found matching ":query"',
+    'enter_search_query' => 'Enter a search query to find LDAP users available for import.',
+    
+    // Setup
+    'setup_wizard' => 'Setup Wizard',
+    'database_configuration' => 'Database Configuration',
+    'site_configuration' => 'Site Configuration',
+    'authentication_configuration' => 'Authentication Configuration',
+    'enable_ldap' => 'Enable LDAP Authentication',
+    'ldap_help_text' => 'When enabled, users will authenticate against LDAP directory instead of local database.',
+    'ldap_configuration' => 'LDAP Configuration',
+    'admin_configuration' => 'Admin Configuration',
+    'admin_account' => 'Admin Account',
+    'setup_complete' => 'Setup Complete',
+    'installation_complete' => 'Installation completed successfully!',
+    'access_admin_panel' => 'Access Admin Panel',
+    'access_public_site' => 'Access Public Site',
+    'delete_setup_directory' => 'Important: Delete the setup directory for security',
+    'delete_setup_instructions' => 'Please remove the setup/ directory from your web server to ensure security.',
+    
+    // Forms
+    'database_host' => 'Database Host',
+    'database_name' => 'Database Name',
+    'database_username' => 'Database Username',
+    'database_password' => 'Database Password',
+    'site_title' => 'Site Title',
+    'admin_username' => 'Admin Username',
+    'admin_password' => 'Admin Password',
+    'admin_email' => 'Admin Email',
+    'email_required' => 'Email is required',
+    'invalid_email' => 'Invalid email format',
+    'admin_password_required' => 'Admin password is required when LDAP is disabled',
+    
+    // LDAP Setup
+    'ldap_server_host' => 'LDAP Server Host',
+    'ldap_port' => 'LDAP Port',
+    'ldap_base_dn' => 'LDAP Base DN',
+    'ldap_user_filter' => 'User Search Filter',
+    'ldap_bind_dn' => 'Bind DN',
+    'ldap_bind_password' => 'Bind Password',
+    'ldap_email_attribute' => 'Email Attribute',
+    'ldap_name_attribute' => 'Name Attribute',
+    'ldap_host_required' => 'LDAP host is required when LDAP is enabled',
+    'ldap_base_dn_required' => 'LDAP base DN is required when LDAP is enabled',
+    
+    // Footer
+    'copyright' => '&copy; :year :site. All rights reserved.',
+    
+    // Pagination
+    'previous' => 'Previous',
+    'next' => 'Next',
+    'page' => 'Page',
+    'of' => 'of',
+    
+    // Messages
+    'success' => 'Success',
+    'error' => 'Error',
+    'warning' => 'Warning',
+    'info' => 'Information',
+    
+    // Additional keys for index page
+    'found_count' => 'Found :count publications',
+    'latest_publications' => 'Latest Publications',
+    'category' => 'Category',
+];

+ 199 - 0
languages/fi.php

@@ -0,0 +1,199 @@
+<?php
+/**
+ * Finnish Translations
+ */
+
+return [
+    // General
+    'language' => 'Kieli',
+    'home' => 'Etusivu',
+    'search' => 'Haku',
+    'categories' => 'Kategoriat',
+    'login' => 'Kirjaudu sisään',
+    'logout' => 'Kirjaudu ulos',
+    'welcome' => 'Tervetuloa',
+    'back' => 'Takaisin',
+    'cancel' => 'Peruuta',
+    'save' => 'Tallenna',
+    'delete' => 'Poista',
+    'edit' => 'Muokkaa',
+    'create' => 'Luo',
+    'update' => 'Päivitä',
+    'view' => 'Näytä',
+    'actions' => 'Toiminnot',
+    'status' => 'Tila',
+    'author' => 'Tekijä',
+    'date' => 'Päivämäärä',
+    'title' => 'Otsikko',
+    'content' => 'Sisältö',
+    'summary' => 'Yhteenveto',
+    'published' => 'Julkaistu',
+    'draft' => 'Luonnos',
+    'archived' => 'Arkistoitu',
+    
+    // Navigation
+    'nav_home' => 'Etusivu',
+    'nav_categories' => 'Kategoriat',
+    'nav_search' => 'Haku',
+    'nav_admin' => 'Ylläpito',
+    'nav_dashboard' => 'Kojelauta',
+    'nav_publications' => 'Julkaisut',
+    'nav_ldap_users' => 'LDAP-käyttäjät',
+    
+    // Public Site
+    'site_title' => 'Verkkojulkaisujärjestelmä',
+    'search_publications' => 'Etsi julkaisuja...',
+    'search_placeholder' => 'Hae julkaisuja...',
+    'no_publications_found' => 'Julkaisuja ei löytynyt.',
+    'by_author' => 'Tekijä :author',
+    'on_date' => ':date',
+    'read_more' => 'Lue lisää',
+    'related_publications' => 'Aiheeseen liittyvät julkaisut',
+    'publication_not_found' => 'Julkaisua ei löytynyt',
+    'publication_not_found_desc' => 'Pyydettyä julkaisua ei löytynyt.',
+    'all_categories' => 'Kaikki kategoriat',
+    'category_overview' => 'Kategoria-yhteenveto',
+    'publications_in_category' => 'Julkaisut kategoriassa :category',
+    'no_publications_category' => 'Julkaisuja ei löytynyt tästä kategoriasta.',
+    'publication_count' => 'julkaisua',
+    'total_categories' => 'Kategorioita yhteensä:',
+    'total_publications' => 'Julkaisuja yhteensä:',
+    'view_publications' => 'Näytä julkaisut',
+    'created' => 'Luotu :date',
+    'results_count' => ':count julkaisua tässä kategoriassa',
+    
+    // Search
+    'search_results' => 'Hakutulokset',
+    'search_query' => 'Hakusana',
+    'no_search_results' => 'Hakua vastaavia julkaisuja ei löytynyt.',
+    'search_for' => 'Etsi',
+    
+    // Admin Interface
+    'admin_login' => 'Ylläpitokirjautuminen',
+    'admin_dashboard' => 'Ylläpitokojelauta',
+    'admin_panel' => 'Ylläpitopaneeli',
+    'login_required' => 'Kirjautuminen vaaditaan',
+    'invalid_credentials' => 'Virheellinen käyttäjätunnus tai salasana',
+    'username_required' => 'Käyttäjätunnus vaaditaan',
+    'password_required' => 'Salasana vaaditaan',
+    'enter_username_password' => 'Syötä sekä käyttäjätunnus että salasana',
+    
+    // Publications Management
+    'manage_publications' => 'Julkaisut',
+    'create_publication' => 'Luo uusi julkaisu',
+    'edit_publication' => 'Muokkaa julkaisua',
+    'publication_title' => 'Julkaisun otsikko',
+    'publication_content' => 'Julkaisun sisältö',
+    'publication_summary' => 'Julkaisun yhteenveto',
+    'publication_status' => 'Julkaisun tila',
+    'publication_categories' => 'Kategoriat',
+    'title_required' => 'Otsikko vaaditaan',
+    'content_required' => 'Sisältö vaaditaan',
+    'author_required' => 'Tekijä vaaditaan',
+    'select_categories' => 'Valitse kategoriat',
+    'publication_created' => 'Julkaisu luotu onnistuneesti',
+    'publication_updated' => 'Julkaisu päivitetty onnistuneesti',
+    'publication_deleted' => 'Julkaisu poistettu onnistuneesti',
+    'error_deleting_publication' => 'Virhe julkaisua poistettaessa: :error',
+    'delete_publication_confirm' => 'Oletko varma, että haluat poistaa tämän julkaisun?',
+    
+    // Categories Management
+    'manage_categories' => 'Kategoriat',
+    'create_category' => 'Luo uusi kategoria',
+    'edit_category' => 'Muokkaa kategoriaa',
+    'category_name' => 'Kategorian nimi',
+    'category_description' => 'Kategorian kuvaus',
+    'category_name_required' => 'Kategorian nimi vaaditaan',
+    'category_exists' => 'Kategorian nimi on jo olemassa',
+    'category_created' => 'Kategoria luotu onnistuneesti',
+    'category_updated' => 'Kategoria päivitetty onnistuneesti',
+    'category_deleted' => 'Kategoria poistettu onnistuneesti',
+    'error_deleting_category' => 'Virhe kategoriaa poistettaessa: :error',
+    'cannot_delete_category' => 'Kategorian poistaminen ei onnistu, koska siinä on julkaisuja',
+    'delete_category_confirm' => 'Oletko varma, että haluat poistaa tämän kategorian?',
+    'existing_categories' => 'Olemassa olevat kategoriat',
+    'no_categories_found' => 'Kategorioita ei löytynyt. Luo ensimmäinen kategoria yllä olevalla painikkeella.',
+    
+    // LDAP
+    'ldap_authentication' => 'LDAP-todennus',
+    'ldap_enabled' => 'LDAP-todennus käytössä',
+    'ldap_notice' => 'Kirjaudu sisään hakemistotunnuksillasi.',
+    'directory_username' => 'Hakemiston käyttäjätunnus',
+    'directory_password' => 'Hakemiston salasana',
+    'username' => 'Käyttäjätunnus',
+    'password' => 'Salasana',
+    'enter_directory_username' => 'Syötä hakemiston käyttäjätunnus',
+    'enter_directory_password' => 'Syötä hakemiston salasana',
+    'ldap_users' => 'LDAP-käyttäjät',
+    'search_ldap_users' => 'Etsi LDAP-käyttäjiä...',
+    'ldap_connection_ok' => 'LDAP-yhteys: OK',
+    'ldap_connection_failed' => 'LDAP-yhteys: Epäonnistui',
+    'import_selected_users' => 'Tuo valitut käyttäjät',
+    'import_user' => 'Tuo',
+    'already_imported' => 'Jo tuotu',
+    'users_imported' => ':count käyttäjää tuotu onnistuneesti.',
+    'no_ldap_users_found' => 'Käyttäjiä ei löytynyt haulle ":query"',
+    'enter_search_query' => 'Syötä hakusana löytääksesi LDAP-käyttäjiä tuotavaksi.',
+    
+    // Setup
+    'setup_wizard' => 'Asennusopas',
+    'database_configuration' => 'Tietokanta-asetukset',
+    'site_configuration' => 'Sivustoasetukset',
+    'authentication_configuration' => 'Todennusasetukset',
+    'enable_ldap' => 'Ota LDAP-todennus käyttöön',
+    'ldap_help_text' => 'Kun käytössä, käyttäjät todennetaan LDAP-hakemistoa vasten paikallisen tietokannan sijaan.',
+    'ldap_configuration' => 'LDAP-asetukset',
+    'admin_configuration' => 'Ylläpitäjän asetukset',
+    'admin_account' => 'Ylläpitäjätili',
+    'setup_complete' => 'Asennus valmis',
+    'installation_complete' => 'Asennus suoritettu onnistuneesti!',
+    'access_admin_panel' => 'Siirry ylläpitopaneeliin',
+    'access_public_site' => 'Siirry julkiselle sivustolle',
+    'delete_setup_directory' => 'Tärkeää: Poista asennushakemisto turvallisuuden vuoksi',
+    'delete_setup_instructions' => 'Poista setup/-hakemisto verkkopalvelimeltasi varmistaaksesi turvallisuuden.',
+    
+    // Forms
+    'database_host' => 'Tietokannan isäntä',
+    'database_name' => 'Tietokannan nimi',
+    'database_username' => 'Tietokannan käyttäjätunnus',
+    'database_password' => 'Tietokannan salasana',
+    'site_title' => 'Sivuston otsikko',
+    'admin_username' => 'Ylläpitäjän käyttäjätunnus',
+    'admin_password' => 'Ylläpitäjän salasana',
+    'admin_email' => 'Ylläpitäjän sähköposti',
+    'email_required' => 'Sähköposti vaaditaan',
+    'invalid_email' => 'Virheellinen sähköpostimuoto',
+    'admin_password_required' => 'Ylläpitäjän salasana vaaditaan, kun LDAP ei ole käytössä',
+    
+    // LDAP Setup
+    'ldap_server_host' => 'LDAP-palvelimen isäntä',
+    'ldap_port' => 'LDAP-portti',
+    'ldap_base_dn' => 'LDAP Base DN',
+    'ldap_user_filter' => 'Käyttäjän hakusuodatin',
+    'ldap_bind_dn' => 'Bind DN',
+    'ldap_bind_password' => 'Bind-salasana',
+    'ldap_email_attribute' => 'Sähköposti-attribuutti',
+    'ldap_name_attribute' => 'Nimi-attribuutti',
+    'ldap_host_required' => 'LDAP-isäntä vaaditaan, kun LDAP on käytössä',
+    'ldap_base_dn_required' => 'LDAP Base DN vaaditaan, kun LDAP on käytössä',
+    
+    // Footer
+    'copyright' => '&copy; :year :site. Kaikki oikeudet pidätetään.',
+    
+    // Pagination
+    'previous' => 'Edellinen',
+    'next' => 'Seuraava',
+    'page' => 'Sivu',
+    'of' => '/',
+    
+    // Messages
+    'success' => 'Onnistui',
+    'error' => 'Virhe',
+    'warning' => 'Varoitus',
+    'info' => 'Tieto',
+    
+    // Additional keys for index page
+    'found_count' => 'Löytyi :count julkaisua',
+    'latest_publications' => 'Viimeisimmät julkaisut',
+    'category' => 'Kategoria',
+];

+ 25 - 17
public/index.php

@@ -2,6 +2,13 @@
 require_once '../includes/config.php';
 require_once '../includes/database.php';
 require_once '../includes/publication.php';
+require_once '../includes/translation.php';
+
+// Start session for language preference
+session_start();
+
+// Initialize translation system
+$translation = Translation::getInstance();
 
 $publication = new Publication();
 
@@ -31,7 +38,7 @@ $categories = $publication->getCategories();
 $totalPages = ceil($totalPublications / $limit);
 ?>
 <!DOCTYPE html>
-<html lang="en">
+<html lang="<?php echo getCurrentLanguage(); ?>">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -43,10 +50,11 @@ $totalPages = ceil($totalPublications / $limit);
         <div class="container">
             <h1><a href="index.php"><?php echo SITE_TITLE; ?></a></h1>
             <nav class="main-nav">
-                <a href="index.php">Home</a>
-                <a href="categories.php">Categories</a>
-                <a href="search.php">Search</a>
+                <a href="index.php"><?php echo t('nav_home'); ?></a>
+                <a href="categories.php"><?php echo t('nav_categories'); ?></a>
+                <a href="search.php"><?php echo t('nav_search'); ?></a>
             </nav>
+            <?php echo $translation->getLanguageSwitcher('index.php'); ?>
         </div>
     </header>
 
@@ -54,20 +62,20 @@ $totalPages = ceil($totalPublications / $limit);
         <div class="content-layout">
             <aside class="sidebar">
                 <div class="sidebar-section">
-                    <h3>Search</h3>
+                    <h3><?php echo t('search'); ?></h3>
                     <form method="get" class="search-form">
-                        <input type="text" name="search" placeholder="Search publications..." 
+                        <input type="text" name="search" placeholder="<?php echo t('search_placeholder'); ?>" 
                                value="<?php echo htmlspecialchars($search); ?>">
-                        <button type="submit" class="btn btn-sm">Search</button>
+                        <button type="submit" class="btn btn-sm"><?php echo t('search'); ?></button>
                     </form>
                 </div>
 
                 <div class="sidebar-section">
-                    <h3>Categories</h3>
+                    <h3><?php echo t('categories'); ?></h3>
                     <ul class="category-list">
                         <li>
                             <a href="index.php" <?php echo empty($category) ? 'class="active"' : ''; ?>>
-                                All Categories
+                                <?php echo t('all_categories'); ?>
                             </a>
                         </li>
                         <?php foreach ($categories as $cat): ?>
@@ -85,17 +93,17 @@ $totalPages = ceil($totalPublications / $limit);
 
             <main class="main-content">
                 <?php if ($search): ?>
-                    <h2>Search Results for "<?php echo htmlspecialchars($search); ?>"</h2>
-                    <p class="results-count">Found <?php echo $totalPublications; ?> publications</p>
+                    <h2><?php echo t('search_results'); ?> "<?php echo htmlspecialchars($search); ?>"</h2>
+                    <p class="results-count"><?php echo t('found_count', ['count' => $totalPublications]); ?></p>
                 <?php elseif ($category): ?>
-                    <h2>Category: <?php echo htmlspecialchars($category); ?></h2>
-                    <p class="results-count"><?php echo $totalPublications; ?> publications</p>
+                    <h2><?php echo t('category'); ?>: <?php echo htmlspecialchars($category); ?></h2>
+                    <p class="results-count"><?php echo $totalPublications; ?> <?php echo t('publication_count'); ?></p>
                 <?php else: ?>
-                    <h2>Latest Publications</h2>
+                    <h2><?php echo t('latest_publications'); ?></h2>
                 <?php endif; ?>
 
                 <?php if (empty($publications)): ?>
-                    <p>No publications found.</p>
+                    <p><?php echo t('no_publications_found'); ?></p>
                 <?php else: ?>
                     <div class="publication-list">
                         <?php foreach ($publications as $pub): ?>
@@ -111,8 +119,8 @@ $totalPages = ceil($totalPublications / $limit);
                                 <?php endif; ?>
                                 
                                 <div class="meta">
-                                    <span class="author">By <?php echo htmlspecialchars($pub['author']); ?></span>
-                                    <span class="date"><?php echo date('F j, Y', strtotime($pub['created_at'])); ?></span>
+                                    <span class="author"><?php echo t('by_author', ['author' => htmlspecialchars($pub['author'])]); ?></span>
+                                    <span class="date"><?php echo t('on_date', ['date' => date('F j, Y', strtotime($pub['created_at']))]); ?></span>
                                     <?php if ($pub['categories']): ?>
                                         <span class="categories">
                                             <?php foreach (explode(',', $pub['categories']) as $cat): ?>

+ 14 - 0
setup/setup.php

@@ -19,6 +19,9 @@ $admin_username = 'admin';
 $admin_password = '';
 $admin_email = '';
 
+// Language configuration
+$default_language = 'en';
+
 // LDAP configuration
 $ldap_enabled = false;
 $ldap_host = '';
@@ -43,6 +46,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
     $admin_username = trim($_POST['admin_username'] ?? 'admin');
     $admin_password = trim($_POST['admin_password'] ?? '');
     $admin_email = trim($_POST['admin_email'] ?? '');
+    $default_language = trim($_POST['default_language'] ?? 'en');
     
     // LDAP configuration
     $ldap_enabled = isset($_POST['ldap_enabled']);
@@ -107,6 +111,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
             $config_content .= "define('SITE_TITLE', '$site_title');\n";
             $config_content .= "define('SITE_URL', '" . ($_SERVER['HTTPS'] ? 'https' : 'http') . "://{$_SERVER['HTTP_HOST']}" . dirname($_SERVER['PHP_SELF']) . "/../');\n";
             $config_content .= "define('ADMIN_EMAIL', '$admin_email');\n\n";
+            $config_content .= "// Language configuration\n";
+            $config_content .= "define('DEFAULT_LANGUAGE', '$default_language');\n\n";
             $config_content .= "// LDAP configuration\n";
             $config_content .= "define('LDAP_ENABLED', " . ($ldap_enabled ? 'true' : 'false') . ");\n";
             $config_content .= "define('LDAP_HOST', '$ldap_host');\n";
@@ -191,6 +197,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
                     <input type="text" id="site_title" name="site_title" value="<?php echo htmlspecialchars($site_title); ?>" required>
                 </div>
 
+                <div class="form-group">
+                    <label for="default_language">Default Language:</label>
+                    <select id="default_language" name="default_language">
+                        <option value="en" <?php echo $default_language === 'en' ? 'selected' : ''; ?>>English</option>
+                        <option value="fi" <?php echo $default_language === 'fi' ? 'selected' : ''; ?>>Finnish</option>
+                    </select>
+                </div>
+
                 <h2>Authentication Configuration</h2>
                 
                 <div class="form-group">