fix loading i18n of plugins

This commit is contained in:
Pig Fang 2020-06-23 18:17:16 +08:00
parent 49b18783ba
commit ce43f9a586
7 changed files with 77 additions and 144 deletions

View File

@ -55,11 +55,6 @@ class FootComposer
$scripts[] = [
'src' => $this->javascript->generate($locale),
];
if ($pluginI18n = $this->javascript->plugin($locale)) {
$scripts[] = [
'src' => $pluginI18n,
];
}
if (Str::startsWith(config('app.asset.env'), 'dev')) {
$scripts[] = [
'src' => $this->webpack->url('style.js'),

View File

@ -1,46 +0,0 @@
<?php
namespace App\Listeners;
use App\Services\Plugin;
use App\Services\PluginManager;
use Illuminate\Filesystem\Filesystem;
class GeneratePluginTranslations
{
/** @var Filesystem */
protected $filesystem;
/** @var PluginManager */
protected $plugins;
public function __construct(Filesystem $filesystem, PluginManager $plugins)
{
$this->filesystem = $filesystem;
$this->plugins = $plugins;
}
public function handle()
{
$plugins = $this->plugins->getEnabledPlugins();
$locales = array_keys(config('locales'));
array_walk($locales, function ($locale) use ($plugins) {
$i18n = $plugins
->filter(function (Plugin $plugin) use ($locale) {
return $this->filesystem->exists(
$plugin->getPath()."/lang/$locale/front-end.yml"
);
})
->map(function (Plugin $plugin) use ($locale) {
return trans($plugin->namespace.'::front-end');
});
if ($i18n->isNotEmpty()) {
$content = 'Object.assign(blessing.i18n, '.
$i18n->toJson(JSON_UNESCAPED_UNICODE).')';
$this->filesystem->put(public_path("lang/${locale}_plugin.js"), $content);
}
});
}
}

View File

@ -11,11 +11,9 @@ class EventServiceProvider extends ServiceProvider
protected $listen = [
'App\Events\PluginWasEnabled' => [
Listeners\CopyPluginAssets::class,
Listeners\GeneratePluginTranslations::class,
],
'plugin.versionChanged' => [
Listeners\CopyPluginAssets::class,
Listeners\GeneratePluginTranslations::class,
],
'App\Events\PluginBootFailed' => [
Listeners\NotifyFailedPlugin::class,

View File

@ -2,6 +2,8 @@
namespace App\Services\Translations;
use App\Services\Plugin;
use App\Services\PluginManager;
use Illuminate\Cache\Repository;
use Illuminate\Filesystem\Filesystem;
@ -13,23 +15,49 @@ class JavaScript
/** @var Repository */
protected $cache;
/** @var PluginManager */
protected $plugins;
protected $prefix = 'front-end-trans-';
public function __construct(Filesystem $filesystem, Repository $cache)
{
public function __construct(
Filesystem $filesystem,
Repository $cache,
PluginManager $plugins
) {
$this->filesystem = $filesystem;
$this->cache = $cache;
$this->plugins = $plugins;
}
public function generate(string $locale): string
{
$source = resource_path("lang/$locale/front-end.yml");
$plugins = $this->plugins->getEnabledPlugins();
$sourceFiles = $plugins
->map(function (Plugin $plugin) use ($locale) {
return $plugin->getPath()."/lang/$locale/front-end.yml";
})
->filter(function ($path) {
return $this->filesystem->exists($path);
});
$sourceFiles->push(resource_path("lang/$locale/front-end.yml"));
$sourceModified = $sourceFiles->max(function ($path) {
return $this->filesystem->lastModified($path);
});
$compiled = public_path("lang/$locale.js");
$sourceModified = $this->filesystem->lastModified($source);
$compiledModified = intval($this->cache->get($this->prefix.$locale, 0));
$compiledModified = (int) $this->cache->get($this->prefix.$locale, 0);
if ($sourceModified > $compiledModified || !$this->filesystem->exists($compiled)) {
$content = 'blessing.i18n = '.json_encode(trans('front-end'), JSON_UNESCAPED_UNICODE);
$translations = trans('front-end');
foreach ($plugins as $plugin) {
$translations = array_merge(
$translations,
[$plugin->name => trans($plugin->namespace.'::front-end')]
);
}
$content = 'blessing.i18n = '.json_encode($translations, JSON_UNESCAPED_UNICODE);
$this->filesystem->put($compiled, $content);
$this->cache->put($this->prefix.$locale, $sourceModified);
@ -43,16 +71,4 @@ class JavaScript
{
$this->cache->put($this->prefix.$locale, 0);
}
public function plugin(string $locale): string
{
$path = public_path("lang/${locale}_plugin.js");
if ($this->filesystem->exists($path)) {
$lastModified = $this->filesystem->lastModified($path);
return url()->asset("lang/${locale}_plugin.js?t=$lastModified");
}
return '';
}
}

View File

@ -30,10 +30,6 @@ class FootComposerTest extends TestCase
->with('en')
->once()
->andReturn('en.js');
$mock->shouldReceive('plugin')
->with('en')
->once()
->andReturn('en_plugin.js');
});
$this->mock(Webpack::class, function ($mock) {
$mock->shouldReceive('url')->with('style.css');
@ -48,10 +44,7 @@ class FootComposerTest extends TestCase
->andReturn('app.js');
});
$this->get('/user')
->assertSee('en.js')
->assertSee('en_plugin.js')
->assertSee('app.js');
$this->get('/user')->assertSee('en.js')->assertSee('app.js');
}
public function testAddExtra()

View File

@ -1,39 +0,0 @@
<?php
namespace Tests;
use App\Services\Plugin;
use App\Services\PluginManager;
use Illuminate\Filesystem\Filesystem;
class GeneratePluginTranslationsTest extends TestCase
{
public function testHandle()
{
config(['locales' => ['en' => [], 'jp' => []]]);
$this->mock(PluginManager::class, function ($mock) {
$mock->shouldReceive('getEnabledPlugins')
->with()
->once()
->andReturn(collect([new Plugin('/reina', ['namespace' => 'れいな'])]));
});
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with('/reina/lang/en/front-end.yml')
->once()
->andReturn(false);
$mock->shouldReceive('exists')
->with('/reina/lang/jp/front-end.yml')
->once()
->andReturn(true);
$mock->shouldReceive('put')
->with(
public_path('lang/jp_plugin.js'),
'Object.assign(blessing.i18n, ["れいな::front-end"])'
)
->once();
});
$this->app->call('App\Listeners\GeneratePluginTranslations@handle');
}
}

View File

@ -2,6 +2,8 @@
namespace Tests;
use App\Services\Plugin;
use App\Services\PluginManager;
use App\Services\Translations\JavaScript;
use Illuminate\Cache\Repository;
use Illuminate\Filesystem\Filesystem;
@ -12,20 +14,35 @@ class JavaScriptTest extends TestCase
protected function setUp(): void
{
parent::setUp();
$this->app->forgetInstance(JavaScript::class);
app()->forgetInstance(JavaScript::class);
}
public function testGenerateFreshFile()
{
$this->mock(PluginManager::class, function ($mock) {
$mock->shouldReceive('getEnabledPlugins')
->with()
->once()
->andReturn(collect([new Plugin('/reina', ['namespace' => 'れいな'])]));
});
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with('/reina/lang/en/front-end.yml')
->once()
->andReturn(true);
$mock->shouldReceive('lastModified')
->with(resource_path('lang/en/front-end.yml'))
->once()
->andReturn(1);
$mock->shouldReceive('lastModified')
->with('/reina/lang/en/front-end.yml')
->once()
->andReturn(2);
$mock->shouldReceive('put')
->withArgs(function ($path, $content) {
$this->assertEquals(public_path('lang/en.js'), $path);
$this->assertTrue(Str::startsWith($content, 'blessing.i18n'));
$this->assertStringContainsString('"れいな::front-end"', $content);
return true;
})
@ -38,20 +55,37 @@ class JavaScriptTest extends TestCase
->once()
->andReturn(0);
$mock->shouldReceive('put')
->with('front-end-trans-en', 1)
->with('front-end-trans-en', 2)
->once();
});
$this->assertEquals(url('lang/en.js?t=1'), resolve(JavaScript::class)->generate('en'));
$this->assertEquals(
url('lang/en.js?t=2'),
resolve(JavaScript::class)->generate('en')
);
}
public function testGenerateCached()
{
$this->mock(PluginManager::class, function ($mock) {
$mock->shouldReceive('getEnabledPlugins')
->with()
->once()
->andReturn(collect([new Plugin('/reina', ['namespace' => 'れいな'])]));
});
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with('/reina/lang/en/front-end.yml')
->once()
->andReturn(true);
$mock->shouldReceive('lastModified')
->with(resource_path('lang/en/front-end.yml'))
->once()
->andReturn(1);
$mock->shouldReceive('lastModified')
->with('/reina/lang/en/front-end.yml')
->once()
->andReturn(2);
$mock->shouldReceive('exists')
->with(public_path('lang/en.js'))
->once()
@ -61,10 +95,13 @@ class JavaScriptTest extends TestCase
$mock->shouldReceive('get')
->with('front-end-trans-en', 0)
->once()
->andReturn(1);
->andReturn(2);
});
$this->assertEquals(url('lang/en.js?t=1'), resolve(JavaScript::class)->generate('en'));
$this->assertEquals(
url('lang/en.js?t=2'),
resolve(JavaScript::class)->generate('en')
);
}
public function testResetTime()
@ -78,27 +115,6 @@ class JavaScriptTest extends TestCase
resolve(JavaScript::class)->resetTime('en');
}
public function testPlugin()
{
$this->mock(Filesystem::class, function ($mock) {
$mock->shouldReceive('exists')
->with(public_path('lang/en_plugin.js'))
->twice()
->andReturn(false, true);
$mock->shouldReceive('lastModified')
->with(public_path('lang/en_plugin.js'))
->once()
->andReturn(1);
});
$this->assertEquals('', resolve(JavaScript::class)->plugin('en'));
$this->assertEquals(
url('lang/en_plugin.js?t=1'),
resolve(JavaScript::class)->plugin('en')
);
}
public function testFallbackLocale()
{
$this->get('/', ['Accept-Language' => 'xyz'])