diff --git a/app/config/app.php b/app/config/app.php index c82e345..2a1557a 100644 --- a/app/config/app.php +++ b/app/config/app.php @@ -14,6 +14,15 @@ return [ */ 'debug' => Helpers::env('DEBUG'), + /** + * The application interface language. + * + * Possible values: See 'app/languages' folder for available translations. + * + * Defualt value: en + */ + 'language' => Helpers::env('LANGUAGE'), + /** * Enable dark mode? * diff --git a/app/src/Bootstrap/AppManager.php b/app/src/Bootstrap/AppManager.php index 785fd46..1d1540d 100644 --- a/app/src/Bootstrap/AppManager.php +++ b/app/src/Bootstrap/AppManager.php @@ -18,6 +18,7 @@ class AppManager protected const PROVIDERS = [ Providers\ConfigProvider::class, Providers\FinderProvider::class, + Providers\TranslationProvider::class, Providers\TwigProvider::class, Providers\WhoopsProvider::class, ]; diff --git a/app/src/Exceptions/ErrorHandler.php b/app/src/Exceptions/ErrorHandler.php index 4c8fd2d..d93bd76 100644 --- a/app/src/Exceptions/ErrorHandler.php +++ b/app/src/Exceptions/ErrorHandler.php @@ -7,24 +7,27 @@ use Psr\Http\Message\ServerRequestInterface; use Slim\Interfaces\ErrorHandlerInterface; use Slim\Psr7\Response; use Slim\Views\Twig; +use Symfony\Contracts\Translation\TranslatorInterface; use Throwable; class ErrorHandler implements ErrorHandlerInterface { - /** @const The default error message string */ - protected const DEFAULT_ERROR_MESSAGE = 'An unexpected error occured'; - /** @var Twig Twig templating component */ protected $view; + /** @var TranslatorInterface Translation component */ + protected $translator; + /** * Create a new ErrorHandler object. * - * @param \Slim\Views\Twig $view + * @param \Slim\Views\Twig $view + * @param \Symfony\Contracts\Translation\TranslatorInterface $translator */ - public function __construct(Twig $view) + public function __construct(Twig $view, TranslatorInterface $translator) { $this->view = $view; + $this->translator = $translator; } /** @@ -49,15 +52,15 @@ class ErrorHandler implements ErrorHandlerInterface if (in_array('application/json', explode(',', $request->getHeaderLine('Accept')))) { $response->getBody()->write(json_encode([ - 'error' => ['message' => self::DEFAULT_ERROR_MESSAGE] + 'error' => ['message' => $this->translator->trans('error.unexpected')] ])); return $response->withHeader('Content-Type', 'application/json'); } return $this->view->render($response, 'error.twig', [ - 'message' => self::DEFAULT_ERROR_MESSAGE, - 'subtext' => 'Enable debugging for additional information', + 'message' => $this->translator->trans('error.unexpected'), + 'subtext' => $this->translator->trans('enable_debugging'), ]); } } diff --git a/app/src/Handlers/DirectoryHandler.php b/app/src/Handlers/DirectoryHandler.php index eb78ee1..dcd9ca6 100644 --- a/app/src/Handlers/DirectoryHandler.php +++ b/app/src/Handlers/DirectoryHandler.php @@ -10,6 +10,7 @@ use Slim\Views\Twig; use Symfony\Component\Finder\Exception\DirectoryNotFoundException; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; +use Symfony\Contracts\Translation\TranslatorInterface; class DirectoryHandler { @@ -22,18 +23,27 @@ class DirectoryHandler /** @var Twig Twig templating component */ protected $view; + /** @var TranslatorInterface Translator component */ + protected $translator; + /** * Create a new IndexController object. * - * @param \PHLAK\Config\Config $config - * @param \Symfony\Component\Finder\Finder $finder - * @param \Slim\Views\Twig $view + * @param \PHLAK\Config\Config $config + * @param \Symfony\Component\Finder\Finder $finder + * @param \Slim\Views\Twig $view + * @param \Symfony\Contracts\Translation\TranslatorInterface $translator */ - public function __construct(Config $config, Finder $finder, Twig $view) - { + public function __construct( + Config $config, + Finder $finder, + Twig $view, + TranslatorInterface $translator + ) { $this->config = $config; $this->finder = $finder; $this->view = $view; + $this->translator = $translator; } /** @@ -52,7 +62,7 @@ class DirectoryHandler $files = $this->finder->in($path)->depth(0); } catch (DirectoryNotFoundException $exception) { return $this->view->render($response->withStatus(404), 'error.twig', [ - 'message' => 'Directory does not exist' + 'message' => $this->translator->trans('error.directory_not_found') ]); } diff --git a/app/src/Handlers/FileInfoHandler.php b/app/src/Handlers/FileInfoHandler.php index 0cd447c..2529289 100644 --- a/app/src/Handlers/FileInfoHandler.php +++ b/app/src/Handlers/FileInfoHandler.php @@ -8,6 +8,7 @@ use Psr\Http\Message\ResponseInterface; use Slim\Psr7\Request; use Slim\Psr7\Response; use SplFileInfo; +use Symfony\Contracts\Translation\TranslatorInterface; class FileInfoHandler { @@ -17,16 +18,24 @@ class FileInfoHandler /** @var Config App configuration component */ protected $config; + /** @var TranslatorInterface Translator component */ + protected $translator; + /** * Create a new FileInfoHandler object. * - * @param \DI\Container $container - * @param \PHLAK\Config\Config $config + * @param \DI\Container $container + * @param \PHLAK\Config\Config $config + * @param \Symfony\Contracts\Translation\TranslatorInterface $translator */ - public function __construct(Container $container, Config $config) - { + public function __construct( + Container $container, + Config $config, + TranslatorInterface $translator + ) { $this->container = $container; $this->config = $config; + $this->translator = $translator; } /** @@ -46,11 +55,11 @@ class FileInfoHandler ); if (! $file->isFile()) { - return $response->withStatus(404, 'File not found'); + return $response->withStatus(404, $this->translator->trans('error.file_not_found')); } if ($file->getSize() >= $this->config->get('app.max_hash_size', 1000000000)) { - return $response->withStatus(500, 'File size too large'); + return $response->withStatus(500, $this->translator->trans('error.file_size_exceeded')); } $response->getBody()->write(json_encode([ diff --git a/app/src/Handlers/SearchHandler.php b/app/src/Handlers/SearchHandler.php index bd33aea..7afb8ea 100644 --- a/app/src/Handlers/SearchHandler.php +++ b/app/src/Handlers/SearchHandler.php @@ -7,6 +7,8 @@ use Slim\Psr7\Request; use Slim\Psr7\Response; use Slim\Views\Twig; use Symfony\Component\Finder\Finder; +use Symfony\Component\Translation\Translator; +use Symfony\Contracts\Translation\TranslatorInterface; class SearchHandler { @@ -16,16 +18,21 @@ class SearchHandler /** @var Twig Twig templating component */ protected $view; + /** @var TranslatorInterface Translator component */ + protected $translator; + /** * Create a new SearchHandler object. * - * @param \Symfony\Component\Finder\Finder $finder - * @param \Slim\Views\Twig $view + * @param \Symfony\Component\Finder\Finder $finder + * @param \Slim\Views\Twig $view + * @param \Symfony\Contracts\Translation\TranslatorInterface $translator */ - public function __construct(Finder $finder, Twig $view) + public function __construct(Finder $finder, Twig $view, TranslatorInterface $translator) { $this->finder = $finder; $this->view = $view; + $this->translator = $translator; } /** @@ -42,7 +49,7 @@ class SearchHandler if (empty($search)) { return $this->view->render($response, 'error.twig', [ - 'message' => 'No results found' + 'message' => $this->translator->trans('error.no_results_found') ]); } diff --git a/app/src/Handlers/ZipHandler.php b/app/src/Handlers/ZipHandler.php index bb8b223..8da04a9 100644 --- a/app/src/Handlers/ZipHandler.php +++ b/app/src/Handlers/ZipHandler.php @@ -10,6 +10,7 @@ use Slim\Psr7\Request; use Slim\Psr7\Response; use Symfony\Component\Finder\Finder; use Symfony\Component\Finder\SplFileInfo; +use Symfony\Contracts\Translation\TranslatorInterface; use Tightenco\Collect\Support\Collection; use ZipArchive; @@ -24,17 +25,25 @@ class ZipHandler /** @var Finder The Finder Component */ protected $finder; + /** @var TranslatorInterface Translator component */ + protected $translator; + /** * Create a new ZipHandler object. * * @param \DI\Container $container * @param \PhpCsFixer\Finder $finder */ - public function __construct(Container $container, Config $config, Finder $finder) - { + public function __construct( + Container $container, + Config $config, + Finder $finder, + TranslatorInterface $translator + ) { $this->container = $container; $this->config = $config; $this->finder = $finder; + $this->translator = $translator; } /** @@ -50,7 +59,7 @@ class ZipHandler $path = $request->getQueryParams()['zip']; if (! $this->config->get('app.zip_downloads', true) || ! realpath($path)) { - return $response->withStatus(404, 'File not found'); + return $response->withStatus(404, $this->translator->trans('error.file_not_found')); } $zip = new ZipArchive; diff --git a/app/src/Providers/TranslationProvider.php b/app/src/Providers/TranslationProvider.php new file mode 100644 index 0000000..ba42138 --- /dev/null +++ b/app/src/Providers/TranslationProvider.php @@ -0,0 +1,63 @@ +container = $container; + $this->config = $config; + } + + /** + * Initialize and register the translation component. + * + * @return void + */ + public function __invoke(): void + { + $language = $this->config->get('app.language', 'en'); + + if (! in_array($language, self::LANGUAGES)) { + throw new RuntimeException("Invalid language option '{$language}'"); + } + + $translator = new Translator($language); + $translator->addLoader('yaml', new YamlFileLoader()); + + Collection::make(self::LANGUAGES)->each( + function (string $language) use ($translator): void { + $resource = sprintf('app/translations/%s.yaml', $language); + $translator->addResource('yaml', $resource, $language); + } + ); + + $this->container->set(TranslatorInterface::class, $translator); + } +} diff --git a/app/src/Providers/TwigProvider.php b/app/src/Providers/TwigProvider.php index c5af2ed..d9db54a 100644 --- a/app/src/Providers/TwigProvider.php +++ b/app/src/Providers/TwigProvider.php @@ -22,6 +22,7 @@ class TwigProvider ViewFunctions\Markdown::class, ViewFunctions\ParentDir::class, ViewFunctions\SizeForHumans::class, + ViewFunctions\Translate::class, ViewFunctions\Url::class, ]; diff --git a/app/src/ViewFunctions/Translate.php b/app/src/ViewFunctions/Translate.php new file mode 100644 index 0000000..9998991 --- /dev/null +++ b/app/src/ViewFunctions/Translate.php @@ -0,0 +1,36 @@ +translator = $translator; + } + + /** + * Retrieve a translated string by ID. + * + * @param string $id + * + * @return string + */ + public function __invoke(string $id): string + { + return $this->translator->trans($id); + } +} diff --git a/app/translations/en.yaml b/app/translations/en.yaml new file mode 100644 index 0000000..fe1b3cb --- /dev/null +++ b/app/translations/en.yaml @@ -0,0 +1,19 @@ +home: Home +download: Download this Directory +search: Search +file: + name: File Name + size: Size + date: Date + info: File Info +powered_by: Powered by +scroll_to_top: Scroll to Top + +error: + directory_not_found: Directory does not exist + file_not_found: File not found + file_size_exceeded: File size too large + no_results_found: No results found + unexpected: An unexpected error occurred + +enable_debugging: Enable debugging for additional information diff --git a/app/translations/es.yaml b/app/translations/es.yaml new file mode 100644 index 0000000..5b582dc --- /dev/null +++ b/app/translations/es.yaml @@ -0,0 +1,19 @@ +home: Hogar +download: Descargar este Directorio +search: Buscar +file: + name: Nombre del Archivo + size: Tamaño + date: Fecha + info: Información del Archivo +powered_by: Desarrollado por +scroll_to_top: Vuelve al Comienzo + +error: + directory_not_found: El directorio no existe + file_not_found: Archivo no encontrado + file_size_exceeded: Tamaño de archivo demasiado grande + no_results_found: No se han encontrado resultados + unexpected: Ocurrió un error inesperado + +enable_debugging: Habilite la depuración para obtener información adicional diff --git a/app/translations/fr.yaml b/app/translations/fr.yaml new file mode 100644 index 0000000..ad1f35c --- /dev/null +++ b/app/translations/fr.yaml @@ -0,0 +1,19 @@ +home: Accueil +download: Téléchargez ce Répertoire +search: Chercher +file: + name: Nom de Fichier + size: Taille + date: Date + info: Informations sur le Fichier +powered_by: Alimenté par +scroll_to_top: Faire Défiler vers le Haut + +error: + directory_not_found: Le répertoire n'existe pas + file_not_found: Fichier non trouvé + file_size_exceeded: Taille de fichier trop grande + no_results_found: Aucun résultat trouvé + unexpected: Une erreur inattendue est apparue + +enable_debugging: Activer le débogage pour des informations supplémentaires diff --git a/app/translations/zh.yaml b/app/translations/zh.yaml new file mode 100644 index 0000000..d374c4f --- /dev/null +++ b/app/translations/zh.yaml @@ -0,0 +1,19 @@ +home: 家 +download: 下载此目录 +search: 搜索 +file: + name: 文件名 + size: 尺寸 + date: 日期 + info: 文件信息 +powered_by: 供电 +scroll_to_top: 滚动到顶部 + +error: + directory_not_found: 目录不存在 + file_not_found: 文件未找到 + file_size_exceeded: 档案太大 + no_results_found: 未找到结果 + unexpected: 一个意料之外的问题发生了 + +enable_debugging: 启用调试以获取其他信息 diff --git a/app/views/components/file.twig b/app/views/components/file.twig index 64efd8e..28e22f2 100644 --- a/app/views/components/file.twig +++ b/app/views/components/file.twig @@ -18,7 +18,7 @@ {% if file.isFile %}