diff --git a/app/plugins/global/languages.php b/app/plugins/global/languages.php index 2b4ded2..588a956 100644 --- a/app/plugins/global/languages.php +++ b/app/plugins/global/languages.php @@ -1,85 +1,216 @@ requestPath); + $currentLang = $defaultLang; - if (file_exists($defaultTranslationFile)) { - $translations = parse_ini_file($defaultTranslationFile) ?: []; + // Remove language prefix from URL if present + if (!empty($pathParts[0]) && in_array($pathParts[0], $availableLangs) && $pathParts[0] !== $defaultLang) { + $currentLang = array_shift($pathParts); + $reflection = new ReflectionProperty($ctx, 'requestPath'); + $reflection->setValue($ctx, implode('/', $pathParts)); } - if (file_exists($customTranslationFile)) { - $customTranslations = parse_ini_file($customTranslationFile) ?: []; - $translations = array_merge($translations, $customTranslations); + $ctx->set('currentLang', $currentLang); + $ctx->set('defaultLang', $defaultLang); + $ctx->set('availableLangs', $availableLangs); + $ctx->set('langPrefix', $currentLang !== $defaultLang ? "/$currentLang" : ''); + $ctx->set('translations', loadTranslations($currentLang)); + + return $ctx; +}); + +// Filter content and metadata by language +Hooks::add(Hook::PROCESS_CONTENT, function(mixed $data, string $dirOrType, string $extraContext = '') { + global $GLOBALS; + $ctx = $GLOBALS['ctx'] ?? null; + if (!$ctx) return $data; + + $currentLang = $ctx->get('currentLang', 'en'); + + // Merge language-specific metadata sections + if ($extraContext === 'metadata' && isset($data['_raw'][$currentLang]) && is_array($data['_raw'][$currentLang])) { + return array_merge($data, $data['_raw'][$currentLang]); + } + + // Format dates with translated month names + if ($dirOrType === 'date_format') { + return formatDate($data, $currentLang); + } + + // Filter content files by language variant + if (is_array($data) && !empty($data) && isset($data[0]['path'])) { + return filterFilesByLanguage($data, $dirOrType, $ctx); + } + + return $data; +}); + +// Add language variables to templates +Hooks::add(Hook::TEMPLATE_VARS, function(array $vars, Context $ctx) { + $currentLang = $ctx->get('currentLang', 'en'); + $defaultLang = $ctx->get('defaultLang', 'en'); + $availableLangs = $ctx->get('availableLangs', ['en']); + + $vars['currentLang'] = $currentLang; + $vars['defaultLang'] = $defaultLang; + $vars['langPrefix'] = $ctx->get('langPrefix', ''); + $vars['translations'] = $ctx->get('translations', []); + $vars['availableLangs'] = $availableLangs; + $vars['languageUrls'] = buildLanguageUrls($ctx, $currentLang, $defaultLang, $availableLangs); + + return $vars; +}); + +// --- Helper functions --- + +function loadTranslations(string $lang): array { + $defaultFile = dirname(__DIR__, 2) . "/default/languages/$lang.ini"; + $customFile = dirname(__DIR__, 3) . "/custom/languages/$lang.ini"; + + $translations = file_exists($defaultFile) ? parse_ini_file($defaultFile) ?: [] : []; + + if (file_exists($customFile)) { + $translations = array_merge($translations, parse_ini_file($customFile) ?: []); } return $translations; } -function shouldHideUntranslated(): bool { - $configFile = file_exists(__DIR__ . '/../../../custom/config.ini') - ? __DIR__ . '/../../../custom/config.ini' - : __DIR__ . '/../../config.ini'; - - if (!file_exists($configFile)) return true; - - $config = parse_ini_file($configFile, true); - return !isset($config['languages']['show_untranslated']) - || $config['languages']['show_untranslated'] !== 'true'; -} - -function hasLanguageMetadata(string $dirPath, string $lang): bool { - $metadataFile = "$dirPath/metadata.ini"; - if (!file_exists($metadataFile)) return false; - - $metadata = parse_ini_file($metadataFile, true); - if (!$metadata) return false; - - if (!isset($metadata[$lang]) || !is_array($metadata[$lang])) return false; - - // Check if language section has meaningful content (title or summary) - return isset($metadata[$lang]['title']) || isset($metadata[$lang]['summary']); -} - -function hasLanguageContent(string $dirPath, string $lang, array $contentExtensions): bool { - if (!is_dir($dirPath)) return false; - - $files = scandir($dirPath) ?: []; - foreach ($files as $file) { - $ext = pathinfo($file, PATHINFO_EXTENSION); - if (!in_array($ext, $contentExtensions)) continue; - - $parts = explode('.', $file); - if (count($parts) >= 3) { - $fileLang = $parts[count($parts) - 2]; - if ($fileLang === $lang && is_file("$dirPath/$file")) { - return true; - } - } - } - return false; -} - function formatDate(string $dateString, string $lang): string { - if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})/', $dateString, $matches)) { + if (!preg_match('/^(\d{4})-(\d{2})-(\d{2})/', $dateString, $m)) { return $dateString; } $translations = loadTranslations($lang); - $day = (int)$matches[3]; - $monthIndex = (int)$matches[2] - 1; - $year = $matches[1]; + $day = (int)$m[3]; + $monthIndex = (int)$m[2] - 1; + $year = $m[1]; + $month = $m[2]; if (isset($translations['months'])) { $months = array_map('trim', explode(',', $translations['months'])); - $month = $months[$monthIndex] ?? $matches[2]; - } else { - $month = $matches[2]; + $month = $months[$monthIndex] ?? $m[2]; } return "$day. $month $year"; } + +function filterFilesByLanguage(array $files, string $dir, Context $ctx): array { + $currentLang = $ctx->get('currentLang', 'en'); + $defaultLang = $ctx->get('defaultLang', 'en'); + $availableLangs = $ctx->get('availableLangs', ['en']); + + $filtered = []; + $seen = []; + + foreach ($files as $file) { + $parts = explode('.', $file['name']); + + // Language-specific file (name.lang.ext) + if (count($parts) >= 3 && in_array($parts[count($parts) - 2], $availableLangs)) { + $fileLang = $parts[count($parts) - 2]; + if ($fileLang === $currentLang) { + $filtered[] = $file; + $seen[$parts[0]] = true; + } + continue; + } + + // Default file - include if no language version exists + $baseName = $parts[0]; + if (!isset($seen[$baseName])) { + $hasLangVersion = $currentLang !== $defaultLang && + array_reduce(CONTENT_EXTENSIONS, + fn($found, $ext) => $found || file_exists("$dir/$baseName.$currentLang.$ext"), + false); + + if (!$hasLangVersion) { + $filtered[] = $file; + $seen[$baseName] = true; + } + } + } + + return $filtered; +} + +function buildLanguageUrls(Context $ctx, string $currentLang, string $defaultLang, array $availableLangs): array { + // Frontpage URLs + if (empty($ctx->requestPath)) { + return array_reduce($availableLangs, + fn($urls, $lang) => $urls + [$lang => $lang === $defaultLang ? '/' : "/$lang/"], + []); + } + + // Resolve current path to actual folder names + $folders = resolvePath($ctx->requestPath, $ctx->contentDir); + if (!$folders) { + // Fallback: simple language prefix + return buildSimpleUrls($ctx, $defaultLang, $availableLangs); + } + + // Build URLs with language-specific slugs + $urls = []; + foreach ($availableLangs as $lang) { + $segments = []; + $dir = $ctx->contentDir; + + foreach ($folders as $folderName) { + $metadataFile = "$dir/$folderName/metadata.ini"; + $slug = $folderName; + + if (file_exists($metadataFile)) { + $metadata = parse_ini_file($metadataFile, true) ?: []; + + // Check for language-specific slug + if (isset($metadata[$lang]['slug'])) { + $slug = $metadata[$lang]['slug']; + } elseif (isset($metadata['slug'])) { + $slug = $metadata['slug']; + } + } + + $segments[] = $slug; + $dir .= '/' . $folderName; + } + + $path = '/' . implode('/', $segments); + if ($ctx->hasTrailingSlash) $path .= '/'; + + $urls[$lang] = $lang === $defaultLang ? $path : "/$lang" . $path; + } + + return $urls; +} + +function resolvePath(string $path, string $contentDir): ?array { + $parts = explode('/', trim($path, '/')); + $folders = []; + $dir = $contentDir; + + foreach ($parts as $slug) { + $folderName = resolveSlugToFolder($dir, $slug); + if (!$folderName) return null; + + $folders[] = $folderName; + $dir .= '/' . $folderName; + } + + return $folders; +} + +function buildSimpleUrls(Context $ctx, string $defaultLang, array $availableLangs): array { + $path = '/' . $ctx->requestPath; + if ($ctx->hasTrailingSlash) $path .= '/'; + + return array_reduce($availableLangs, + fn($urls, $lang) => $urls + [$lang => $lang === $defaultLang ? $path : "/$lang" . $path], + []); +}