From 118771e243d7dee2ee4ce487c0785d9da06d206e Mon Sep 17 00:00:00 2001 From: Chris Kankiewicz Date: Mon, 24 Feb 2020 14:28:53 -0700 Subject: [PATCH] Added translations --- app/config/app.php | 9 ++ app/src/Bootstrap/AppManager.php | 1 + app/src/Exceptions/ErrorHandler.php | 19 +-- app/src/Handlers/DirectoryHandler.php | 22 +++- app/src/Handlers/FileInfoHandler.php | 21 ++- app/src/Handlers/SearchHandler.php | 15 ++- app/src/Handlers/ZipHandler.php | 15 ++- app/src/Providers/TranslationProvider.php | 63 +++++++++ app/src/Providers/TwigProvider.php | 1 + app/src/ViewFunctions/Translate.php | 36 ++++++ app/translations/en.yaml | 19 +++ app/translations/es.yaml | 19 +++ app/translations/fr.yaml | 19 +++ app/translations/zh.yaml | 19 +++ app/views/components/file.twig | 2 +- app/views/components/footer.twig | 2 +- app/views/components/header.twig | 8 +- app/views/components/scroll-to-top.twig | 4 +- app/views/index.twig | 6 +- composer.json | 1 + composer.lock | 136 +++++++++++++++++++- tests/Exceptions/ErrorHandlerTest.php | 21 ++- tests/Handlers/DirectoryHandlerTest.php | 9 +- tests/Handlers/FileInfoHandlerTest.php | 6 +- tests/Handlers/ZipHandlerTest.php | 6 +- tests/Providers/TranslationProviderTest.php | 31 +++++ tests/TestCase.php | 33 +++++ tests/ViewFunctions/TranslateTest.php | 45 +++++++ 28 files changed, 536 insertions(+), 52 deletions(-) create mode 100644 app/src/Providers/TranslationProvider.php create mode 100644 app/src/ViewFunctions/Translate.php create mode 100644 app/translations/en.yaml create mode 100644 app/translations/es.yaml create mode 100644 app/translations/fr.yaml create mode 100644 app/translations/zh.yaml create mode 100644 tests/Providers/TranslationProviderTest.php create mode 100644 tests/ViewFunctions/TranslateTest.php 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 %}
diff --git a/app/views/index.twig b/app/views/index.twig index 268f9b1..a5b087c 100644 --- a/app/views/index.twig +++ b/app/views/index.twig @@ -7,15 +7,15 @@
- File Name + {{ translate('file.name') }}
diff --git a/composer.json b/composer.json index 4e8d636..efdc51c 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "slim/slim": "^4.3", "slim/twig-view": "^3.0", "symfony/finder": "^5.0", + "symfony/translation": "^5.0", "tightenco/collect": "^6.4", "vlucas/phpdotenv": "^4.0" }, diff --git a/composer.lock b/composer.lock index e37f795..b07ca16 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1a4f0a5fb5a91dc494cb5479886f6001", + "content-hash": "ec21efdea1e0689436e9653f81e1c18c", "packages": [ { "name": "erusev/parsedown", @@ -1690,6 +1690,140 @@ ], "time": "2020-01-13T11:15:53+00:00" }, + { + "name": "symfony/translation", + "version": "v5.0.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "28e1054f1ea26c63762d9260c37cb1056ea62dbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/28e1054f1ea26c63762d9260c37cb1056ea62dbb", + "reference": "28e1054f1ea26c63762d9260c37cb1056ea62dbb", + "shasum": "" + }, + "require": { + "php": "^7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2" + }, + "conflict": { + "symfony/config": "<4.4", + "symfony/dependency-injection": "<5.0", + "symfony/http-kernel": "<5.0", + "symfony/twig-bundle": "<5.0", + "symfony/yaml": "<4.4" + }, + "provide": { + "symfony/translation-implementation": "2.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^4.4|^5.0", + "symfony/console": "^4.4|^5.0", + "symfony/dependency-injection": "^5.0", + "symfony/finder": "^4.4|^5.0", + "symfony/http-kernel": "^5.0", + "symfony/intl": "^4.4|^5.0", + "symfony/service-contracts": "^1.1.2|^2", + "symfony/yaml": "^4.4|^5.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "https://symfony.com", + "time": "2020-01-21T08:40:24+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "8cc682ac458d75557203b2f2f14b0b92e1c744ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/8cc682ac458d75557203b2f2f14b0b92e1c744ed", + "reference": "8cc682ac458d75557203b2f2f14b0b92e1c744ed", + "shasum": "" + }, + "require": { + "php": "^7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "time": "2019-11-18T17:27:11+00:00" + }, { "name": "symfony/var-dumper", "version": "v5.0.4", diff --git a/tests/Exceptions/ErrorHandlerTest.php b/tests/Exceptions/ErrorHandlerTest.php index 751445a..4c1c684 100644 --- a/tests/Exceptions/ErrorHandlerTest.php +++ b/tests/Exceptions/ErrorHandlerTest.php @@ -11,10 +11,19 @@ use Tests\TestCase; class ErrorHandlerTest extends TestCase { + public function setUp(): void + { + parent::setUp(); + + $this->container->call(TwigProvider::class); + } + public function test_it_returns_an_error(): void { - $this->container->call(TwigProvider::class); - $errorHandler = new ErrorHandler($this->container->get(Twig::class)); + $errorHandler = new ErrorHandler( + $this->container->get(Twig::class), + $this->translator + ); $response = $errorHandler( $this->createMock(Request::class), @@ -32,8 +41,10 @@ class ErrorHandlerTest extends TestCase public function test_it_returns_an_error_for_a_json_request(): void { - $this->container->call(TwigProvider::class); - $errorHandler = new ErrorHandler($this->container->get(Twig::class)); + $errorHandler = new ErrorHandler( + $this->container->get(Twig::class), + $this->translator + ); $request = $this->createMock(Request::class); $request->expects($this->once())->method('getHeaderLine')->willReturn( @@ -50,7 +61,7 @@ class ErrorHandlerTest extends TestCase $this->assertEquals(500, $response->getStatusCode()); $this->assertEquals('application/json', $response->getHeaderLine('Content-Type')); - $this->assertEquals('An unexpected error occured', json_decode( + $this->assertEquals('An unexpected error occurred', json_decode( (string) $response->getBody() )->error->message); } diff --git a/tests/Handlers/DirectoryHandlerTest.php b/tests/Handlers/DirectoryHandlerTest.php index a5516d6..06290ef 100644 --- a/tests/Handlers/DirectoryHandlerTest.php +++ b/tests/Handlers/DirectoryHandlerTest.php @@ -28,7 +28,8 @@ class DirectoryHandlerTest extends TestCase $controller = new DirectoryHandler( $this->config, new Finder, - $this->container->get(Twig::class) + $this->container->get(Twig::class), + $this->translator ); chdir($this->filePath('.')); @@ -53,7 +54,8 @@ class DirectoryHandlerTest extends TestCase $controller = new DirectoryHandler( $this->config, new Finder, - $this->container->get(Twig::class) + $this->container->get(Twig::class), + $this->translator ); $request = $this->createMock(Request::class); @@ -73,7 +75,8 @@ class DirectoryHandlerTest extends TestCase $controller = new DirectoryHandler( $this->config, new Finder, - $this->container->get(Twig::class) + $this->container->get(Twig::class), + $this->translator ); $request = $this->createMock(Request::class); diff --git a/tests/Handlers/FileInfoHandlerTest.php b/tests/Handlers/FileInfoHandlerTest.php index 5fc8338..fd6bfd0 100644 --- a/tests/Handlers/FileInfoHandlerTest.php +++ b/tests/Handlers/FileInfoHandlerTest.php @@ -12,7 +12,7 @@ class FileInfoHandlerTest extends TestCase { public function test_it_can_return_a_successful_response(): void { - $handler = new FileInfoHandler($this->container, $this->config); + $handler = new FileInfoHandler($this->container, $this->config, $this->translator); $request = $this->createMock(Request::class); $request->method('getQueryParams')->willReturn(['info' => 'README.md']); @@ -32,7 +32,7 @@ class FileInfoHandlerTest extends TestCase public function test_it_can_return_a_not_found_response(): void { - $handler = new FileInfoHandler($this->container, $this->config); + $handler = new FileInfoHandler($this->container, $this->config, $this->translator); $request = $this->createMock(Request::class); $request->method('getQueryParams')->willReturn(['info' => 'not_a_file.test']); @@ -46,7 +46,7 @@ class FileInfoHandlerTest extends TestCase public function test_it_returns_an_error_when_file_size_is_too_large(): void { $this->config->set('app.max_hash_size', 10); - $handler = new FileInfoHandler($this->container, $this->config); + $handler = new FileInfoHandler($this->container, $this->config, $this->translator); $request = $this->createMock(Request::class); $request->method('getQueryParams')->willReturn(['info' => 'README.md']); diff --git a/tests/Handlers/ZipHandlerTest.php b/tests/Handlers/ZipHandlerTest.php index d686a7a..9904925 100644 --- a/tests/Handlers/ZipHandlerTest.php +++ b/tests/Handlers/ZipHandlerTest.php @@ -13,7 +13,7 @@ class ZipHandlerTest extends TestCase { public function test_it_returns_a_successful_response_for_a_zip_request(): void { - $handler = new ZipHandler($this->container, $this->config, new Finder); + $handler = new ZipHandler($this->container, $this->config, new Finder, $this->translator); $request = $this->createMock(Request::class); $request->method('getQueryParams')->willReturn(['zip' => 'subdir']); @@ -30,7 +30,7 @@ class ZipHandlerTest extends TestCase public function test_it_returns_a_404_error_when_not_found(): void { - $handler = new ZipHandler($this->container, $this->config, new Finder); + $handler = new ZipHandler($this->container, $this->config, new Finder, $this->translator); $request = $this->createMock(Request::class); $request->method('getQueryParams')->willReturn(['zip' => '404']); @@ -45,7 +45,7 @@ class ZipHandlerTest extends TestCase public function test_it_returns_a_404_error_when_disabled_via_config(): void { $this->config->set('app.zip_downloads', false); - $handler = new ZipHandler($this->container, $this->config, new Finder); + $handler = new ZipHandler($this->container, $this->config, new Finder, $this->translator); $request = $this->createMock(Request::class); $request->method('getQueryParams')->willReturn(['zip' => 'subdir']); diff --git a/tests/Providers/TranslationProviderTest.php b/tests/Providers/TranslationProviderTest.php new file mode 100644 index 0000000..86e3496 --- /dev/null +++ b/tests/Providers/TranslationProviderTest.php @@ -0,0 +1,31 @@ +container, $this->config))(); + + $translator = $this->container->get(TranslatorInterface::class); + + $this->assertEquals('en', $translator->getLocale()); + $this->assertInstanceOf(MessageCatalogue::class, $translator->getCatalogue('en')); + $this->assertInstanceOf(MessageCatalogue::class, $translator->getCatalogue('fr')); + } + + public function test_it_throws_an_exception_for_an_invalid_language(): void + { + $this->expectException(RuntimeException::class); + + $this->config->set('app.language', 'xx'); + (new TranslationProvider($this->container, $this->config))(); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 5c446cd..40d0055 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -5,6 +5,9 @@ namespace Tests; use DI\Container; use PHLAK\Config\Config; use PHPUnit\Framework\TestCase as PHPUnitTestCase; +use Symfony\Component\Translation\Loader\ArrayLoader; +use Symfony\Component\Translation\Translator; +use Symfony\Contracts\Translation\TranslatorInterface; class TestCase extends PHPUnitTestCase { @@ -14,6 +17,9 @@ class TestCase extends PHPUnitTestCase /** @var Config The test config */ protected $config; + /** @var TranslatorInterface Test translator */ + protected $translator; + /** @var string Path to test files directory */ protected $testFilesPath = __DIR__ . '/_files'; @@ -28,6 +34,8 @@ class TestCase extends PHPUnitTestCase $this->config = new Config([ 'app' => [ + 'debug' => false, + 'language' => 'en', 'dark_mode' => false, 'display_readmes' => true, 'zip_downloads' => true, @@ -43,9 +51,34 @@ class TestCase extends PHPUnitTestCase ] ]); + $this->translator = new Translator('en'); + $this->translator->addLoader('array', new ArrayLoader); + $this->translator->addResource('array', [ + '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', + ], 'en'); + $this->container = new Container(); $this->container->set('base_path', $this->testFilesPath); $this->container->set(Config::class, $this->config); + $this->container->set(TranslatorInterface::class, $this->translator); } /** diff --git a/tests/ViewFunctions/TranslateTest.php b/tests/ViewFunctions/TranslateTest.php new file mode 100644 index 0000000..1f4017e --- /dev/null +++ b/tests/ViewFunctions/TranslateTest.php @@ -0,0 +1,45 @@ +translator = new Translator('en'); + $this->translator->addLoader('array', new ArrayLoader); + $this->translator->addResource('array', [ + 'foo' => 'Foo', 'bar' => ['baz' => 'Bar Baz'], + ], 'en'); + $this->translator->addResource('array', [ + 'foo' => 'Le Foo', 'bar' => ['baz' => 'Le Bar Baz'], + ], 'fr'); + } + + public function test_it_can_get_a_translation_for_the_defualt_locale(): void + { + $translate = new Translate($this->translator); + + $this->assertEquals('Foo', $translate('foo')); + $this->assertEquals('Bar Baz', $translate('bar.baz')); + } + + public function test_it_can_get_a_translation_for_an_alternative_locale(): void + { + $this->translator->setLocale('fr'); + $translate = new Translate($this->translator); + + $this->assertEquals('Le Foo', $translate('foo')); + $this->assertEquals('Le Bar Baz', $translate('bar.baz')); + } +}