feat: 后端全量更新 - 含所有本次需求
- Contract.php: 返回合约账户余额(balance_contract) - My.php: 地址管理增加BTC/ETH - AppContract.php: 一键平仓(closeall) - AppProxy.php: 代理专属注册链接 + 分级权限(L1/L2) - site.php: 手续费减半(0.018→0.009) - agent_permission_setup.sql: 代理权限SQL - crypto_news_crawler.py: 新闻自动采集脚本
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
language: php
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- 7.3
|
||||
|
||||
before_script:
|
||||
- composer self-update
|
||||
- composer install --prefer-source --no-interaction --dev
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2013 Levan Velijanashvili
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
Google Translate PHP
|
||||
====================
|
||||
|
||||
[](https://travis-ci.org/Stichoza/google-translate-php) [](https://packagist.org/packages/stichoza/google-translate-php) [](https://packagist.org/packages/stichoza/google-translate-php) [](https://packagist.org/packages/stichoza/google-translate-php) [](https://www.patreon.com/stichoza) [](https://paypal.me/stichoza)
|
||||
|
||||
Free Google Translate API PHP Package. Translates totally free of charge.
|
||||
|
||||
---
|
||||
|
||||
- **[Installation](#installation)**
|
||||
- **[Basic Usage](#basic-usage)**
|
||||
- [Advanced Usage](#advanced-usage)
|
||||
- [Language Detection](#language-detection)
|
||||
- [Using Raw Response](#using-raw-response)
|
||||
- [Custom URL](#custom-url)
|
||||
- [HTTP Client Configuration](#http-client-configuration)
|
||||
- [Custom Token Generator](#custom-token-generator)
|
||||
- [Errors and Exception Handling](#errors-and-exception-handling)
|
||||
- [Known Limitations](#known-limitations)
|
||||
- [Disclaimer](#disclaimer)
|
||||
- [Donation](#donation)
|
||||
|
||||
## Installation
|
||||
|
||||
Install this package via [Composer](https://getcomposer.org/).
|
||||
|
||||
```
|
||||
composer require stichoza/google-translate-php
|
||||
```
|
||||
|
||||
> Note: **PHP 7.1 or later** is required. For older versoins, use `^3.2` version of this package (see [old docs](https://github.com/Stichoza/google-translate-php/tree/3.2#google-translate-php)).
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Create GoogleTranslate object
|
||||
|
||||
```php
|
||||
use Stichoza\GoogleTranslate\GoogleTranslate;
|
||||
|
||||
$tr = new GoogleTranslate('en'); // Translates into English
|
||||
```
|
||||
Or you can change languages later
|
||||
```php
|
||||
$tr = new GoogleTranslate(); // Translates to 'en' from auto-detected language by default
|
||||
$tr->setSource('en'); // Translate from English
|
||||
$tr->setSource(); // Detect language automatically
|
||||
$tr->setTarget('ka'); // Translate to Georgian
|
||||
```
|
||||
Translate sentences
|
||||
```php
|
||||
echo $tr->translate('Hello World!');
|
||||
```
|
||||
Also, you can also use method chaining
|
||||
```php
|
||||
echo $tr->setSource('en')->setTarget('ka')->translate('Goodbye');
|
||||
```
|
||||
Or call a shorthand static method `trans`
|
||||
```php
|
||||
echo GoogleTranslate::trans('Hello again', 'ka', 'en');
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Language Detection
|
||||
|
||||
To detect language automatically, just set the source language to `null`:
|
||||
|
||||
```php
|
||||
$tr = new GoogleTranslate('es', null); // Or simply do not pass the second parameter
|
||||
```
|
||||
|
||||
```php
|
||||
$tr->setSource(); // Another way
|
||||
```
|
||||
|
||||
Use `getLastDetectedSource()` to get detected language:
|
||||
|
||||
```php
|
||||
$tr = new GoogleTranslate('fr');
|
||||
|
||||
$text = $tr->translate('Hello World!');
|
||||
|
||||
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).
|
||||
|
||||
### Using Raw Response
|
||||
|
||||
For advanced usage, you might need the raw results that Google Translate provides. you can use `getResponse` method for that.
|
||||
|
||||
```php
|
||||
$responseArray = $tr->getResponse('Hello world!');
|
||||
```
|
||||
|
||||
### Custom URL
|
||||
|
||||
You can override the default Google Translate url by `setUrl` method. Useful for some countries
|
||||
|
||||
```php
|
||||
$tr->setUrl('http://translate.google.cn/translate_a/single');
|
||||
```
|
||||
|
||||
### HTTP Client Configuration
|
||||
|
||||
This package uses [Guzzle](https://github.com/guzzle/guzzle) for HTTP requests. You can pass an array of [guzzle client configuration options](http://docs.guzzlephp.org/en/latest/request-options.html) as a third parameter to `GoogleTranslate` constructor, or just use `setOptions` method.
|
||||
|
||||
You can configure proxy, user-agent, default headers, connection timeout and so on using this options.
|
||||
|
||||
```php
|
||||
$tr = new GoogleTranslate('en', 'ka', [
|
||||
'timeout' => 10,
|
||||
'proxy' => [
|
||||
'http' => 'tcp://localhost:8125',
|
||||
'https' => 'tcp://localhost:9124'
|
||||
],
|
||||
'headers' => [
|
||||
'User-Agent' => 'Foo/5.0 Lorem Ipsum Browser'
|
||||
]
|
||||
]);
|
||||
```
|
||||
|
||||
```php
|
||||
// Set proxy to tcp://localhost:8090
|
||||
$tr->setOptions(['proxy' => 'tcp://localhost:8090'])->translate('Hello');
|
||||
|
||||
// Set proxy to socks5://localhost:1080
|
||||
$tr->setOptions(['proxy' => 'socks5://localhost:1080'])->translate('World');
|
||||
```
|
||||
|
||||
For more information, see [Creating a Client](http://guzzle.readthedocs.org/en/latest/quickstart.html#creating-a-client) section in Guzzle docs (6.x version).
|
||||
|
||||
### Custom Token Generator
|
||||
|
||||
You can override the token generator class by passing a generator object as a fourth parameter of constructor or just use `setTokenProvider` method.
|
||||
|
||||
Generator must implement `Stichoza\GoogleTranslate\Tokens\TokenProviderInterface`.
|
||||
|
||||
```php
|
||||
use Stichoza\GoogleTranslate\Tokens\TokenProviderInterface;
|
||||
|
||||
class MyTokenGenerator implements TokenProviderInterface
|
||||
{
|
||||
public function generateToken(string $source, string $target, string $text) : string
|
||||
{
|
||||
// Your code here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And use:
|
||||
|
||||
```php
|
||||
$tr->setTokenProvider(new MyTokenGenerator);
|
||||
```
|
||||
|
||||
### Errors and Exception Handling
|
||||
|
||||
Static method `trans()` and non-static `translate()` and `getResponse()` will throw following Exceptions:
|
||||
|
||||
- `ErrorException` If the HTTP request fails for some reason.
|
||||
- `UnexpectedValueException` If data received from Google cannot be decoded.
|
||||
|
||||
In addition, `translate()` and `trans()` methods will return `null` if there is no translation available.
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- `503 Service Unavailable` response:
|
||||
If you are getting this error, it is most likely that Google has banned your external IP address and/or [requires you to solve a CAPTCHA](https://github.com/Stichoza/google-translate-php/issues/18). This is not a bug in this package. Google has become stricter, and it seems like they keep lowering the number of allowed requests per IP per a certain amount of time. Try sending less requests to stay under the radar, or change your IP frequently ([for example using proxies](#http-client-configuration)). Please note that once an IP is banned, even if it's only temporary, the ban can last from a few minutes to more than 12-24 hours, as each case is different.
|
||||
- `429 Too Many Requests` response:
|
||||
This error is basically the same as explained above.
|
||||
- `413 Request Entity Too Large` response:
|
||||
This error means that your input string is too long. Google only allows a maximum of 5000 characters to be translated at once. If you want to translate a longer text, you can split it to shorter parts, and translate them one-by-one.
|
||||
- `403 Forbidden` response:
|
||||
This is not an issue with this package. Google Translate itself has some problems when it comes to translating some characters and HTML entities. See https://github.com/Stichoza/google-translate-php/issues/119#issuecomment-558078133
|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This package is developed for educational purposes only. Do not depend on this package as it may break anytime as it is based on crawling the Google Translate website. Consider buying [Official Google Translate API](https://cloud.google.com/translate/) for other types of usage.
|
||||
|
||||
## Donation
|
||||
|
||||
If this package helped you reduce your time to develop something, or it solved any major problems you had, feel free give me a cup of coffee :)
|
||||
|
||||
- [Patreon](https://www.patreon.com/stichoza)
|
||||
- [PayPal](https://paypal.me/stichoza)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "stichoza/google-translate-php",
|
||||
"description": "Free Google Translate API PHP Package",
|
||||
"keywords": ["google", "translate", "translator", "php"],
|
||||
"license": "MIT",
|
||||
"homepage": "http://github.com/Stichoza/google-translate-php",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Levan Velijanashvili",
|
||||
"email": "me@stichoza.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"guzzlehttp/guzzle": "~6.0",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Stichoza\\GoogleTranslate\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Stichoza\\GoogleTranslate\\Tests\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false">
|
||||
<testsuites>
|
||||
<testsuite name="Application Test Suite">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
|
||||
namespace Stichoza\GoogleTranslate;
|
||||
|
||||
use ErrorException;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Stichoza\GoogleTranslate\Tokens\GoogleTokenGenerator;
|
||||
use Stichoza\GoogleTranslate\Tokens\TokenProviderInterface;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Free Google Translate API PHP Package.
|
||||
*
|
||||
* @author Levan Velijanashvili <me@stichoza.com>
|
||||
* @link http://stichoza.com/
|
||||
* @license MIT
|
||||
*/
|
||||
class GoogleTranslate
|
||||
{
|
||||
/**
|
||||
* @var \GuzzleHttp\Client HTTP Client
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* @var string|null Source language - from where the string should be translated
|
||||
*/
|
||||
protected $source;
|
||||
|
||||
/**
|
||||
* @var string Target language - to which language string should be translated
|
||||
*/
|
||||
protected $target;
|
||||
|
||||
/**
|
||||
* @var string|null Last detected source language
|
||||
*/
|
||||
protected $lastDetectedSource;
|
||||
|
||||
/**
|
||||
* @var string Google Translate URL base
|
||||
*/
|
||||
protected $usUrl = 'https://translate.google.com/translate_a/single';
|
||||
|
||||
protected $url = 'https://translate.google.cn/translate_a/single';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @var array Dynamic GuzzleHttp client options
|
||||
*/
|
||||
protected $options = [];
|
||||
|
||||
/**
|
||||
* @var array URL Parameters
|
||||
*/
|
||||
protected $urlParams = [
|
||||
'client' => 'webapp',
|
||||
'hl' => 'en',
|
||||
'dt' => [
|
||||
't', // Translate
|
||||
'bd', // Full translate with synonym ($bodyArray[1])
|
||||
'at', // Other translate ($bodyArray[5] - in google translate page this shows when click on translated word)
|
||||
'ex', // Example part ($bodyArray[13])
|
||||
'ld', // I don't know ($bodyArray[8])
|
||||
'md', // Definition part with example ($bodyArray[12])
|
||||
'qca', // I don't know ($bodyArray[8])
|
||||
'rw', // Read also part ($bodyArray[14])
|
||||
'rm', // I don't know
|
||||
'ss' // Full synonym ($bodyArray[11])
|
||||
],
|
||||
'sl' => null, // Source language
|
||||
'tl' => null, // Target language
|
||||
'q' => null, // String to translate
|
||||
'ie' => 'UTF-8', // Input encoding
|
||||
'oe' => 'UTF-8', // Output encoding
|
||||
'multires' => 1,
|
||||
'otf' => 0,
|
||||
'pc' => 1,
|
||||
'trs' => 1,
|
||||
'ssel' => 0,
|
||||
'tsel' => 0,
|
||||
'kc' => 1,
|
||||
'tk' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Regex key-value patterns to replace on response data
|
||||
*/
|
||||
protected $resultRegexes = [
|
||||
'/,+/' => ',',
|
||||
'/\[,/' => '[',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var TokenProviderInterface Token provider
|
||||
*/
|
||||
protected $tokenProvider;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* For more information about HTTP client configuration options, see "Request Options" in
|
||||
* GuzzleHttp docs: http://docs.guzzlephp.org/en/stable/request-options.html
|
||||
*
|
||||
* @param string $target Target language
|
||||
* @param string|null $source Source language
|
||||
* @param array|null $options Associative array of http client configuration options
|
||||
* @param TokenProviderInterface|null $tokenProvider
|
||||
*/
|
||||
public function __construct(string $target = 'en', string $source = null, array $options = null, TokenProviderInterface $tokenProvider = null)
|
||||
{
|
||||
$this->client = new Client();
|
||||
$this->setTokenProvider($tokenProvider ?? new GoogleTokenGenerator)
|
||||
->setOptions($options) // Options are already set in client constructor tho.
|
||||
->setSource($source)
|
||||
->setTarget($target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set target language for translation.
|
||||
*
|
||||
* @param string $target Language code
|
||||
* @return GoogleTranslate
|
||||
*/
|
||||
public function setTarget(string $target): self
|
||||
{
|
||||
$this->target = $target;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set source language for translation.
|
||||
*
|
||||
* @param string|null $source Language code
|
||||
* @return GoogleTranslate
|
||||
*/
|
||||
public function setSource(string $source = null): self
|
||||
{
|
||||
$this->source = $source ?? 'auto';
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Google Translate URL base
|
||||
*
|
||||
* @param string $url Google Translate URL base
|
||||
* @return GoogleTranslate
|
||||
*/
|
||||
public function setUrl(string $url): self
|
||||
{
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set GuzzleHttp client options.
|
||||
*
|
||||
* @param array $options guzzleHttp client options.
|
||||
* @return GoogleTranslate
|
||||
*/
|
||||
public function setOptions(array $options = null): self
|
||||
{
|
||||
$this->options = $options ?? [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set token provider.
|
||||
*
|
||||
* @param TokenProviderInterface $tokenProvider
|
||||
* @return GoogleTranslate
|
||||
*/
|
||||
public function setTokenProvider(TokenProviderInterface $tokenProvider): self
|
||||
{
|
||||
$this->tokenProvider = $tokenProvider;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last detected source language
|
||||
*
|
||||
* @return string|null Last detected source language
|
||||
*/
|
||||
public function getLastDetectedSource(): ?string
|
||||
{
|
||||
return $this->lastDetectedSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override translate method for static call.
|
||||
*
|
||||
* @param string $string
|
||||
* @param string $target
|
||||
* @param string|null $source
|
||||
* @param array $options
|
||||
* @param TokenProviderInterface|null $tokenProvider
|
||||
* @return null|string
|
||||
* @throws ErrorException If the HTTP request fails
|
||||
* @throws UnexpectedValueException If received data cannot be decoded
|
||||
*/
|
||||
public static function trans(string $string, string $target = 'en', string $source = null, array $options = [], TokenProviderInterface $tokenProvider = null): ?string
|
||||
{
|
||||
return (new self)
|
||||
->setTokenProvider($tokenProvider ?? new GoogleTokenGenerator)
|
||||
->setOptions($options) // Options are already set in client constructor tho.
|
||||
->setSource($source)
|
||||
->setTarget($target)
|
||||
->translate($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate text.
|
||||
*
|
||||
* This can be called from instance method translate() using __call() magic method.
|
||||
* Use $instance->translate($string) instead.
|
||||
*
|
||||
* @param string $string String to translate
|
||||
* @return string|null
|
||||
* @throws ErrorException If the HTTP request fails
|
||||
* @throws UnexpectedValueException If received data cannot be decoded
|
||||
*/
|
||||
public function translate(string $string): ?string
|
||||
{
|
||||
/*
|
||||
* if source lang and target lang are the same
|
||||
* just return the string without any request to google
|
||||
*/
|
||||
if ($this->source == $this->target) return $string;
|
||||
|
||||
$responseArray = $this->getResponse($string);
|
||||
|
||||
/*
|
||||
* if response in text and the content has zero the empty returns true, lets check
|
||||
* if response is string and not empty and create array for further logic
|
||||
*/
|
||||
if (is_string($responseArray) && $responseArray != '') {
|
||||
$responseArray = [$responseArray];
|
||||
}
|
||||
|
||||
// Check if translation exists
|
||||
if (!isset($responseArray[0]) || empty($responseArray[0])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Detect languages
|
||||
$detectedLanguages = [];
|
||||
|
||||
// the response contains only single translation, don't create loop that will end with
|
||||
// invalid foreach and warning
|
||||
if (!is_string($responseArray)) {
|
||||
foreach ($responseArray as $item) {
|
||||
if (is_string($item)) {
|
||||
$detectedLanguages[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Another case of detected language
|
||||
if (isset($responseArray[count($responseArray) - 2][0][0])) {
|
||||
$detectedLanguages[] = $responseArray[count($responseArray) - 2][0][0];
|
||||
}
|
||||
|
||||
// Set initial detected language to null
|
||||
$this->lastDetectedSource = null;
|
||||
|
||||
// Iterate and set last detected language
|
||||
foreach ($detectedLanguages as $lang) {
|
||||
if ($this->isValidLocale($lang)) {
|
||||
$this->lastDetectedSource = $lang;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// the response can be sometimes an translated string.
|
||||
if (is_string($responseArray)) {
|
||||
return $responseArray;
|
||||
} else {
|
||||
if (is_array($responseArray[0])) {
|
||||
return (string) array_reduce($responseArray[0], function ($carry, $item) {
|
||||
$carry .= $item[0];
|
||||
return $carry;
|
||||
});
|
||||
} else {
|
||||
return (string) $responseArray[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response array.
|
||||
*
|
||||
* @param string $string String to translate
|
||||
* @throws ErrorException If the HTTP request fails
|
||||
* @throws UnexpectedValueException If received data cannot be decoded
|
||||
* @return array|string Response
|
||||
*/
|
||||
public function getResponse(string $string): array
|
||||
{
|
||||
$queryArray = array_merge($this->urlParams, [
|
||||
'sl' => $this->source,
|
||||
'tl' => $this->target,
|
||||
'tk' => $this->tokenProvider->generateToken($this->source, $this->target, $string),
|
||||
'q' => $string
|
||||
]);
|
||||
|
||||
$queryUrl = preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', http_build_query($queryArray));
|
||||
|
||||
try {
|
||||
$response = $this->client->get($this->url, [
|
||||
'query' => $queryUrl,
|
||||
] + $this->options);
|
||||
} catch (RequestException $e) {
|
||||
throw new ErrorException($e->getMessage());
|
||||
}
|
||||
|
||||
$body = $response->getBody(); // Get response body
|
||||
|
||||
// Modify body to avoid json errors
|
||||
$bodyJson = preg_replace(array_keys($this->resultRegexes), array_values($this->resultRegexes), $body);
|
||||
|
||||
// Decode JSON data
|
||||
if (($bodyArray = json_decode($bodyJson, true)) === null) {
|
||||
throw new UnexpectedValueException('Data cannot be decoded or it is deeper than the recursion limit');
|
||||
}
|
||||
|
||||
return $bodyArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given locale is valid.
|
||||
*
|
||||
* @param string $lang Langauge code to verify
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValidLocale(string $lang): bool
|
||||
{
|
||||
return (bool) preg_match('/^([a-z]{2})(-[A-Z]{2})?$/', $lang);
|
||||
}
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace Stichoza\GoogleTranslate\Tokens;
|
||||
|
||||
/**
|
||||
* Google Token Generator.
|
||||
*
|
||||
* Thanks to @helen5106 and @tehmaestro and few other cool guys
|
||||
* at https://github.com/Stichoza/google-translate-php/issues/32
|
||||
*/
|
||||
class GoogleTokenGenerator implements TokenProviderInterface
|
||||
{
|
||||
/**
|
||||
* Generate and return a token.
|
||||
*
|
||||
* @param string $source Source language
|
||||
* @param string $target Target language
|
||||
* @param string $text Text to translate
|
||||
* @return string Token
|
||||
*/
|
||||
public function generateToken(string $source, string $target, string $text): string
|
||||
{
|
||||
return $this->TL($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a valid Google Translate request token.
|
||||
*
|
||||
* @param string $a text to translate
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function TL($a)
|
||||
{
|
||||
$tkk = $this->TKK();
|
||||
$b = $tkk[0];
|
||||
|
||||
for ($d = [], $e = 0, $f = 0; $f < $this->JS_length($a); $f++) {
|
||||
$g = $this->JS_charCodeAt($a, $f);
|
||||
if (128 > $g) {
|
||||
$d[$e++] = $g;
|
||||
} else {
|
||||
if (2048 > $g) {
|
||||
$d[$e++] = $g >> 6 | 192;
|
||||
} else {
|
||||
if (55296 == ($g & 64512) && $f + 1 < $this->JS_length($a) && 56320 == ($this->JS_charCodeAt($a, $f + 1) & 64512)) {
|
||||
$g = 65536 + (($g & 1023) << 10) + ($this->JS_charCodeAt($a, ++$f) & 1023);
|
||||
$d[$e++] = $g >> 18 | 240;
|
||||
$d[$e++] = $g >> 12 & 63 | 128;
|
||||
} else {
|
||||
$d[$e++] = $g >> 12 | 224;
|
||||
}
|
||||
$d[$e++] = $g >> 6 & 63 | 128;
|
||||
}
|
||||
$d[$e++] = $g & 63 | 128;
|
||||
}
|
||||
}
|
||||
$a = $b;
|
||||
for ($e = 0; $e < count($d); $e++) {
|
||||
$a += $d[$e];
|
||||
$a = $this->RL($a, '+-a^+6');
|
||||
}
|
||||
$a = $this->RL($a, '+-3^+b+-f');
|
||||
$a ^= $tkk[1] ? $tkk[1] + 0 : 0;
|
||||
if (0 > $a) {
|
||||
$a = ($a & 2147483647) + 2147483648;
|
||||
}
|
||||
$a = fmod($a, pow(10, 6));
|
||||
|
||||
return $a.'.'.($a ^ $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
private function TKK()
|
||||
{
|
||||
return ['406398', (561666268 + 1526272306)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process token data by applying multiple operations.
|
||||
* (Params are safe, no need for multibyte functions)
|
||||
*
|
||||
* @param int $a
|
||||
* @param string $b
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function RL($a, $b)
|
||||
{
|
||||
for ($c = 0; $c < strlen($b) - 2; $c += 3) {
|
||||
$d = $b[$c + 2];
|
||||
$d = 'a' <= $d ? ord($d[0]) - 87 : intval($d);
|
||||
$d = '+' == $b[$c + 1] ? $this->unsignedRightShift($a, $d) : $a << $d;
|
||||
$a = '+' == $b[$c] ? ($a + $d & 4294967295) : $a ^ $d;
|
||||
}
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsigned right shift implementation
|
||||
* https://msdn.microsoft.com/en-us/library/342xfs5s(v=vs.94).aspx
|
||||
* http://stackoverflow.com/a/43359819/2953830
|
||||
*
|
||||
* @param $a
|
||||
* @param $b
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
private function unsignedRightShift($a, $b)
|
||||
{
|
||||
if ($b >= 32 || $b < -32) {
|
||||
$m = (int)($b / 32);
|
||||
$b = $b - ($m * 32);
|
||||
}
|
||||
|
||||
if ($b < 0) {
|
||||
$b = 32 + $b;
|
||||
}
|
||||
|
||||
if ($b == 0) {
|
||||
return (($a >> 1) & 0x7fffffff) * 2 + (($a >> $b) & 1);
|
||||
}
|
||||
|
||||
if ($a < 0) {
|
||||
$a = ($a >> 1);
|
||||
$a &= 2147483647;
|
||||
$a |= 0x40000000;
|
||||
$a = ($a >> ($b - 1));
|
||||
} else {
|
||||
$a = ($a >> $b);
|
||||
}
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JS charCodeAt equivalent result with UTF-16 encoding
|
||||
*
|
||||
* @param string $str
|
||||
* @param int $index
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
private function JS_charCodeAt($str, $index)
|
||||
{
|
||||
$utf16 = mb_convert_encoding($str, 'UTF-16LE', 'UTF-8');
|
||||
return ord($utf16[$index*2]) + (ord($utf16[$index*2+1]) << 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JS equivalent string length with UTF-16 encoding
|
||||
*
|
||||
* @param string $str
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
private function JS_length($str)
|
||||
{
|
||||
$utf16 = mb_convert_encoding($str, 'UTF-16LE', 'UTF-8');
|
||||
return strlen($utf16)/2;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Stichoza\GoogleTranslate\Tokens;
|
||||
|
||||
/**
|
||||
* A nice interface for providing tokens.
|
||||
*/
|
||||
class SampleTokenGenerator implements TokenProviderInterface
|
||||
{
|
||||
/**
|
||||
* Generate a fake token just as an example.
|
||||
*
|
||||
* @param string $source Source language
|
||||
* @param string $target Target langiage
|
||||
* @param string $text Text to translate
|
||||
* @return string Token
|
||||
*/
|
||||
public function generateToken(string $source, string $target, string $text): string
|
||||
{
|
||||
return sprintf('%d.%d', rand(10000, 99999), rand(10000, 99999));
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Stichoza\GoogleTranslate\Tokens;
|
||||
|
||||
/**
|
||||
* A nice interface for providing tokens.
|
||||
*/
|
||||
interface TokenProviderInterface
|
||||
{
|
||||
/**
|
||||
* Generate and return a token.
|
||||
*
|
||||
* @param string $source Source language
|
||||
* @param string $target Target langiage
|
||||
* @param string $text Text to translate
|
||||
* @return string Token
|
||||
*/
|
||||
public function generateToken(string $source, string $target, string $text): string;
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
use think\Exception;
|
||||
use Stichoza\GoogleTranslate\GoogleTranslate;
|
||||
use app\admin\model\Proxy as ProxyIp;
|
||||
|
||||
/**
|
||||
* @param $text 需要翻译的内容-可以是任意语种文本-不超过5000字符
|
||||
* @param $toLang 要翻译成目标语种
|
||||
* @param string $fromLang 原始文本的语种-不填写可以自动识别-填写错误会导致翻译无效
|
||||
* @param bool $useProxy 是否使用代理-可以传入代理 true 自动随机代理 false 不使用代理
|
||||
* @param int $timout 超时时间
|
||||
* @param int $retryTime 失败重试次数
|
||||
* @return array ['status' => boolean 'data' => result, 'msg' => 'errormsg']
|
||||
* @throws ErrorException
|
||||
*/
|
||||
function doGoogleTranslate($text, $toLang, $fromLang = '', $useProxy = false, $timout = 5, $retryTime = 5)
|
||||
{
|
||||
$result = [
|
||||
'data' => '',
|
||||
'status' => false,
|
||||
'msg' => '',
|
||||
];
|
||||
$text = filterHtmlEntryChar($text);
|
||||
do{
|
||||
try {
|
||||
$tr = new GoogleTranslate();
|
||||
if ($fromLang) {
|
||||
$tr->setSource($fromLang);
|
||||
}
|
||||
$tr->setTarget($toLang);
|
||||
if ($useProxy !== false) {
|
||||
$options = [];
|
||||
if ($useProxy === true) {
|
||||
//从配置文件中取出代理
|
||||
$proxyIps = get_addon_fullconfig('translate');
|
||||
$proxyIps = $proxyIps[0]['value'];
|
||||
if ($proxyIps) {
|
||||
$proxyIps = array_unique(explode(PHP_EOL, $proxyIps));
|
||||
$proxyIndex = array_rand($proxyIps, 1);
|
||||
$proxy = trim($proxyIps[$proxyIndex]);
|
||||
} else {
|
||||
$proxy = false;
|
||||
}
|
||||
if ($proxy) {
|
||||
$options = [
|
||||
'proxy' => [$proxy],
|
||||
'timeout' => $timout,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
$options = [
|
||||
'proxy' => [$useProxy],
|
||||
'timeout' => $timout,
|
||||
];
|
||||
}
|
||||
if ($options) {
|
||||
$tr->setOptions($options);
|
||||
}
|
||||
}
|
||||
$result['data'] = $tr->translate($text);
|
||||
$result['status'] = true;
|
||||
} catch (Exception $e) {
|
||||
$result['data'] = '';
|
||||
$result['status'] = false;
|
||||
$result['msg'] = $e->getMessage();
|
||||
}
|
||||
} while($retryTime-- > 0 && $result['status'] === false);
|
||||
return $result;
|
||||
}
|
||||
|
||||
function filterHtmlEntryChar($str)
|
||||
{
|
||||
$str = htmlspecialchars_decode($str);//过滤掉html实体 避免谷歌翻译失败 https://www.cnblogs.com/mengmengi/p/10137167.html
|
||||
//过滤替换掉会导致谷歌翻译的失败的标签
|
||||
$seachAndReplace = [
|
||||
"<" => "<",
|
||||
">" => ">",
|
||||
"&" => "&",
|
||||
""" => "“",
|
||||
"®" => "®",
|
||||
"©" => "©",
|
||||
"™" => "™",
|
||||
" " => "",
|
||||
" " => "",
|
||||
" " => "",
|
||||
"´" => "´",
|
||||
"&" => "&",
|
||||
"¦" => "¦",
|
||||
"•" => "•",
|
||||
"¸" => "¸",
|
||||
"¢" => "¢",
|
||||
"€" => "€",
|
||||
"„" => "„",
|
||||
"ˆ" => "ˆ",
|
||||
"†" => "†",
|
||||
"‡" => "‡",
|
||||
"≈" => "≈",
|
||||
"∩" => "∩",
|
||||
"♣" => "♣",
|
||||
"↓" => "↓",
|
||||
"♦" => "♦",
|
||||
"≡" => "≡",
|
||||
"α" => "α",
|
||||
"β" => "β",
|
||||
"χ" => "χ",
|
||||
"δ" => "δ",
|
||||
"ε" => "ε",
|
||||
"Α" => "Α",
|
||||
"Β" => "Β",
|
||||
"Χ" => "Χ",
|
||||
"Δ" => "Δ",
|
||||
"Ε" => "Ε",
|
||||
"©" => "©",
|
||||
"°" => "°",
|
||||
"÷" => "÷",
|
||||
"½" => "½",
|
||||
"¼" => "¼",
|
||||
"¾" => "¾",
|
||||
"£" => "£",
|
||||
"…" => "…",
|
||||
"“" => "“",
|
||||
"‹" => "‹",
|
||||
"‘" => "‘",
|
||||
"⁄" => "⁄",
|
||||
"≥" => "≥",
|
||||
"↔" => "↔",
|
||||
"♥" => "♥",
|
||||
"∞" => "∞",
|
||||
"∫" => "∫",
|
||||
"η" => "η",
|
||||
"γ" => "γ",
|
||||
"ι" => "ι",
|
||||
"κ" => "κ",
|
||||
"λ" => "λ",
|
||||
"Η" => "Η",
|
||||
"Γ" => "Γ",
|
||||
"Ι" => "Ι",
|
||||
"Κ" => "Κ",
|
||||
"Λ" => "Λ",
|
||||
">" => ">",
|
||||
"¡" => "¡",
|
||||
"¿" => "¿",
|
||||
"«" => "«",
|
||||
"<" => "<",
|
||||
"¯" => "¯",
|
||||
"¥" => "¥",
|
||||
"·" => "·",
|
||||
"—" => "—",
|
||||
"–" => "–",
|
||||
"‰" => "‰",
|
||||
"←" => "←",
|
||||
"≤" => "≤",
|
||||
"◊" => "◊",
|
||||
"−" => "−",
|
||||
"≠" => "≠",
|
||||
"‾" => "‾",
|
||||
"μ" => "μ",
|
||||
"ν" => "ν",
|
||||
"ω" => "ω",
|
||||
"ο" => "ο",
|
||||
"φ" => "φ",
|
||||
"Μ" => "Μ",
|
||||
"Ν" => "Ν",
|
||||
"Ω" => "Ω",
|
||||
"Ο" => "Ο",
|
||||
"Φ" => "Φ",
|
||||
"µ" => "µ",
|
||||
"¬" => "¬",
|
||||
"¶" => "¶",
|
||||
"±" => "±",
|
||||
""" => "“",
|
||||
"›" => "›",
|
||||
"’" => "’",
|
||||
"‚" => "‚",
|
||||
"­" => "",
|
||||
"∂" => "∂",
|
||||
"″" => "″",
|
||||
"′" => "′",
|
||||
"∏" => "∏",
|
||||
"√" => "√",
|
||||
"→" => "→",
|
||||
"π" => "π",
|
||||
"ψ" => "ψ",
|
||||
"ρ" => "ρ",
|
||||
"σ" => "σ",
|
||||
"τ" => "τ",
|
||||
"Π" => "Π",
|
||||
"Ψ" => "Ψ",
|
||||
"Ρ" => "Ρ",
|
||||
"Σ" => "Σ",
|
||||
"Τ" => "Τ",
|
||||
"®" => "®",
|
||||
"»" => "»",
|
||||
"§" => "§",
|
||||
"¨" => "¨",
|
||||
"×" => "×",
|
||||
"™" => "™",
|
||||
"ª" => "ª",
|
||||
"º" => "º",
|
||||
"”" => "”",
|
||||
"˜" => "˜",
|
||||
"♠" => "♠",
|
||||
"∑" => "∑",
|
||||
"↑" => "↑",
|
||||
"‍" => " ",
|
||||
"‌" => " ",
|
||||
"θ" => "θ",
|
||||
"υ" => "υ",
|
||||
"ξ" => "ξ",
|
||||
"ζ" => "ζ",
|
||||
"Θ" => "Θ",
|
||||
"Υ" => "Υ",
|
||||
"Ξ" => "Ξ",
|
||||
"Ζ" => "Ζ",
|
||||
"ς" => "ς",
|
||||
];
|
||||
if (strpos($str, '&') !== false) {
|
||||
$str = str_replace(array_keys($seachAndReplace), array_values($seachAndReplace), $str);
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Stichoza\GoogleTranslate\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Stichoza\GoogleTranslate\GoogleTranslate;
|
||||
|
||||
class LanguageDetectionTest extends TestCase
|
||||
{
|
||||
public $tr;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->tr = new GoogleTranslate();
|
||||
}
|
||||
|
||||
public function testSingleWord()
|
||||
{
|
||||
$this->tr->translate('გამარჯობა');
|
||||
$this->assertEquals($this->tr->getLastDetectedSource(), 'ka');
|
||||
|
||||
$this->tr->translate('Cześć');
|
||||
$this->assertEquals($this->tr->getLastDetectedSource(), 'pl');
|
||||
}
|
||||
|
||||
public function testSingleSentence()
|
||||
{
|
||||
$this->tr->translate('იყო არაბეთს როსტევან');
|
||||
$this->assertEquals($this->tr->getLastDetectedSource(), 'ka');
|
||||
|
||||
$this->tr->translate('Путин хуйло');
|
||||
$this->assertEquals($this->tr->getLastDetectedSource(), 'ru');
|
||||
}
|
||||
|
||||
public function testMultipleSentence()
|
||||
{
|
||||
$this->tr->translate('ჩემი ხატია სამშობლო. სახატე - მთელი ქვეყანა. განათებული მთა-ბარი.');
|
||||
$this->assertEquals($this->tr->getLastDetectedSource(), 'ka');
|
||||
|
||||
$this->tr->translate('Ще не вмерла Україна, И слава, и воля! Ще намъ, браття-молодці, Усміхнеться доля!');
|
||||
$this->assertEquals($this->tr->getLastDetectedSource(), 'uk');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Stichoza\GoogleTranslate\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Stichoza\GoogleTranslate\GoogleTranslate;
|
||||
|
||||
class TranslationTest extends TestCase
|
||||
{
|
||||
public $tr;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->tr = new GoogleTranslate();
|
||||
}
|
||||
|
||||
public function testTranslationEquality()
|
||||
{
|
||||
try {
|
||||
$resultOne = GoogleTranslate::trans('Hello', 'ka', 'en');
|
||||
} catch (\ErrorException $e) {
|
||||
$resultOne = null;
|
||||
}
|
||||
$resultTwo = $this->tr->setSource('en')->setTarget('ka')->translate('Hello');
|
||||
|
||||
$this->assertEquals($resultOne, $resultTwo, 'გამარჯობა');
|
||||
}
|
||||
|
||||
public function testUTF16Translation()
|
||||
{
|
||||
try {
|
||||
$resultOne = GoogleTranslate::trans('yes 👍🏽', 'de', 'en');
|
||||
} catch (\ErrorException $e) {
|
||||
$resultOne = null;
|
||||
}
|
||||
$resultTwo = $this->tr->setSource('en')->setTarget('de')->translate('yes 👍🏽');
|
||||
|
||||
$this->assertEquals($resultOne, $resultTwo, 'ja 👍🏽');
|
||||
}
|
||||
|
||||
public function testRawResponse()
|
||||
{
|
||||
$rawResult = $this->tr->getResponse('cat');
|
||||
|
||||
$this->assertTrue(is_array($rawResult), 'Method getResponse() should return an array.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Stichoza\GoogleTranslate\Tests;
|
||||
|
||||
use ReflectionClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Stichoza\GoogleTranslate\GoogleTranslate;
|
||||
|
||||
class UtilityTest extends TestCase
|
||||
{
|
||||
public $tr;
|
||||
|
||||
public $method;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->tr = new GoogleTranslate();
|
||||
$reflection = new ReflectionClass(get_class($this->tr));
|
||||
$this->method = $reflection->getMethod('isValidLocale');
|
||||
$this->method->setAccessible(true);
|
||||
}
|
||||
|
||||
public function testIsValidLocale()
|
||||
{
|
||||
$m = $this->method;
|
||||
$t = $this->tr;
|
||||
|
||||
$booleanAssertions = [
|
||||
'ab' => true,
|
||||
'ab-CD' => true,
|
||||
'ab-CDE' => false,
|
||||
'abc-DE' => false,
|
||||
'abc-DEF' => false,
|
||||
'abc' => false,
|
||||
'ab-' => false,
|
||||
'a' => false,
|
||||
];
|
||||
|
||||
foreach ($booleanAssertions as $key => $value) {
|
||||
$this->assertEquals($m->invokeArgs($t, [$key]), $value);
|
||||
}
|
||||
}
|
||||
|
||||
public function testSetOptions()
|
||||
{
|
||||
$res = fopen('php://memory', 'r+');
|
||||
|
||||
$this->tr->setOptions([
|
||||
'debug' => $res,
|
||||
'headers' => [
|
||||
'User-Agent' => 'Foo',
|
||||
],
|
||||
])->translate('hello');
|
||||
rewind($res);
|
||||
$output = str_replace("\r", '', stream_get_contents($res));
|
||||
$this->assertContains('User-Agent: Foo', $output);
|
||||
|
||||
GoogleTranslate::trans('world', 'en', null, [
|
||||
'debug' => $res,
|
||||
'headers' => [
|
||||
'User-Agent' => 'Bar',
|
||||
],
|
||||
]);
|
||||
rewind($res);
|
||||
$output = str_replace("\r", '', stream_get_contents($res));
|
||||
$this->assertContains('User-Agent: Bar', $output);
|
||||
fclose($res);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user