Skip to content

Commit d8b23f9

Browse files
committed
Download the Franc binary and tests
1 parent 23c7b88 commit d8b23f9

11 files changed

+172
-27
lines changed

README.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,26 @@ You can install the package via composer:
1616
composer require oneofftech/laravel-language-recognizer
1717
```
1818

19+
The language recognition, when performed using the local driver,
20+
is done using the [Franc](https://github.com/wooorm/franc) library, in particular
21+
a [packaged version](https://github.com/avvertix/franc-bin) in form on an executable.
22+
23+
To download the executable version run:
24+
25+
```bash
26+
php artisan language-recognizer:install-local-driver
27+
```
28+
1929
You can publish the config file with:
30+
2031
```bash
2132
php artisan vendor:publish --provider="Oneofftech\LaravelLanguageRecognizer\LaravelLanguageRecognizerServiceProvider" --tag="laravel-language-recognizer-config"
2233
```
2334

24-
This is the contents of the published config file:
35+
> If you change the path to the Franc binary, as configured in the local driver, ensure that the file is moved or present in that location. You can run `php artisan language-recognizer:install-local-driver` to download the binary in the configured location
2536
26-
```php
27-
return [
28-
];
29-
```
37+
This configuration file allows to specifiy the drivers for performing the language
38+
recognition and their eventual options.
3039

3140
## Usage
3241

@@ -38,6 +47,8 @@ LanguageRecognizer::recognize('Which language is used in this string!');
3847

3948
## Testing
4049

50+
A test suite is available. To execute the tests run:
51+
4152
```bash
4253
composer test
4354
```

composer.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,16 @@
1919
"php": "^7.4|^8.0",
2020
"illuminate/contracts": "^8.37",
2121
"illuminate/support": "^8.37",
22+
"illuminate/http": "^8.37",
2223
"symfony/process": "^5.1.4"
2324
},
2425
"require-dev": {
2526
"brianium/paratest": "^6.2",
2627
"nunomaduro/collision": "^5.3",
2728
"orchestra/testbench": "^6.15",
2829
"phpunit/phpunit": "^9.3",
29-
"vimeo/psalm": "^4.8"
30-
},
31-
"suggest": {
32-
"illuminate/http": "To allow download of language recognizer executable from https://github.com/avvertix/franc-bin"
30+
"vimeo/psalm": "^4.8",
31+
"guzzlehttp/guzzle": "^7.0.1"
3332
},
3433
"autoload": {
3534
"psr-4": {
@@ -43,7 +42,7 @@
4342
},
4443
"scripts": {
4544
"psalm": "vendor/bin/psalm",
46-
"test": "./vendor/bin/testbench package:test --parallel --no-coverage",
45+
"test": "vendor/bin/phpunit --no-coverage",
4746
"test-coverage": "vendor/bin/phpunit --coverage-html coverage"
4847
},
4948
"config": {

config/language-recognizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
|
1717
| The binary path must end with the linux executable file name
1818
*/
19-
'path' => env('LANGUAGE_RECOGNIZER_LOCAL_BIN_PATH', './bin/language-guesser'),
19+
'path' => env('LANGUAGE_RECOGNIZER_LOCAL_BIN_PATH', './bin/language-recognizer'),
2020
// 'exclude' => ['cat','sot','kat','bcl','glg','lao','lit','umb','tsn','vec','nso','ban','bug','knc','kng','ibb','lug','ace','bam','tzm','ydd','kmb','lun','shn','war','dyu','wol','nds','mkd','vmw','zgh','ewe','khk','slv','ayr','bem','emk','bci','bum','epo','pam','tiv','tpi','ven','ssw','nyn','kbd','iii','yao','lav','quz','src','rup','sco','tso','rmy','men','fon','nhn','dip','kde','snn','kbp','tem','toi','est','snk','cjk','ada','aii','quy','rmn','bin','gaa','ndo'],
2121
],
2222

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
3+
namespace Oneofftech\LaravelLanguageRecognizer\Commands;
4+
5+
use Exception;
6+
use Illuminate\Console\Command;
7+
use Illuminate\Support\Str;
8+
use Illuminate\Support\Facades\Http;
9+
10+
class InstallLocalRecognizerCommand extends Command
11+
{
12+
public $signature = 'language-recognizer:install-local-driver {--path=}';
13+
14+
public $description = 'Install the local Franc binary used to recognize the language of a text';
15+
16+
17+
/**
18+
* Artifacts urls keyed based on operating system
19+
*
20+
* @var array
21+
*/
22+
protected static $urls = [
23+
'linux' => 'https://github.com/avvertix/franc-bin/releases/download/v1.0.0/franc-bin-linux',
24+
'darwin' => 'https://github.com/avvertix/franc-bin/releases/download/v1.0.0/franc-bin-macos',
25+
'winnt' => 'https://github.com/avvertix/franc-bin/releases/download/v1.0.0/franc-bin-win.exe',
26+
];
27+
28+
29+
/**
30+
* @return int
31+
*
32+
*/
33+
public function handle()
34+
{
35+
$this->comment('Downloading Franc from binary from https://github.com/avvertix/franc-bin');
36+
37+
$os = $this->getOs();
38+
39+
$url = self::$urls[$os] ?? null;
40+
41+
if(is_null($url)){
42+
throw new Exception("Unsupported operating system [{$os}]");
43+
}
44+
45+
$this->info($url);
46+
47+
$downloadPath = $this->getDownloadPath();
48+
49+
if(!is_dir($directory = dirname($downloadPath))){
50+
mkdir($directory);
51+
}
52+
53+
Http::withOptions([
54+
'sink' => $downloadPath,
55+
])->timeout(30)->get($url);
56+
57+
$this->comment('All done');
58+
59+
return 0;
60+
}
61+
62+
protected function getOs()
63+
{
64+
return strtolower(PHP_OS);
65+
}
66+
67+
protected function getDownloadPath()
68+
{
69+
$pathOption = $this->option('path');
70+
71+
if($pathOption){
72+
73+
if($this->getOs() === 'winnt' && ! Str::endsWith($pathOption, '.exe')){
74+
return $pathOption.'.exe';
75+
}
76+
77+
return $pathOption;
78+
}
79+
80+
$suffixes = [
81+
'winnt' => '.exe',
82+
];
83+
84+
$configuredPath = config('language-recognizer.drivers.local.path');
85+
86+
$resolvedPath = Str::startsWith($configuredPath, '.') ? base_path($configuredPath) : $configuredPath;
87+
88+
return $resolvedPath . ($suffixes[$this->getOs()] ?? '');
89+
}
90+
}

src/Drivers/LocalLanguageRecognizerDriver.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212

1313
class LocalLanguageRecognizerDriver implements LanguageRecognizer
1414
{
15+
/**
16+
* The path of the Franc binary
17+
*
18+
* @var string
19+
*/
20+
protected $binaryPath = null;
21+
1522
public function __construct($config)
1623
{
1724
if (empty($config['path'])) {
@@ -21,6 +28,9 @@ public function __construct($config)
2128
$this->binaryPath = $this->obtainBinaryPath($config['path']);
2229
}
2330

31+
/**
32+
* @return false|string
33+
*/
2434
protected function obtainBinaryPath($configuredPath)
2535
{
2636
$basePath = Str::startsWith($configuredPath, '.') ? base_path($configuredPath) : $configuredPath;
@@ -66,7 +76,7 @@ public function recognize($text, $limit = 2): array
6676
})->toArray();
6777
}
6878

69-
private function run($text)
79+
private function run($text): string
7080
{
7181
$options = [$this->binaryPath, '--all'];
7282

@@ -97,7 +107,7 @@ private function run($text)
97107
throw new Exception((new ProcessFailedException($process))->getMessage());
98108
}
99109
} catch (ProcessFailedException $ex) {
100-
throw new Exception($ex->getMessage(), $ex->getCode(), $ex);
110+
throw new Exception($ex->getMessage(), 143, $ex);
101111
}
102112

103113
return $process->getOutput();

src/LaravelLanguageRecognizer.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ public function driver($driver = null)
2626
return parent::driver($driver);
2727
}
2828

29+
/**
30+
* @return \Oneofftech\LaravelLanguageRecognizer\Drivers\LocalLanguageRecognizerDriver
31+
*/
2932
protected function createLocalDriver()
3033
{
3134
return new LocalLanguageRecognizerDriver($this->container['config']['language-recognizer.drivers.local']);

src/LaravelLanguageRecognizerServiceProvider.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@
33
namespace Oneofftech\LaravelLanguageRecognizer;
44

55
use Illuminate\Support\ServiceProvider;
6+
use Oneofftech\LaravelLanguageRecognizer\Commands\InstallLocalRecognizerCommand;
67

78
class LaravelLanguageRecognizerServiceProvider extends ServiceProvider
89
{
9-
public function boot()
10+
public function boot(): void
1011
{
1112
if ($this->app->runningInConsole()) {
12-
// $this->commands([
13-
// ScaffoldAuthenticationControllers::class,
14-
// ]);
13+
14+
$this->commands([
15+
InstallLocalRecognizerCommand::class,
16+
]);
17+
1518
$this->publishes([
1619
__DIR__.'/../config/language-recognizer.php' => config_path('language-recognizer.php'),
1720
]);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Oneofftech\LaravelLanguageRecognizer\Tests\Integration;
4+
5+
use Illuminate\Support\Facades\Artisan;
6+
7+
class InstallCommandTest extends TestCase
8+
{
9+
/** @test */
10+
public function download_the_franc_binary_to_custom_path()
11+
{
12+
$exit = Artisan::call('language-recognizer:install-local-driver', ['--path' => __DIR__ . '/../../bin/language-guesser']);
13+
14+
$this->assertEquals(0, $exit);
15+
$this->assertFileExists(__DIR__ . '/../../bin/language-guesser' . (strtolower(PHP_OS) === 'winnt' ?'.exe':'' ));
16+
}
17+
18+
/** @test */
19+
public function download_the_franc_binary_to_default_path()
20+
{
21+
$this->app['config']->set('language-recognizer.drivers.local.path', './bin/language-guesser');
22+
23+
$exit = Artisan::call('language-recognizer:install-local-driver');
24+
25+
$this->assertEquals(0, $exit);
26+
$this->assertFileExists(base_path('bin/language-guesser') . (strtolower(PHP_OS) === 'winnt' ?'.exe':'' ));
27+
}
28+
29+
}

tests/Integration/LaravelLanguageRecognizerFacadeTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class LaravelLanguageRecognizerFacadeTest extends TestCase
1111
public function default_local_driver_can_be_obtained()
1212
{
1313
$this->app['config']->set('language-recognizer.default', 'local');
14-
$this->app['config']->set('language-recognizer.drivers.local.path', __DIR__ . '/../../bin/language-guesser');
14+
$this->app['config']->set('language-recognizer.drivers.local.path', __DIR__ . '/../../bin/language-recognizer');
1515

1616
$service = LanguageRecognizer::driver('local');
1717

@@ -22,7 +22,7 @@ public function default_local_driver_can_be_obtained()
2222
public function recognition_can_be_done_from_facade()
2323
{
2424
$this->app['config']->set('language-recognizer.default', 'local');
25-
$this->app['config']->set('language-recognizer.drivers.local.path', __DIR__ . '/../../bin/language-guesser');
25+
$this->app['config']->set('language-recognizer.drivers.local.path', __DIR__ . '/../../bin/language-recognizer');
2626

2727
$languages = LanguageRecognizer::recognize('This is a string');
2828

tests/Integration/LaravelLanguageRecognizerServiceProviderTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function default_local_driver_can_be_set()
3131
public function default_local_driver_can_be_instantiated()
3232
{
3333
$this->app['config']->set('language-recognizer.default', 'local');
34-
$this->app['config']->set('language-recognizer.drivers.local.path', __DIR__ . '/../../bin/language-guesser');
34+
$this->app['config']->set('language-recognizer.drivers.local.path', __DIR__ . '/../../bin/language-recognizer');
3535

3636
$service = $this->app[LaravelLanguageRecognizer::class];
3737

0 commit comments

Comments
 (0)