Skip to content

Commit

Permalink
Implement localization support in Progressive Web App bundle
Browse files Browse the repository at this point in the history
Updated various modules to support localization, primarily adjusting how manifest files are generated and served. Added new "locales" configuration parameter, made necessary adjustments in manifest and service worker compilers. Additionally, refactored the twig runtime to properly handle localized manifest URLs.
  • Loading branch information
Spomky committed Apr 23, 2024
1 parent 2091496 commit 0ef8172
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 21 deletions.
5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,11 @@ parameters:
count: 3
path: src/Resources/config/definition/service_worker.php

-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:children\\(\\)\\.$#"
count: 1
path: src/Resources/config/definition/translation.php

-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\:\\:end\\(\\)\\.$#"
count: 1
Expand Down
7 changes: 5 additions & 2 deletions src/Dto/TranslatableTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ public function provideTranslation(null|string|array $data): null|string|Transla
return $data;
}
if (is_array($data)) {
return array_map(fn (string $value): TranslatableInterface => new TranslatableMessage($value), $data);
return array_map(
fn (string $value): TranslatableInterface => new TranslatableMessage($value, [], 'pwa'),
$data
);
}

return new TranslatableMessage($data);
return new TranslatableMessage($data, [], 'pwa');
}
}
14 changes: 14 additions & 0 deletions src/Resources/config/definition/translation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;

return static function (DefinitionConfigurator $definition): void {
$definition->rootNode()
->children()
->arrayNode('locales')
->scalarPrototype()->end()
->end()
->end();
};
1 change: 1 addition & 0 deletions src/SpomkyLabsPwaBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function loadExtension(array $config, ContainerConfigurator $container, C
$builder->setAlias('pwa.web_client', $config['web_client']);
}
$builder->setParameter('spomky_labs_pwa.screenshot_user_agent', $config['user_agent']);
$builder->setParameter('spomky_labs_pwa.locales', $config['locales']);

$serviceWorkerConfig = $config['serviceworker'];
$manifestConfig = $config['manifest'];
Expand Down
41 changes: 36 additions & 5 deletions src/Subscriber/ManifestCompileEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\TranslatableNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
use function assert;
use function count;
use const JSON_PRETTY_PRINT;
use const JSON_THROW_ON_ERROR;
use const JSON_UNESCAPED_SLASHES;
Expand All @@ -31,6 +34,9 @@

private array $jsonOptions;

/**
* @param string[] $locales
*/
public function __construct(
private SerializerInterface $serializer,
private Manifest $manifest,
Expand All @@ -42,7 +48,9 @@ public function __construct(
private PublicAssetsFilesystemInterface $assetsFilesystem,
#[Autowire('%kernel.debug%')]
bool $debug,
null|EventDispatcherInterface $dispatcher = null,
null|EventDispatcherInterface $dispatcher,
#[Autowire('%spomky_labs_pwa.locales%')]
private array $locales,
) {
$this->dispatcher = $dispatcher ?? new NullEventDispatcher();
$this->manifestPublicUrl = '/' . trim($manifestPublicUrl, '/');
Expand All @@ -64,10 +72,33 @@ public function __invoke(PreAssetsCompileEvent $event): void
return;
}
$manifest = clone $this->manifest;
$this->dispatcher->dispatch(new PreManifestCompileEvent($manifest));
$data = $this->serializer->serialize($manifest, 'json', $this->jsonOptions);
if (count($this->locales) === 0) {
$this->compileManifest($manifest, null);
} else {
foreach ($this->locales as $locale) {
$this->compileManifest($manifest, $locale);
}
}
}

private function compileManifest(Manifest $manifest, null|string $locale): void
{
$preEvent = new PreManifestCompileEvent($manifest);
$preEvent = $this->dispatcher->dispatch($preEvent);
assert($preEvent instanceof PreManifestCompileEvent);

$options = $this->jsonOptions;
$manifestPublicUrl = $this->manifestPublicUrl;
if ($locale !== null) {
$options[TranslatableNormalizer::NORMALIZATION_LOCALE_KEY] = $locale;
$manifestPublicUrl = str_replace('{locale}', $locale, $this->manifestPublicUrl);
}
$data = $this->serializer->serialize($preEvent->manifest, 'json', $options);

$postEvent = new PostManifestCompileEvent($manifest, $data);
$this->dispatcher->dispatch($postEvent);
$this->assetsFilesystem->write($this->manifestPublicUrl, $postEvent->data);
$postEvent = $this->dispatcher->dispatch($postEvent);
assert($postEvent instanceof PostManifestCompileEvent);

$this->assetsFilesystem->write($manifestPublicUrl, $postEvent->data);
}
}
43 changes: 34 additions & 9 deletions src/Subscriber/PwaDevServerSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\TranslatableNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
use function assert;
use function count;
use function in_array;
use function is_array;
use function is_string;
use function mb_strlen;
Expand All @@ -47,6 +49,9 @@

private array $jsonOptions;

/**
* @param string[] $locales
*/
public function __construct(
private ServiceWorkerCompiler $serviceWorkerBuilder,
private SerializerInterface $serializer,
Expand All @@ -61,7 +66,9 @@ public function __construct(
private null|Profiler $profiler,
#[Autowire('%kernel.debug%')]
bool $debug,
null|EventDispatcherInterface $dispatcher = null,
null|EventDispatcherInterface $dispatcher,
#[Autowire('%spomky_labs_pwa.locales%')]
private array $locales,
) {
$this->dispatcher = $dispatcher ?? new NullEventDispatcher();
$this->manifestPublicUrl = '/' . trim($manifestPublicUrl, '/');
Expand Down Expand Up @@ -93,11 +100,22 @@ public function onKernelRequest(RequestEvent $event): void
return;
}

$pathInfo = $event->getRequest()
->getPathInfo();
$request = $event->getRequest();
$pathInfo = $request->getPathInfo();
$localizedManifestPublicUrls = [];
foreach ($this->locales as $locale) {
$localizedManifestPublicUrls[$locale] = str_replace('{locale}', $locale, $this->manifestPublicUrl);
}

switch (true) {
case $this->manifestEnabled === true && $pathInfo === $this->manifestPublicUrl:
case $this->manifestEnabled === true && in_array($pathInfo, $localizedManifestPublicUrls, true):
$locale = array_search($pathInfo, $localizedManifestPublicUrls, true);
assert(is_string($locale), 'Locale not found.');
$this->serveManifest($event, $locale);
break;
case $this->manifestEnabled === true && count(
$localizedManifestPublicUrls
) === 0 && $pathInfo === $this->manifestPublicUrl:
$this->serveManifest($event);
break;
case $this->serviceWorkerEnabled === true && $pathInfo === $this->serviceWorkerPublicUrl:
Expand Down Expand Up @@ -133,16 +151,23 @@ public static function getSubscribedEvents(): array
];
}

private function serveManifest(RequestEvent $event): void
private function serveManifest(RequestEvent $event, null|string $locale = null): void
{
$this->profiler?->disable();
$manifest = clone $this->manifest;
$this->dispatcher->dispatch(new PreManifestCompileEvent($manifest));
$data = $this->serializer->serialize($manifest, 'json', $this->jsonOptions);
$options = $this->jsonOptions;
if ($locale !== null) {
$options[TranslatableNormalizer::NORMALIZATION_LOCALE_KEY] = $locale;
}
$preEvent = new PreManifestCompileEvent($manifest);
$preEvent = $this->dispatcher->dispatch($preEvent);
assert($preEvent instanceof PreManifestCompileEvent);
$data = $this->serializer->serialize($preEvent->manifest, 'json', $options);
$postEvent = new PostManifestCompileEvent($manifest, $data);
$this->dispatcher->dispatch($postEvent);
$postEvent = $this->dispatcher->dispatch($postEvent);
assert($postEvent instanceof PostManifestCompileEvent);

$response = new Response($data, Response::HTTP_OK, [
$response = new Response($postEvent->data, Response::HTTP_OK, [
'Cache-Control' => 'public, max-age=604800, immutable',
'Content-Type' => 'application/manifest+json',
'X-Manifest-Dev' => true,
Expand Down
20 changes: 15 additions & 5 deletions src/Twig/PwaRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ public function load(
bool $injectThemeColor = true,
bool $injectIcons = true,
bool $injectSW = true,
array $swAttributes = []
array $swAttributes = [],
null|string $locale = null,
): string {
$output = '';
if ($this->manifestEnabled === true) {
$output = $this->injectManifestFile($output);
$output = $this->injectManifestFile($output, $locale);
}
if ($this->serviceWorkerEnabled === true) {
$output = $this->injectServiceWorker($output, $injectSW, $swAttributes);
Expand All @@ -53,15 +54,24 @@ public function load(
return $this->injectThemeColor($output, $injectThemeColor);
}

private function injectManifestFile(string $output): string
private function injectManifestFile(string $output, null|string $locale): string
{
$url = $this->assetMapper->getPublicPath($this->manifestPublicUrl) ?? $this->manifestPublicUrl;
$manifestPublicUrl = $locale === null ? $this->manifestPublicUrl : str_replace(
'{locale}',
$locale,
$this->manifestPublicUrl
);
$url = $this->assetMapper->getPublicPath($manifestPublicUrl) ?? $manifestPublicUrl;
$useCredentials = '';
if ($this->manifest->useCredentials === true) {
$useCredentials = ' crossorigin="use-credentials"';
}
$hreflang = '';
if ($locale !== null) {
$hreflang = sprintf(' hreflang="%s"', mb_strtolower(str_replace('_', '-', $locale)));
}

return $output . sprintf('%s<link rel="manifest"%s href="%s">', PHP_EOL, $useCredentials, $url);
return $output . sprintf('%s<link rel="manifest" href="%s"%s%s>', PHP_EOL, $url, $useCredentials, $hreflang);
}

private function injectThemeColor(string $output, bool $themeColor): string
Expand Down

0 comments on commit 0ef8172

Please sign in to comment.