diff --git a/.gitignore b/.gitignore
index a7f372d..093e122 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,5 +7,7 @@ docs
phpunit.xml
phpstan.neon
testbench.yaml
-vendor
+/vendor
node_modules
+da
+da.json
diff --git a/composer.json b/composer.json
index 8a7e666..cc64112 100644
--- a/composer.json
+++ b/composer.json
@@ -17,8 +17,8 @@
],
"require": {
"php": "^8.2",
- "spatie/laravel-package-tools": "^1.14.0",
- "illuminate/contracts": "^10.0"
+ "illuminate/contracts": "^10.0",
+ "spatie/laravel-package-tools": "^1.14.0"
},
"require-dev": {
"laravel/pint": "^1.0",
@@ -81,4 +81,4 @@
},
"minimum-stability": "dev",
"prefer-stable": true
-}
\ No newline at end of file
+}
diff --git a/config/translation-faker.php b/config/translation-faker.php
index f3cff89..f3c75e4 100644
--- a/config/translation-faker.php
+++ b/config/translation-faker.php
@@ -1,6 +1,44 @@
'en',
+ /*
+ |--------------------------------------------------------------------------
+ | Locale Replacers
+ |--------------------------------------------------------------------------
+ |
+ | The following lists the "replacers" that find the keys listed below and
+ | replaces them with their mapped value. Please avoid using colon ':' in
+ | this configuration as it may mess with our placeholder detection. Any
+ | locales you want to use as the base language must be configured here.
+ |
+ | Note: This is case sensitive, so please provide replaces for different
+ | cases as needed.
+ |
+ */
+ 'replacers' => [
+ 'en' => [
+ 'A' => 'Ä',
+ 'a' => 'ä',
+ 'E' => 'Ë',
+ 'e' => 'ë',
+ 'I' => 'Ï',
+ 'i' => 'ï',
+ 'O' => 'Ö',
+ 'o' => 'ö',
+ 'U' => 'Ü',
+ 'u' => 'ü',
+ ],
+ ],
];
diff --git a/database/factories/ModelFactory.php b/database/factories/ModelFactory.php
deleted file mode 100644
index 3b74450..0000000
--- a/database/factories/ModelFactory.php
+++ /dev/null
@@ -1,19 +0,0 @@
-id();
-
- // add fields
-
- $table->timestamps();
- });
- }
-};
diff --git a/resources/views/.gitkeep b/resources/views/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/src/Commands/FakeTranslationCommand.php b/src/Commands/FakeTranslationCommand.php
new file mode 100644
index 0000000..095fbd5
--- /dev/null
+++ b/src/Commands/FakeTranslationCommand.php
@@ -0,0 +1,97 @@
+argument('locale');
+ $baseLocale = $this->option('baseLocale') ?: config('translation-faker.default');
+ $replacers = Arr::wrap(config('translation-faker.replacers'));
+ $namespaces = $namespaceFinder->execute();
+
+ if (! array_key_exists($baseLocale, $replacers)) {
+ $this->components->error("Please add $baseLocale base locale to replacer configuration.");
+
+ return self::FAILURE;
+ }
+
+ $replacers = $replacers[$baseLocale];
+
+ foreach ($namespaces as $path) {
+ $files = $finder->execute($path, $baseLocale);
+
+ /** @var SplFileInfo $file */
+ foreach ($files as $file) {
+ $outputFilename = "$locale.json";
+
+ $outputDirectory = Str::of($file->getPath())
+ ->finish(DIRECTORY_SEPARATOR)
+ ->replace(
+ [DIRECTORY_SEPARATOR.$baseLocale.DIRECTORY_SEPARATOR, "$baseLocale.json"],
+ [DIRECTORY_SEPARATOR.$locale.DIRECTORY_SEPARATOR, $outputFilename],
+ )->before($outputFilename)->toString();
+
+ $outputPath = Str::of($file->getPathname())
+ ->replace(
+ [DIRECTORY_SEPARATOR.$baseLocale.DIRECTORY_SEPARATOR, "$baseLocale.json"],
+ [DIRECTORY_SEPARATOR.$locale.DIRECTORY_SEPARATOR, $outputFilename],
+ )->toString();
+
+ $this->components->task(
+ "Ensuring directory exists $outputDirectory",
+ fn () => $filesystem->ensureDirectoryExists($outputDirectory),
+ OutputInterface::VERBOSITY_DEBUG,
+ );
+
+ $translations = $reader
+ ->getTranslations($file)
+ ->dot()
+ ->mapWithKeys(fn ($langLine, $langKey) => [$langKey => $this->convertToUmlaut($replacers, $langLine)])
+ ->undot();
+
+ $this->components->task(
+ "Writing to $outputPath",
+ fn () => $filesystem->put($outputPath, $printer->execute($file, $translations)),
+ OutputInterface::VERBOSITY_DEBUG,
+ );
+ }
+ }
+
+ $this->components->info("Translations successfully generated from '$baseLocale' to '$locale'.");
+
+ return self::SUCCESS;
+ }
+
+ private function convertToUmlaut(array $replacers, string $text): string
+ {
+ $str = Str::of($text)->explode(' ')->map(fn ($string) => Str::match('/:\w+/', $string)
+ ? $string
+ : Str::replace(array_keys($replacers), array_values($replacers), $string));
+
+ return $str->implode(' ');
+ }
+}
diff --git a/src/Commands/LaravelTranslationFakerCommand.php b/src/Commands/LaravelTranslationFakerCommand.php
deleted file mode 100644
index a71eb67..0000000
--- a/src/Commands/LaravelTranslationFakerCommand.php
+++ /dev/null
@@ -1,19 +0,0 @@
-comment('All done');
-
- return self::SUCCESS;
- }
-}
diff --git a/src/Contracts/Finders/Finder.php b/src/Contracts/Finders/Finder.php
new file mode 100644
index 0000000..253a219
--- /dev/null
+++ b/src/Contracts/Finders/Finder.php
@@ -0,0 +1,10 @@
+filesystem->exists($path)) {
+ $files = new Collection($this->filesystem->allFiles($path));
+ $extensions = $this->manager->getEnabledDrivers();
+
+ return $files->filter(function (SplFileInfo $file) use ($extensions, $locale) {
+ if (in_array($file->getExtension(), $extensions)) {
+ if ($file->getFilenameWithoutExtension() === $locale) {
+ return true;
+ }
+
+ return str_contains(
+ $file->getPathname(),
+ DIRECTORY_SEPARATOR.$locale.DIRECTORY_SEPARATOR
+ );
+ }
+
+ return false;
+ });
+ }
+
+ return new Collection();
+ }
+}
diff --git a/src/Finders/LanguageNamespaceFinder.php b/src/Finders/LanguageNamespaceFinder.php
new file mode 100644
index 0000000..f20079e
--- /dev/null
+++ b/src/Finders/LanguageNamespaceFinder.php
@@ -0,0 +1,34 @@
+translator->getLoader();
+
+ foreach ($loader->namespaces() as $hint => $path) {
+ $namespacesCollection->put($hint, $path);
+ }
+
+ // Add default namespace
+ $namespacesCollection->put('', app()->langPath());
+
+ // Return namespaces collection after removing non existing paths
+ return $namespacesCollection->filter(function ($path) {
+ return file_exists($path) ? $path : false;
+ });
+ }
+}
diff --git a/src/LaravelTranslationFaker.php b/src/LaravelTranslationFaker.php
deleted file mode 100755
index 402737d..0000000
--- a/src/LaravelTranslationFaker.php
+++ /dev/null
@@ -1,7 +0,0 @@
-name('laravel-translation-faker')
->hasConfigFile()
- ->hasViews()
- ->hasMigration('create_laravel-translation-faker_table')
- ->hasCommand(LaravelTranslationFakerCommand::class);
+ ->hasCommand(FakeTranslationCommand::class);
+ }
+
+ public function registeringPackage()
+ {
+ $this->app->bind(LanguageFileFinderContract::class, LanguageFileFinder::class);
+
+ $this->app->bind(LanguageFilePrinterContract::class, LanguageFilePrinter::class);
+
+ $this->app->bind(LanguageFileReaderContract::class, LanguageFileReader::class);
+
+ $this->app->scoped(LanguageFilePrinterManager::class, LanguageFilePrinterManager::class);
+
+ $this->app->scoped(LanguageFileReaderManager::class, LanguageFileReaderManager::class);
+
+ $this->app->bind(LanguageNamespaceFinderContract::class, LanguageNamespaceFinder::class);
+ }
+
+ public function provides()
+ {
+ return [
+ LanguageFileFinderContract::class,
+ LanguageFilePrinterContract::class,
+ LanguageFileReaderContract::class,
+ LanguageFilePrinterManager::class,
+ LanguageFileReaderManager::class,
+ LanguageNamespaceFinderContract::class,
+ ];
}
}
diff --git a/src/Managers/LanguageFilePrinterManager.php b/src/Managers/LanguageFilePrinterManager.php
new file mode 100644
index 0000000..b029ecf
--- /dev/null
+++ b/src/Managers/LanguageFilePrinterManager.php
@@ -0,0 +1,44 @@
+ JsonLanguageFilePrinter::class,
+ 'php' => PhpLanguageFilePrinter::class,
+ ];
+
+ public function __construct(Container $container)
+ {
+ parent::__construct($container);
+
+ foreach (static::DRIVERS as $driver => $readerClass) {
+ $this->extend($driver, fn (Container $app) => $app->get($readerClass));
+ }
+ }
+
+ public function getDefaultDriver()
+ {
+ return array_key_first($this->customCreators);
+ }
+
+ public function isEnabled(string $driver)
+ {
+ return array_key_exists($driver, $this->customCreators);
+ }
+
+ public function getEnabledDrivers()
+ {
+ return array_keys($this->customCreators);
+ }
+}
diff --git a/src/Managers/LanguageFileReaderManager.php b/src/Managers/LanguageFileReaderManager.php
new file mode 100644
index 0000000..09fab7d
--- /dev/null
+++ b/src/Managers/LanguageFileReaderManager.php
@@ -0,0 +1,44 @@
+ JsonLanguageFileReader::class,
+ 'php' => PhpLanguageFileReader::class,
+ ];
+
+ public function __construct(Container $container)
+ {
+ parent::__construct($container);
+
+ foreach (static::DRIVERS as $driver => $readerClass) {
+ $this->extend($driver, fn (Container $app) => $app->get($readerClass));
+ }
+ }
+
+ public function getDefaultDriver()
+ {
+ return array_key_first($this->customCreators);
+ }
+
+ public function isEnabled(string $driver)
+ {
+ return array_key_exists($driver, $this->customCreators);
+ }
+
+ public function getEnabledDrivers()
+ {
+ return array_keys($this->customCreators);
+ }
+}
diff --git a/src/Printers/JsonLanguageFilePrinter.php b/src/Printers/JsonLanguageFilePrinter.php
new file mode 100644
index 0000000..afc408c
--- /dev/null
+++ b/src/Printers/JsonLanguageFilePrinter.php
@@ -0,0 +1,17 @@
+getExtension();
+ $translations = '';
+
+ if ($this->manager->isEnabled($extension)) {
+ $translations = $this->manager->driver($extension)->execute($file, $items);
+ }
+
+ if (! $translations) {
+ throw new InvalidArgumentException("Unable to print any data from {$file->getPathname()}!");
+ }
+
+ return $translations;
+ }
+}
diff --git a/src/Printers/PhpLanguageFilePrinter.php b/src/Printers/PhpLanguageFilePrinter.php
new file mode 100644
index 0000000..1dad10a
--- /dev/null
+++ b/src/Printers/PhpLanguageFilePrinter.php
@@ -0,0 +1,47 @@
+build($items->toArray(), 1);
+
+ return $this->print($body);
+ }
+
+ protected function build(array $array, int $indent): string
+ {
+ $lines = [];
+
+ foreach ($array as $key => $value) {
+ $indentedKey = str_repeat(' ', $indent * 4).var_export($key, true);
+
+ if (is_array($value)) {
+ $lines[] = $indentedKey.' => [';
+ $lines[] = $this->build($value, $indent + 1);
+ $lines[] = str_repeat(' ', $indent * 4).'],';
+ } else {
+ $lines[] = $indentedKey.' => '.var_export($value, true).',';
+ }
+ }
+
+ return implode(PHP_EOL, $lines);
+ }
+
+ protected function print($body): string
+ {
+ return implode(PHP_EOL, [
+ 'getContents(), true);
+
+ if (! is_array($translations)) {
+ throw new InvalidArgumentException("Unable to extract an array from {$file->getPathname()}!");
+ }
+
+ return new Collection($translations);
+ }
+}
diff --git a/src/Readers/LanguageFileReader.php b/src/Readers/LanguageFileReader.php
new file mode 100644
index 0000000..d199844
--- /dev/null
+++ b/src/Readers/LanguageFileReader.php
@@ -0,0 +1,32 @@
+getExtension();
+ $translations = new Collection();
+
+ if ($this->manager->isEnabled($extension)) {
+ $translations = $this->manager->driver($extension)->getTranslations($file);
+
+ if ($translations->isEmpty()) {
+ throw new InvalidArgumentException("Unable to extract any data from {$file->getPathname()}!");
+ }
+ }
+
+ return $translations;
+ }
+}
diff --git a/src/Readers/PhpLanguageFileReader.php b/src/Readers/PhpLanguageFileReader.php
new file mode 100644
index 0000000..8055538
--- /dev/null
+++ b/src/Readers/PhpLanguageFileReader.php
@@ -0,0 +1,43 @@
+getPath())
+ ->finish(DIRECTORY_SEPARATOR)
+ ->after(DIRECTORY_SEPARATOR.$locale.DIRECTORY_SEPARATOR)
+ ->append($file->getFilenameWithoutExtension())
+ ->toString();
+ }
+
+ public function getNamespaceHintedKey(SplFileInfo $file, string $locale, string $namespaceHint, string $key): string
+ {
+ return Str::of($namespaceHint)
+ ->whenNotEmpty(fn (Stringable $str) => $str->append('::'))
+ ->append($key)
+ ->toString();
+ }
+
+ public function getTranslations(SplFileInfo $file): Collection
+ {
+ $translations = include $file->getPathname();
+
+ if (! is_array($translations)) {
+ throw new InvalidArgumentException("Unable to extract an array from {$file->getPathname()}!");
+ }
+
+ return new Collection($translations);
+ }
+}
diff --git a/tests/.pest/snapshots/ExampleTest/it_can_test.snap b/tests/.pest/snapshots/ExampleTest/it_can_test.snap
new file mode 100644
index 0000000..eb7dbe5
--- /dev/null
+++ b/tests/.pest/snapshots/ExampleTest/it_can_test.snap
@@ -0,0 +1,3 @@
+
+ INFO Translations successfully generated from 'en' to 'da'.
+
diff --git a/tests/.pest/snapshots/ExampleTest/it_can_test__2.snap b/tests/.pest/snapshots/ExampleTest/it_can_test__2.snap
new file mode 100644
index 0000000..28f462a
--- /dev/null
+++ b/tests/.pest/snapshots/ExampleTest/it_can_test__2.snap
@@ -0,0 +1,20 @@
+ 'Ï äm üsëd ïn php wïth ä :placeholder änd änöthër ïn bräckëts (Something:placeholder)',
+ 'unused' => 'Ï äm ünüsëd ïn php cläss',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'Ï äm üsëd ïn blädë',
+ 'unused' => 'Ï äm ünüsëd ïn blädë',
+ ],
+ 'lang' => [
+ 'used' => 'Ï äm üsëd ïn blädë',
+ 'unused' => 'Ï äm ünüsëd ïn blädë',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'Ï äm üsëd ïn vüë cömpönënt',
+ 'unused' => 'Ï äm ünüsëd ïn vüë cömpönënt',
+ ],
+];
\ No newline at end of file
diff --git a/tests/.pest/snapshots/ExampleTest/it_can_test__3.snap b/tests/.pest/snapshots/ExampleTest/it_can_test__3.snap
new file mode 100644
index 0000000..3ea6d31
--- /dev/null
+++ b/tests/.pest/snapshots/ExampleTest/it_can_test__3.snap
@@ -0,0 +1,20 @@
+ 'Ï äm üsëd ïn php cläss',
+ 'unused' => 'Ï äm ünüsëd ïn php cläss',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'Ï äm üsëd ïn blädë',
+ 'unused' => 'Ï äm ünüsëd ïn blädë',
+ ],
+ 'lang' => [
+ 'used' => 'Ï äm üsëd ïn blädë',
+ 'unused' => 'Ï äm ünüsëd ïn blädë',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'Ï äm üsëd ïn vüë cömpönënt',
+ 'unused' => 'Ï äm ünüsëd ïn vüë cömpönënt',
+ ],
+];
\ No newline at end of file
diff --git a/tests/.pest/snapshots/ExampleTest/it_can_test__4.snap b/tests/.pest/snapshots/ExampleTest/it_can_test__4.snap
new file mode 100644
index 0000000..3ea6d31
--- /dev/null
+++ b/tests/.pest/snapshots/ExampleTest/it_can_test__4.snap
@@ -0,0 +1,20 @@
+ 'Ï äm üsëd ïn php cläss',
+ 'unused' => 'Ï äm ünüsëd ïn php cläss',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'Ï äm üsëd ïn blädë',
+ 'unused' => 'Ï äm ünüsëd ïn blädë',
+ ],
+ 'lang' => [
+ 'used' => 'Ï äm üsëd ïn blädë',
+ 'unused' => 'Ï äm ünüsëd ïn blädë',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'Ï äm üsëd ïn vüë cömpönënt',
+ 'unused' => 'Ï äm ünüsëd ïn vüë cömpönënt',
+ ],
+];
\ No newline at end of file
diff --git a/tests/.pest/snapshots/ExampleTest/it_can_test__5.snap b/tests/.pest/snapshots/ExampleTest/it_can_test__5.snap
new file mode 100644
index 0000000..3ea6d31
--- /dev/null
+++ b/tests/.pest/snapshots/ExampleTest/it_can_test__5.snap
@@ -0,0 +1,20 @@
+ 'Ï äm üsëd ïn php cläss',
+ 'unused' => 'Ï äm ünüsëd ïn php cläss',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'Ï äm üsëd ïn blädë',
+ 'unused' => 'Ï äm ünüsëd ïn blädë',
+ ],
+ 'lang' => [
+ 'used' => 'Ï äm üsëd ïn blädë',
+ 'unused' => 'Ï äm ünüsëd ïn blädë',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'Ï äm üsëd ïn vüë cömpönënt',
+ 'unused' => 'Ï äm ünüsëd ïn vüë cömpönënt',
+ ],
+];
\ No newline at end of file
diff --git a/tests/.pest/snapshots/ExampleTest/it_can_test__6.snap b/tests/.pest/snapshots/ExampleTest/it_can_test__6.snap
new file mode 100644
index 0000000..4480673
--- /dev/null
+++ b/tests/.pest/snapshots/ExampleTest/it_can_test__6.snap
@@ -0,0 +1,8 @@
+{
+ "Used PHP Class": "Ï äm üsëd ïn php cläss",
+ "Unused PHP Class": "Ï äm ünüsëd ïn php cläss",
+ "Used Blade File": "Ï äm üsëd ïn blädë",
+ "Unused Blade File": "Ï äm ünüsëd ïn blädë",
+ "Used Vue Component": "Ï äm üsëd ïn vüë cömpönënt",
+ "Unused Vue Component": "Ï äm ünüsëd ïn vüë cömpönënt"
+}
\ No newline at end of file
diff --git a/tests/.pest/snapshots/ExampleTest/it_can_test__7.snap b/tests/.pest/snapshots/ExampleTest/it_can_test__7.snap
new file mode 100644
index 0000000..dae73fc
--- /dev/null
+++ b/tests/.pest/snapshots/ExampleTest/it_can_test__7.snap
@@ -0,0 +1,8 @@
+{
+ "Used Vendor PHP Class": "Ï äm üsëd ïn php cläss",
+ "Unused Vendor PHP Class": "Ï äm ünüsëd ïn php cläss",
+ "Used Vendor Blade File": "Ï äm üsëd ïn blädë",
+ "Unused Vendor Blade File": "Ï äm ünüsëd ïn blädë",
+ "Used Vendor Vue Component": "Ï äm üsëd ïn vüë cömpönënt",
+ "Unused Vendor Vue Component": "Ï äm ünüsëd ïn vüë cömpönënt"
+}
\ No newline at end of file
diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php
index 5d36321..3003e1e 100644
--- a/tests/ExampleTest.php
+++ b/tests/ExampleTest.php
@@ -1,5 +1,41 @@
deleteDirectory(lang_path('da'));
+ $files->deleteDirectory(workbench_path('vendor/example/lang/da'));
+ $files->delete(lang_path('da.json'));
+ $files->delete(workbench_path('vendor/example/lang/da.json'));
+});
+
it('can test', function () {
- expect(true)->toBeTrue();
+ withoutMockingConsoleOutput();
+ artisan('translation:fake da');
+ expect(Artisan::output())->toMatchSnapshot();
+
+ $files = array_merge(
+ glob(lang_path('da/*.*')),
+ glob(lang_path('da/**/*.*')),
+ glob(workbench_path('vendor/example/lang/da/*.*')),
+ glob(workbench_path('vendor/example/lang/da/**/*.*')),
+ [
+ lang_path('da.json'),
+ workbench_path('vendor/example/lang/da.json'),
+ ]
+ );
+
+ foreach ($files as $file) {
+ expect($file)
+ ->toBeReadableFile()
+ ->and(file_get_contents($file))
+ ->toMatchSnapshot();
+ }
});
diff --git a/tests/TestCase.php b/tests/TestCase.php
index 2734ee2..7fbe151 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -3,34 +3,23 @@
namespace Fidum\LaravelTranslationFaker\Tests;
use Fidum\LaravelTranslationFaker\LaravelTranslationFakerServiceProvider;
-use Illuminate\Database\Eloquent\Factories\Factory;
use Orchestra\Testbench\TestCase as Orchestra;
+use Workbench\App\Providers\WorkbenchServiceProvider;
+
+use function Orchestra\Testbench\workbench_path;
class TestCase extends Orchestra
{
- protected function setUp(): void
- {
- parent::setUp();
-
- Factory::guessFactoryNamesUsing(
- fn (string $modelName) => 'Fidum\\LaravelTranslationFaker\\Database\\Factories\\'.class_basename($modelName).'Factory'
- );
- }
-
protected function getPackageProviders($app)
{
return [
LaravelTranslationFakerServiceProvider::class,
+ WorkbenchServiceProvider::class,
];
}
- public function getEnvironmentSetUp($app)
+ protected function getEnvironmentSetUp($app)
{
- config()->set('database.default', 'testing');
-
- /*
- $migration = include __DIR__.'/../database/migrations/create_laravel-translation-faker_table.php.stub';
- $migration->up();
- */
+ $app->setBasePath(workbench_path());
}
}
diff --git a/workbench/app/Providers/WorkbenchServiceProvider.php b/workbench/app/Providers/WorkbenchServiceProvider.php
index 001d06d..8717001 100644
--- a/workbench/app/Providers/WorkbenchServiceProvider.php
+++ b/workbench/app/Providers/WorkbenchServiceProvider.php
@@ -2,24 +2,14 @@
namespace Workbench\App\Providers;
-use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
+use function Orchestra\Testbench\workbench_path;
+
class WorkbenchServiceProvider extends ServiceProvider
{
- /**
- * Register services.
- */
- public function register(): void
- {
- //
- }
-
- /**
- * Bootstrap services.
- */
public function boot(): void
{
- Route::view('/', 'welcome');
+ $this->loadTranslationsFrom(workbench_path('/vendor/example/lang'), 'example');
}
}
diff --git a/workbench/bootstrap/cache/.gitignore b/workbench/bootstrap/cache/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/workbench/bootstrap/cache/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/workbench/lang/de.json b/workbench/lang/de.json
new file mode 100644
index 0000000..9a664b8
--- /dev/null
+++ b/workbench/lang/de.json
@@ -0,0 +1,8 @@
+{
+ "Used PHP Class": "Ich werde in einer PHP-Klasse verwendet",
+ "Unused PHP Class": "Ich werde in einer PHP-Klasse nicht verwendet",
+ "Used Blade File": "Ich werde in Blade verwendet",
+ "Unused Blade File": "Ich werde in Blade nicht verwendet",
+ "Used Vue Component": "Ich werde in einem Vue-Komponenten verwendet",
+ "Unused Vue Component": "Ich werde in einem Vue-Komponenten nicht verwendet"
+}
diff --git a/workbench/lang/de/example.php b/workbench/lang/de/example.php
new file mode 100644
index 0000000..644845b
--- /dev/null
+++ b/workbench/lang/de/example.php
@@ -0,0 +1,20 @@
+ 'Ich werde in einer PHP-Klasse verwendet',
+ 'unused' => 'Ich werde in einer PHP-Klasse nicht verwendet',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'Ich werde in Blade verwendet',
+ 'unused' => 'Ich werde in Blade nicht verwendet',
+ ],
+ 'lang' => [
+ 'used' => 'Ich werde in Blade verwendet',
+ 'unused' => 'Ich werde in Blade nicht verwendet',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'Ich werde in einem Vue-Komponenten verwendet',
+ 'unused' => 'Ich werde in einem Vue-Komponenten nicht verwendet',
+ ],
+];
diff --git a/workbench/lang/de/folder/example.php b/workbench/lang/de/folder/example.php
new file mode 100644
index 0000000..644845b
--- /dev/null
+++ b/workbench/lang/de/folder/example.php
@@ -0,0 +1,20 @@
+ 'Ich werde in einer PHP-Klasse verwendet',
+ 'unused' => 'Ich werde in einer PHP-Klasse nicht verwendet',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'Ich werde in Blade verwendet',
+ 'unused' => 'Ich werde in Blade nicht verwendet',
+ ],
+ 'lang' => [
+ 'used' => 'Ich werde in Blade verwendet',
+ 'unused' => 'Ich werde in Blade nicht verwendet',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'Ich werde in einem Vue-Komponenten verwendet',
+ 'unused' => 'Ich werde in einem Vue-Komponenten nicht verwendet',
+ ],
+];
diff --git a/workbench/lang/en.json b/workbench/lang/en.json
new file mode 100644
index 0000000..8988c16
--- /dev/null
+++ b/workbench/lang/en.json
@@ -0,0 +1,8 @@
+{
+ "Used PHP Class": "I am used in php class",
+ "Unused PHP Class": "I am unused in php class",
+ "Used Blade File": "I am used in blade",
+ "Unused Blade File": "I am unused in blade",
+ "Used Vue Component": "I am used in vue component",
+ "Unused Vue Component": "I am unused in vue component"
+}
diff --git a/workbench/lang/en/example.php b/workbench/lang/en/example.php
new file mode 100644
index 0000000..0a0a6a7
--- /dev/null
+++ b/workbench/lang/en/example.php
@@ -0,0 +1,20 @@
+ 'I am used in php with a :placeholder and another in brackets (Something:placeholder)',
+ 'unused' => 'I am unused in php class',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'I am used in blade',
+ 'unused' => 'I am unused in blade',
+ ],
+ 'lang' => [
+ 'used' => 'I am used in blade',
+ 'unused' => 'I am unused in blade',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'I am used in vue component',
+ 'unused' => 'I am unused in vue component',
+ ],
+];
diff --git a/workbench/lang/en/folder/example.php b/workbench/lang/en/folder/example.php
new file mode 100644
index 0000000..391dc72
--- /dev/null
+++ b/workbench/lang/en/folder/example.php
@@ -0,0 +1,20 @@
+ 'I am used in php class',
+ 'unused' => 'I am unused in php class',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'I am used in blade',
+ 'unused' => 'I am unused in blade',
+ ],
+ 'lang' => [
+ 'used' => 'I am used in blade',
+ 'unused' => 'I am unused in blade',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'I am used in vue component',
+ 'unused' => 'I am unused in vue component',
+ ],
+];
diff --git a/workbench/resources/js/ExampleComponent.vue b/workbench/resources/js/ExampleComponent.vue
new file mode 100644
index 0000000..d24942e
--- /dev/null
+++ b/workbench/resources/js/ExampleComponent.vue
@@ -0,0 +1,22 @@
+
+ {{ __('example.vue.used', {foo: 'bar'}) }}
+ {{ __(
+ 'folder/example.vue.used',
+ {foo: 'bar'}
+ )}}
+
+
+
diff --git a/workbench/resources/views/welcome.blade.php b/workbench/resources/views/welcome.blade.php
new file mode 100644
index 0000000..b694b7a
--- /dev/null
+++ b/workbench/resources/views/welcome.blade.php
@@ -0,0 +1,22 @@
+@lang('example.blade.lang.used', ['foo' => 'bar'])
+@lang(
+ 'folder/example.blade.lang.used',
+ ['foo' => 'bar'],
+)
+@lang('example::example.blade.lang.used')
+@lang(
+ "example::folder/example.blade.lang.used"
+)
+
+{{ __('Used Blade File') }}
+{{ __("Used Vendor Blade File") }}
+
+@if(true)
+ @choice('example.blade.choice.used', 1)
+ @choice('folder/example.blade.choice.used', 1)
+ @choice('example::example.blade.choice.used', 1)
+ @choice(
+ "example::folder/example.blade.choice.used",
+ 1,
+ )
+@endif
diff --git a/workbench/vendor/example/lang/de.json b/workbench/vendor/example/lang/de.json
new file mode 100644
index 0000000..d5363e8
--- /dev/null
+++ b/workbench/vendor/example/lang/de.json
@@ -0,0 +1,8 @@
+{
+ "Used Vendor PHP Class": "Ich werde in einer PHP-Klasse verwendet",
+ "Unused Vendor PHP Class": "Ich werde in einer PHP-Klasse nicht verwendet",
+ "Used Vendor Blade File": "Ich werde in Blade verwendet",
+ "Unused Vendor Blade File": "Ich werde in Blade nicht verwendet",
+ "Used Vendor Vue Component": "Ich werde in einem Vue-Komponenten verwendet",
+ "Unused Vendor Vue Component": "Ich werde in einem Vue-Komponenten nicht verwendet"
+}
diff --git a/workbench/vendor/example/lang/en.json b/workbench/vendor/example/lang/en.json
new file mode 100644
index 0000000..52ec4d3
--- /dev/null
+++ b/workbench/vendor/example/lang/en.json
@@ -0,0 +1,8 @@
+{
+ "Used Vendor PHP Class": "I am used in php class",
+ "Unused Vendor PHP Class": "I am unused in php class",
+ "Used Vendor Blade File": "I am used in blade",
+ "Unused Vendor Blade File": "I am unused in blade",
+ "Used Vendor Vue Component": "I am used in vue component",
+ "Unused Vendor Vue Component": "I am unused in vue component"
+}
diff --git a/workbench/vendor/example/lang/en/example.php b/workbench/vendor/example/lang/en/example.php
new file mode 100644
index 0000000..391dc72
--- /dev/null
+++ b/workbench/vendor/example/lang/en/example.php
@@ -0,0 +1,20 @@
+ 'I am used in php class',
+ 'unused' => 'I am unused in php class',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'I am used in blade',
+ 'unused' => 'I am unused in blade',
+ ],
+ 'lang' => [
+ 'used' => 'I am used in blade',
+ 'unused' => 'I am unused in blade',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'I am used in vue component',
+ 'unused' => 'I am unused in vue component',
+ ],
+];
diff --git a/workbench/vendor/example/lang/en/folder/example.php b/workbench/vendor/example/lang/en/folder/example.php
new file mode 100644
index 0000000..391dc72
--- /dev/null
+++ b/workbench/vendor/example/lang/en/folder/example.php
@@ -0,0 +1,20 @@
+ 'I am used in php class',
+ 'unused' => 'I am unused in php class',
+ 'blade' => [
+ 'choice' => [
+ 'used' => 'I am used in blade',
+ 'unused' => 'I am unused in blade',
+ ],
+ 'lang' => [
+ 'used' => 'I am used in blade',
+ 'unused' => 'I am unused in blade',
+ ],
+ ],
+ 'vue' => [
+ 'used' => 'I am used in vue component',
+ 'unused' => 'I am unused in vue component',
+ ],
+];