Skip to content

Commit

Permalink
Merge pull request #211 from a-drew/master
Browse files Browse the repository at this point in the history
feat: retrieve a list of supported languages
  • Loading branch information
Stichoza authored Aug 5, 2024
2 parents 3e4dbbf + 9885bb5 commit 9429773
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 3 deletions.
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Free Google Translate API PHP Package. Translates totally free of charge.
- **[Basic Usage](#basic-usage)**
- [Advanced Usage](#advanced-usage)
- [Language Detection](#language-detection)
- [Supported Languages](#supported-languages)
- [Preserving Parameters](#preserving-parameters)
- [Using Raw Response](#using-raw-response)
- [Custom URL](#custom-url)
Expand Down Expand Up @@ -92,7 +93,41 @@ echo $tr->getLastDetectedSource(); // Output: en

Return value will be `null` if the language couldn't be detected.

Supported languages are listed in [Google API docs](https://cloud.google.com/translate/docs/languages).
### Supported Languages

You can get a list of all the supported languages using the `languages` method.

```php
$tr = new GoogleTranslate();

$languages = $tr->languages(); // Get supported languages in iso-639 format

// Output: [ 'ab', 'ace', 'ach', 'aa', 'af', 'sq', 'alz', ... ]
```

Optionally, pass a target language code to retrieve supported languages with names displayed in that language.

```php
$tr = new GoogleTranslate();

$languages = $tr->languages('en'); // Get supported languages, display name in english
// Output: [ 'en' => English', 'es' => 'Spanish', 'it' => 'Italian', ... ]

echo $languages['en']; // Output: 'English'
echo $languages['ka']; // Output: 'Georgian'
```

Same as with the `translate`/`trans` methods, you can also use a static `langs` method:

```php
GoogleTranslate::langs();
// Output: [ 'ab', 'ace', 'ach', 'aa', 'af', 'sq', 'alz', ... ]

GoogleTranslate::langs('en');
// Output: [ 'en' => English', 'es' => 'Spanish', 'it' => 'Italian', ... ]
```

Supported languages are also listed in [Google API docs](https://cloud.google.com/translate/docs/languages).

### Preserving Parameters

Expand Down Expand Up @@ -241,4 +276,3 @@ If this package helped you reduce your time to develop something, or it solved a

- [Patreon](https://www.patreon.com/stichoza)
- [PayPal](https://paypal.me/stichoza)

1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"require": {
"php": "^8.0",
"guzzlehttp/guzzle": "^7.0",
"ext-dom": "*",
"ext-json": "*",
"ext-mbstring": "*"
},
Expand Down
10 changes: 10 additions & 0 deletions src/Exceptions/LanguagesRequestException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Stichoza\GoogleTranslate\Exceptions;

use ErrorException;

class LanguagesRequestException extends ErrorException
{
//
}
80 changes: 79 additions & 1 deletion src/GoogleTranslate.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use JsonException;
use Stichoza\GoogleTranslate\Exceptions\LanguagesRequestException;
use Stichoza\GoogleTranslate\Exceptions\LargeTextException;
use Stichoza\GoogleTranslate\Exceptions\RateLimitException;
use Stichoza\GoogleTranslate\Exceptions\TranslationDecodingException;
Expand Down Expand Up @@ -366,7 +367,7 @@ protected function injectParameters(string $string, array $replacements): string
{
// Remove space added by google in the parameters
$string = preg_replace('/#\{\s*(\d+)\s*\}/', '#{$1}', $string);

return preg_replace_callback(
'/\#{(\d+)}/',
fn($matches) => $replacements[$matches[1]],
Expand Down Expand Up @@ -456,4 +457,81 @@ protected function isValidLocale(string $lang): bool
{
return (bool) preg_match('/^([a-z]{2,3})(-[A-Za-z]{2,4})?$/', $lang);
}

/**
* Fetch the list of supported languages from Google Translate
*
* @param string|null $target iso code of display language, when null returns only iso codes
* @return string[]|array<string, string>
* @throws RateLimitException
* @throws LanguagesRequestException
*/
public static function langs(?string $target = null): array
{
return (new self)->languages($target);
}

/**
* Fetch the list of supported languages from Google Translate
*
* @param string|null $target iso code of display language, when null returns only iso codes
* @return string[]|array<string, string>
* @throws RateLimitException
* @throws LanguagesRequestException
*/
public function languages(?string $target = null): array
{
$languages = $this->localizedLanguages($target ?? $this->target ?? $this->source ?? '');

if ($target === null) {
return array_keys($languages);
}

return $languages;
}

/**
* Fetch the list of supported languages from Google Translate
*
* @param string $target iso code of localized display language
* @return array<string, string>
* @throws RateLimitException
* @throws LanguagesRequestException
*/
public function localizedLanguages(string $target): array
{
$menu = 'sl'; // 'tl';
$url = parse_url($this->url);
$url = $url['scheme'].'://'.$url['host']."/m?mui=$menu&hl=$target";

try {
$response = $this->client->get($url, $this->options);
} catch (GuzzleException $e) {
match ($e->getCode()) {
429, 503 => throw new RateLimitException($e->getMessage(), $e->getCode()),
default => throw new LanguagesRequestException($e->getMessage(), $e->getCode()),
};
} catch (Throwable $e) {
throw new LanguagesRequestException($e->getMessage(), $e->getCode());
}

// add a meta tag to ensure the HTML content is treated as UTF-8, fixes xpath node values
$html = preg_replace('/<head>/i', '<head><meta charset="UTF-8">', $response->getBody()->getContents());

// Prepare to crawl DOM
$dom = new \DOMDocument();
$dom->loadHTML($html);
$xpath = new \DOMXPath($dom);

$nodes = $xpath->query('//div[@class="language-item"]/a');

$languages = [];
foreach ($nodes as $node) {
$href = $node->getAttribute('href');
$code = strtok(substr($href, strpos($href, "$menu=") + strlen("$menu=")), '&');
$languages[$code] = $node->nodeValue;
}

return $languages;
}
}
8 changes: 8 additions & 0 deletions tests/ExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use ErrorException;
use PHPUnit\Framework\TestCase;
use Stichoza\GoogleTranslate\Exceptions\LanguagesRequestException;
use Stichoza\GoogleTranslate\Exceptions\LargeTextException;
use Stichoza\GoogleTranslate\Exceptions\RateLimitException;
use Stichoza\GoogleTranslate\Exceptions\TranslationDecodingException;
Expand Down Expand Up @@ -68,4 +69,11 @@ public function testInheritanceForErrorException(): void

$this->tr->setUrl('https://httpstat.us/413')->translate('Test');
}

public function testLanguagesRequestException(): void
{
$this->expectException(LanguagesRequestException::class);

$this->tr->setUrl('https://httpstat.us/418')->languages();
}
}
72 changes: 72 additions & 0 deletions tests/SupportedLanguagesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

namespace Stichoza\GoogleTranslate\Tests;

use PHPUnit\Framework\TestCase;
use Stichoza\GoogleTranslate\GoogleTranslate;

class SupportedLanguagesTest extends TestCase
{
public GoogleTranslate $tr;

public function setUp(): void
{
$this->tr = new GoogleTranslate();
}

public function testLanguageCodesRequest(): void
{
$result = $this->tr->languages();
$this->assertContains('en', $result);
$this->assertContains('fr', $result);
$this->assertContains('ka', $result);
$this->assertContains('it', $result);
$this->assertContains('pt', $result);
$this->assertContains('pt-PT', $result);
$this->assertContains('pl', $result);
$this->assertContains('vi', $result);
$this->assertContains('ja', $result);
$this->assertContains('et', $result);
$this->assertContains('hr', $result);
$this->assertContains('es', $result);
$this->assertContains('zh-CN', $result);
$this->assertContains('zh-TW', $result);
}

public function testLocalizedLanguages(): void
{
$result = $this->tr->languages('en');
$this->assertEquals('English', $result['en']);
$this->assertEquals('French', $result['fr']);
$this->assertEquals('Georgian', $result['ka']);
$this->assertEquals('Italian', $result['it']);
$this->assertEquals('Portuguese (Brazil)', $result['pt']);

$result = $this->tr->languages('ka');
$this->assertEquals('ინგლისური', $result['en']);
$this->assertEquals('ფრანგული', $result['fr']);
$this->assertEquals('ქართული', $result['ka']);
$this->assertEquals('იტალიური', $result['it']);
$this->assertEquals('პორტუგალიური (ბრაზილია)', $result['pt']);

$result = $this->tr->languages('pt');
$this->assertEquals('Inglês', $result['en']);
$this->assertEquals('Francês', $result['fr']);
$this->assertEquals('Georgiano', $result['ka']);
$this->assertEquals('Italiano', $result['it']);
$this->assertEquals('Português (Brasil)', $result['pt']);
}

public function testLanguagesEquality(): void
{
$resultOne = GoogleTranslate::langs();
$resultTwo = $this->tr->languages();

$this->assertEqualsIgnoringCase($resultOne, $resultTwo, 'Static and instance methods should return same result.');

$resultOne = GoogleTranslate::langs('pt');
$resultTwo = $this->tr->languages('pt');

$this->assertEqualsIgnoringCase($resultOne, $resultTwo, 'Static and instance methods should return same result.');
}
}

0 comments on commit 9429773

Please sign in to comment.