-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add OpenAI integration #9
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
/.idea | ||
/vendor | ||
composer.lock | ||
.DS_Store |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,25 +13,67 @@ Did you buy a Laravel script and it doesn't have your language in the`lang`file? | |
Do you want to make your web application bilingual, but you don't know how to translate all those words?</br> | ||
Do you not have the possibility to use the JSON format that many Laravel language translation packages give you? | ||
|
||
|
||
# Installation | ||
``` | ||
composer require alisalehi/laravel-lang-files-translator | ||
``` | ||
|
||
# 💎Usage | ||
After installation, publish the configuration file: | ||
``` | ||
php artisan vendor:publish --tag=lang-files-translator-config | ||
``` | ||
|
||
# Configuration | ||
|
||
The package supports two translation providers: | ||
- Google Translate (default) | ||
- OpenAI (requires API key) | ||
|
||
To use OpenAI for translations, set your API key in `.env`: | ||
``` | ||
php artisan translate:lang {from} {to} | ||
OPENAI_API_KEY=your-api-key | ||
``` | ||
for example, your locale is English and you have en lang files and want to have these files to Persian(fa) lang too. | ||
just enough to run: | ||
|
||
# 💎Usage | ||
|
||
The package provides a simple artisan command to translate your language files: | ||
|
||
```bash | ||
php artisan translate:lang {from} {to} [options] | ||
``` | ||
|
||
## Available Options | ||
|
||
### Using Google Translate (Default) | ||
```bash | ||
php artisan translate:lang en fa | ||
``` | ||
and done! | ||
Go to lang/fa and you will see all the translated files from the en folder. | ||
No configuration needed, just run the command. | ||
|
||
### Using OpenAI | ||
First, add your OpenAI API key to `.env`: | ||
``` | ||
OPENAI_API_KEY=your-api-key | ||
``` | ||
|
||
Then run the command with OpenAI options: | ||
```bash | ||
php artisan translate:lang en fa --provider=openai --model=gpt-3.5-turbo | ||
``` | ||
|
||
how to use video ⤵️ | ||
## Command Reference | ||
|
||
### Arguments: | ||
- `from`: Source language code (e.g., en) | ||
- `to`: Target language code (e.g., fa) | ||
|
||
### Options: | ||
- `--provider`: Translation provider (google or openai) | ||
- `--model`: OpenAI model name (required when using openai) | ||
|
||
After running the command, translated files will be created in the `lang/{target-language}` folder. | ||
|
||
## Demo Video ⤵️ | ||
|
||
https://github.com/alisalehi1380/laravel-lang-files-translator/assets/111766206/748eaba0-29a3-4782-8505-1d8368d44ed2 | ||
|
||
|
@@ -42,14 +84,17 @@ As Einstein said, **"There's a way to do it better!"** So I welcome any change t | |
|
||
Just open an issue or pull a request. | ||
|
||
|
||
## License | ||
The MIT License (MIT). See **[License File](https://github.com/alisalehi1380/laravel-lang-files-translator/blob/master/LICENSE)** for more information. | ||
|
||
## ❤️Contributing | ||
This project exists thanks to all the people who | ||
contribute. [CONTRIBUTING](https://github.com/alisalehi/laravel-lang-files-translator/graphs/contributors) | ||
This project exists thanks to all the people who contribute: | ||
|
||
- [Ali Salehi](https://github.com/alisalehi1380) - Original author and maintainer | ||
- [Amirmohammad Mokhtari](https://github.com/am-mokhtari) | ||
- [Navid Mirzaaghazadeh](https://github.com/mirzaaghazadeh) - Implemented OpenAI integration for natural language translation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. همون طوری که همه مون میدونیم از اپن سورس پولی در نمیاد. فقط اعتبار هست که بهمون میده. منم کلا بردار. ما کاری نکردیم که. هر چی بوده زحمات شما و دوستان بوده. |
||
|
||
See all [contributors](https://github.com/alisalehi1380/laravel-lang-files-translator/graphs/contributors). | ||
|
||
[img-package]: https://banners.beyondco.de/laravel-lang-files-translator%20.png?theme=dark&packageManager=composer+require&packageName=alisalehi%2Flaravel-lang-files-translator&pattern=fourPointStars&style=style_1&description=Easiest+way+to+translate+lang+files&md=1&showWatermark=0&fontSize=100px&images=translate | ||
[ico-laravel]: https://img.shields.io/packagist/dependency-v/alisalehi/laravel-lang-files-translator/laravel/framework.svg?color=%23f13c2f | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php | ||
|
||
return [ | ||
/* | ||
|-------------------------------------------------------------------------- | ||
| OpenAI Configuration | ||
|-------------------------------------------------------------------------- | ||
| | ||
| Configure OpenAI API settings for translation when using OpenAI provider | ||
| | ||
*/ | ||
'chatgpt' => [ | ||
'api_key' => env('OPENAI_API_KEY'), | ||
'temperature' => 0.3, // Controls translation creativity (0.0 = precise, 1.0 = creative) | ||
], | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,19 +7,36 @@ | |
|
||
class Translate extends Command | ||
{ | ||
protected $signature = 'translate:lang {from : translate from language} {to : translate to language}'; | ||
protected $signature = 'translate:lang {from : translate from language} {to : translate to language} | ||
{--provider=google : translation provider (google or openai)} | ||
{--model= : OpenAI model name (required when provider is openai)}'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. برای راحتی استفاده، امکانش هست یه مدل پیشفرض داشته باشیم؟ اگر کاربر خواست تغییرش بده. تو README دیدم نوشته بودی. اگه صلاح میدونی که خوبه، اینجا دیفالت همون رو بزار. |
||
|
||
protected $description = 'translate lang files'; | ||
|
||
public function handle(TranslateService $translateService) | ||
{ | ||
$this->info('start translation. please wait...'); | ||
$provider = $this->option('provider'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. مدل رو هم بگیر
|
||
if (!in_array($provider, ['google', 'openai'])) { | ||
$this->error('Invalid provider. Use either "google" or "openai"'); | ||
return 1; | ||
} | ||
|
||
if ($provider === 'openai' && empty($this->option('model'))) { | ||
$this->error('Model name is required when using OpenAI provider'); | ||
return 1; | ||
} | ||
|
||
$this->info('Start translation using ' . strtoupper($provider) . ' provider...'); | ||
$this->info(PHP_EOL . 'The speed of translation of files depends on the speed of the Internet,'); | ||
$this->info('the number of file lines and the indentation of each key.'); | ||
$this->info('So please be patient until the translation of the files is finished.'); | ||
$this->info('Thankful'); | ||
|
||
$translateService->to($this->argument('to'))->from($this->argument('from'))->translate(); | ||
$translateService | ||
->to($this->argument('to')) | ||
->from($this->argument('from')) | ||
->withProvider($provider, $this->option('model')) | ||
->translate(); | ||
|
||
$this->getOutput()->writeln(PHP_EOL . ' - Finished translation! (go to lang/' . $this->argument('to') . ' folder) '); | ||
|
||
|
@@ -37,4 +54,4 @@ public function thanks(): void | |
$this->line('<fg=blue>|-------------------------------------------------|</>'); | ||
$this->line('https://github.com/alisalehi1380/laravel-lang-files-translator'); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?php | ||
|
||
namespace Alisalehi\LaravelLangFilesTranslator\Contracts; | ||
|
||
interface TranslatorInterface | ||
{ | ||
public function setSource(string $source): self; | ||
public function setTarget(string $target): self; | ||
public function translate(string $text): string; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,13 +3,16 @@ | |
namespace Alisalehi\LaravelLangFilesTranslator\Services; | ||
|
||
use Illuminate\Support\Facades\File; | ||
use Stichoza\GoogleTranslate\GoogleTranslate; | ||
use Alisalehi\LaravelLangFilesTranslator\Contracts\TranslatorInterface; | ||
use Alisalehi\LaravelLangFilesTranslator\Services\TranslatorFactory; | ||
use Symfony\Component\Finder\SplFileInfo; | ||
|
||
class TranslateService | ||
{ | ||
private string $translate_from; | ||
private string $translate_to; | ||
private string $provider = 'google'; | ||
private ?string $model = null; | ||
|
||
//setters | ||
public function from(string $from): TranslateService | ||
|
@@ -23,6 +26,13 @@ public function to(string $to): TranslateService | |
$this->translate_to = $to; | ||
return $this; | ||
} | ||
|
||
public function withProvider(string $provider, ?string $model = null): TranslateService | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. بشه |
||
{ | ||
$this->provider = $provider; | ||
$this->model = $model; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. به نظرم دیفالت گذاشتن اینها خیلی منطقی نیست. خطوط 14 و 15 در اینجا
مقادیر provider و model رو داریم پاس میدیم. در خطوط بالایی هم تمام ولیدیشن هایی که برای این دو فیلد نیاز بوده، چک شده. پس اینجا قطعا این مقادیر وجود دارند. دیفالت null رو هم دیگه نیازی بهش نداریم.
|
||
return $this; | ||
} | ||
|
||
public function translate(): void | ||
{ | ||
|
@@ -60,30 +70,30 @@ private function getTranslatedData(SplFileInfo $file): string | |
return $this->addPhpSyntax($translatedData); | ||
} | ||
|
||
private function setUpGoogleTranslate(): GoogleTranslate | ||
private function getTranslator(): TranslatorInterface | ||
{ | ||
$google = new GoogleTranslate(); | ||
return $google->setSource($this->translate_from) | ||
return TranslatorFactory::create($this->provider, $this->model) | ||
->setSource($this->translate_from) | ||
->setTarget($this->translate_to); | ||
} | ||
|
||
private function translateLangFiles(array $content): array | ||
{ | ||
$google = $this->setUpGoogleTranslate(); | ||
|
||
if (empty($content)) | ||
if (empty($content)) { | ||
return []; | ||
} | ||
|
||
return $this->translateRecursive($content, $google); | ||
$translator = $this->getTranslator(); | ||
return $this->translateRecursive($content, $translator); | ||
} | ||
|
||
private function translateRecursive($content, $google) : array | ||
private function translateRecursive($content, TranslatorInterface $translator): array | ||
{ | ||
$trans_data = []; | ||
|
||
foreach ($content as $key => $value) { | ||
if (is_array($value)) { | ||
$trans_data[$key] = $this->translateRecursive($value, $google); | ||
$trans_data[$key] = $this->translateRecursive($value, $translator); | ||
continue; | ||
} | ||
|
||
|
@@ -96,7 +106,7 @@ private function translateRecursive($content, $google) : array | |
) | ||
: $value; | ||
|
||
$translatedValue = $google->translate($modifiedValue); | ||
$translatedValue = $translator->translate($modifiedValue); | ||
|
||
$trans_data[$key] = $hasProps | ||
? str_replace(['{', '}'], '', $translatedValue) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
namespace Alisalehi\LaravelLangFilesTranslator\Services; | ||
|
||
use Alisalehi\LaravelLangFilesTranslator\Contracts\TranslatorInterface; | ||
use Alisalehi\LaravelLangFilesTranslator\Services\Translators\ChatGPTTranslator; | ||
use Alisalehi\LaravelLangFilesTranslator\Services\Translators\GoogleTranslator; | ||
|
||
class TranslatorFactory | ||
{ | ||
public static function create(string $provider = 'google', ?string $model = null): TranslatorInterface | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. نیازی به دیفالت نیست. |
||
{ | ||
if ($provider === 'openai') { | ||
if (empty($model)) { | ||
throw new \InvalidArgumentException('Model parameter is required for OpenAI provider'); | ||
} | ||
Comment on lines
+14
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. نیازی به چک کردن نیست. قبلا چک شده و اینجا حتما هستش |
||
$translator = new ChatGPTTranslator(); | ||
$translator->setModel($model); | ||
return $translator; | ||
} | ||
|
||
if ($provider === 'google') { | ||
return new GoogleTranslator(); | ||
} | ||
|
||
throw new \InvalidArgumentException("Unsupported translator provider: {$provider}"); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. به نظرم داریم لقمه رو میپیچونیم :) نظرت چیه تو همون اگه بخواییم خیلی وسواسی بریم جلو اینم باید با singleton پیاده سازیش کنیم که مطمئن باشیم در طول اجرا فقط و فقط یک instance وجود داره. ولی همین اوکیه. نیازی نیست تغییرش بدی. 🙏 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<?php | ||
|
||
namespace Alisalehi\LaravelLangFilesTranslator\Services\Translators; | ||
|
||
use Alisalehi\LaravelLangFilesTranslator\Contracts\TranslatorInterface; | ||
use Illuminate\Support\Facades\Http; | ||
|
||
class ChatGPTTranslator implements TranslatorInterface | ||
{ | ||
private string $source; | ||
private string $target; | ||
private string $apiKey; | ||
private string $model; | ||
private float $temperature; | ||
|
||
public function __construct() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. میتونیم از Constructor property promotion ورژن 8 استفاده کنیم و این طوری بنویسیم.
|
||
{ | ||
$this->apiKey = config('lang-files-translator.chatgpt.api_key'); | ||
$this->temperature = config('lang-files-translator.chatgpt.temperature', 0.3); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. به نظرم چون توی کانفیگ نوشتی، از اینجا برش دار.
|
||
|
||
if (empty($this->apiKey)) { | ||
throw new \RuntimeException('OpenAI API key is not configured. Set OPENAI_API_KEY in your .env file.'); | ||
} | ||
} | ||
|
||
public function setModel(string $model): self | ||
{ | ||
$this->model = $model; | ||
return $this; | ||
} | ||
|
||
public function setSource(string $source): TranslatorInterface | ||
{ | ||
$this->source = $source; | ||
return $this; | ||
} | ||
|
||
public function setTarget(string $target): TranslatorInterface | ||
{ | ||
$this->target = $target; | ||
return $this; | ||
} | ||
|
||
public function translate(string $text): string | ||
{ | ||
$response = Http::withHeaders([ | ||
'Authorization' => 'Bearer ' . $this->apiKey, | ||
'Content-Type' => 'application/json', | ||
])->post('https://api.openai.com/v1/chat/completions', [ | ||
'model' => $this->model, | ||
'messages' => [ | ||
[ | ||
'role' => 'system', | ||
'content' => "You are a professional translator. Translate from {$this->source} to {$this->target}. Maintain any variables or placeholders in the text. Only return the translated text without explanations or additional context.", | ||
], | ||
[ | ||
'role' => 'user', | ||
'content' => $text, | ||
], | ||
], | ||
'temperature' => $this->temperature, | ||
]); | ||
|
||
if (!$response->successful()) { | ||
throw new \RuntimeException('ChatGPT API request failed: ' . $response->body()); | ||
} | ||
|
||
return trim($response->json('choices.0.message.content')); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
اینو بزار اینجا.
کلا زمانی که زیاد توضیح میدی طرف فکر میکنه خیلی سخته کارکردن با پکیج شما.
تمام توضیحاتی که دادی رو باید طرف با کامند
php artisan translate:lang -help
در بیاره.نکته آخر: به طرف اجازه نده فکر کنه. اون دلش میخواد فقط کامند ها رو کپی پیست کنه :)
publish the configuration:
💎Usage
Google Translate (Default)
OpenAI
set your API key in
.env
:Then run the command with OpenAI options: