Shopify 页面翻译实现

实现在自己网站后台控制shopify页面翻译

 

 

  1 <?php
  2 namespace Cli\Controller;
  3 
  4 use Curl;
  5 use Exception;
  6 use Think\Controller;
  7 use Think\Log;
  8 use Think\Think;
  9 use translate\Baidu;
 10 use translate\Translate;
 11 
 12 class ShopifyTranslateController extends Controller
 13 {
 14     private $_api       = [
 15         //api info ...
 20     ];
 21     private $_logque;
 22     private $_curlLastErr;
 23     private $_storefrontToken   = 'token....';
 24     private $_rePrefix      = 'ShopifyTranslate';
 25     private $_redis;
 26     //locale shopLocales
 27     const CHINESE       = 'zh-CN';
 28     const JAPANESE      = 'ja';
 29 
 30 
 31     public function __construct()
 32     {
 33         $this->_api['authorization']    = base64_encode( "{$this->_api['key']}:{$this->_api['pwd']}" );
 34         $this->_redis   = getRedis();
 35 
 36     }
 37 
 38     public function test()
 39     {
 40         $list   = M('shopifyTranslate')->where( ['resourceId' => 'gid://shopify/OnlineStoreTheme/80042688647'] )->select();
 41 
 42         $translations   = [];
 43         foreach( $list as $_item ) {
 44             $source     = json_decode( $_item['source'], true );
 45 
 46             $cn         = str_replace("\n", "\\n", addslashes( $_item['zh-cn'] ) );
 47             $ja         = str_replace("\n", "\\n", addslashes( $_item['ja'] ) );
 48 
 49             $translations[]     = [
 50                 'key'   => $source['key'],
 51                 'value'     => $cn,
 52                 'locale'    => "zh-CN",
 53                 'translatableContentDigest'     => $source['digest']
 54             ];
 55 
 56             $translations[]     = [
 57                 'key'   => $source['key'],
 58                 'value'     => $ja,
 59                 'locale'    => "ja",
 60                 'translatableContentDigest'     => $source['digest']
 61             ];
 62         }
 63 
 64         $chunked        = array_chunk( $translations, 100 );
 65         foreach( $chunked as $_chunk ) {
 66             $_translations       = json_encode( $_chunk );
 67             $data   = <<<EOF
 68 {
 69     "query": "mutation CreateTranslation(\$id: ID!, \$translations: [TranslationInput!]!) { translationsRegister(resourceId: \$id, translations: \$translations) { translations { locale key outdated value } userErrors { field message } } }",
 70     "variables": {
 71         "id": "gid://shopify/OnlineStoreTheme/80042688647",
 72         "translations": $_translations
 73     }
 74 }           
 75 EOF;
 76             $ret    = $this->sendRequest( $data, 'post', 'graphql.json', [], false, 'json' );
 77             var_dump( $ret );
 78         }
 79 
 80         
 81     }
 82 
 83     public function pull()
 84     {
 85 
 86     }
 87 
 88     //第三方翻译。 https://github.com/John-Theo/google-translate-server
 89     public function translate()
 90     {
 91         $list   = M('shopifyTranslate')->field('id, en')->where( 'ja is null' )->select();
 92         import( 'Lib.Extend.Translate.sdk.Baidu', dirname( THINK_PATH ) );
 93 
 94         $baidu      = new Baidu();
 95         foreach( $list as $_item ) {
 96 
 97             $up         = [];
 98 
 99             $en     = urlencode( $_item['en'] );
100 
101             $ret = (new Curl())->get( "http://127.0.0.1:30031?to=zh-CN&text={$en}" );
102             $ret    = json_decode( $ret, true );
103             if( !empty( $ret['text'] ) ) {
104                 $up['zh-CN']    = $ret['text'];
105             }else {
106                 $str        = addslashes( $_item['en'] );
107                 $res        = $baidu->run( $str, 'zh', 'en' );
108                 $up['zh-CN']    = addslashes( $res );
109             }
110             sleep( 3 );
111 
112             $ret = (new Curl())->get( "http://127.0.0.1:30031?to=ja&text={$en}" );
113             $ret    = json_decode( $ret, true );
114             if( !empty( $ret['text'] ) ) {
115                 $up['ja']    = $ret['text'];
116             }else {
117                 $res        = $baidu->run( $str, 'jp', 'en' );
118                 $up['ja']    = addslashes( $res );
119                 sleep(1);
120             }
121             sleep( 3 );
122 
123 
124             M('shopifyTranslate')->where( ['id' => $_item['id']] )->save( $up );
125         }
126     }
127 
128     public function setTranslate( $id = 0 )
129     {
130         $this->_setLog( 'setTranslate start' );
131         
132         //     $list   = M('shopifyTranslate')->where( ['synch' => 0, 'ja' => ['exp', 'is not null']] )->select();
133 
134         // $keys   = [
135         //     '!', '"', '#', '$', '%', ''', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\', ']', '^', '_', '`', '{', '|', '}', '~',
136         //     '⦅', '⦆', '¢', '£', '¬', ' ̄', '¦', '¥', '₩', '│', '←', '↑', '→', '↓', '■', '○','“','”','/ ',' _ ',' // '
137         // ];
138         // $values     = [
139         //     '!', '"', '#', '$', '%', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~',
140         //     '⦅', '⦆', '¢', '£', '¬', '¯', '¦', '¥', '₩', '│', '←', '↑', '→', '↓', '■', '○','"','"','/','_','//'
141         // ];
142         // foreach( $list as $_item ) {
143         //     $up     = [];
144 
145         //     $up['zh-CN']    = strtr( $_item['zh-cn'], array_combine( $keys, $values ) );
146         //     $up['ja']    = strtr( $_item['ja'], array_combine( $keys, $values ) );
147 
148         //     M('shopifyTranslate')->where( ['id' => $_item['id']] )->save($up);
149         // }
150         // die;
151 
152         $list   = M('shopifyTranslate')->where( ['synch' => 0, 'ja' => ['exp', 'is not null'], 'type' => ['neq', 'EMAIL_TEMPLATE']] )->select();
153         // $list   = M('shopifyTranslate')->where( ['id' => $id] )->select();
154 
155         foreach( $list as $_item ) {
156 
157             $source     = json_decode( $_item['source'], true );
158             $cn         = str_replace("\n", "\\n", addslashes( $_item['zh-cn'] ) );
159             $ja         = str_replace("\n", "\\n", addslashes( $_item['ja'] ) );
160 
161             $data   = <<<EOF
162 {
163     "query": "mutation CreateTranslation(\$id: ID!, \$translations: [TranslationInput!]!) { translationsRegister(resourceId: \$id, translations: \$translations) { translations { locale key outdated value } userErrors { field message } } }",
164     "variables": {
165         "id": "{$_item['resourceid']}",
166         "translations": [
167             {
168                 "key": "{$source['key']}",
169                 "value": "{$cn}",
170                 "locale": "zh-CN",
171                 "translatableContentDigest": "{$source['digest']}"
172             },
173             {
174                 "key": "{$source['key']}",
175                 "value": "{$ja}",
176                 "locale": "ja",
177                 "translatableContentDigest": "{$source['digest']}"
178             }
179         ]
180     }
181 }           
182 EOF;
183             $ret    = $this->sendRequest( $data, 'post', 'graphql.json', [], false, 'json' );
184             if( $ret ) {
185                 M('shopifyTranslate')->where( ['id' => $_item['id']] )->save(['synch' => 1]);
186                 var_dump( $_item['id'] . ' ok ..' ); 
187                 // return true;
188                 usleep(500);
189             }else {
190                 var_dump( $_item['id'] . ' fail ..' . $ret );
191                 // return false;
192             }
193         }
194 
195     }
196 
197     public function setToDb()
198     {
199         $types      = $this->getTranslateType();
200         foreach(  $types as $type ) {
201             $this->getTranslatableResources( $type, self::CHINESE );
202             $this->parseDataToDb( $type, self::CHINESE );
203         }
204     }
205 
206     //解析数据入库
207     public function parseDataToDb( $type, $locale )
208     {
209         $this->_setLog( 'parseDataToDb start' . json_encode( func_get_args() ) );
210         $cacheFile  = DATA_PATH . "ShopifyTranslate:to{$locale}:{$type}";
211         if( !file_exists( $cacheFile ) ) {
212             $this->_setLog( 'err :: 获取原始数据失败' . json_encode( func_get_args() ), Log::WARN );
213             return false;
214         }
215 
216         $fhandle    = fopen( $cacheFile, 'r+' );
217         if( !$fhandle ) {
218             $this->_setLog( 'err :: 打开文件失败' . $cacheFile, Log::WARN );
219             return false;
220         }
221 
222         $preData    = [];
223         $totals     = 0;
224         while( ($line = fgets( $fhandle )) !== false ) { 
225             $ret       = json_decode( $line, true );
226             if( !$ret ) {
227                 $this->_setLog( 'err :: 读取文件异常' . $cacheFile . ' >>> ' .  $line, Log::WARN );
228                 continue;
229             }
230     
231             
232             if( isset( $ret['data']['translatableResources']['edges'] ) ) {
233                 // $this->_setLog( 'parseDataToDb edges count == ' . count( $ret['data']['translatableResources']['edges']  ) );
234                 foreach( $ret['data']['translatableResources']['edges'] as $edges ) {
235                     $resourceId     = $edges['node']['resourceId'];
236                     // $this->_setLog( 'parseDataToDb edges > translatableContent > count == ' . count( $edges['node']['translatableContent'] ) );
237                     foreach( $edges['node']['translatableContent'] as $node ) {
238                         if( empty( $node['value'] ) ) 
239                             continue;
240 
241                         $preData_key    = md5( $node['key'] . $resourceId );
242                         $preData[$preData_key] = [
243                             'en'            => $node['value'],
244                             'resourceId'    => $resourceId,
245                             'en_md5'        => md5( $node['key'] . $node['value'] . $resourceId . $type ),
246                             'source'        => json_encode( $node ),
247                             'type'      => $type,
248                         ];
249                         $totals++;
250                     }
251 
252                     // foreach( $edges['node']['translations'] as $node ) {
253                     //     $preData_key    = md5( $node['key'] . $resourceId );
254                     //     if( isset( $preData[$preData_key] ) )
255                     //         $preData[$preData_key][$node['locale']]  = $node['value'];
256                     
257                     // }
258                 }
259             }else {
260                 $this->_setLog( 'err : 解析结果失败...' . $cacheFile, Log::WARN );
261             }
262 
263         }
264         $this->_setLog( "parseDataToDb totals == {$totals} , preData count == " . count( $preData ), Log::WARN );
265 
266         fclose( $fhandle );
267        
268         $i = 0;
269         foreach( $preData as $_item ) {
270             if( !M('shopifyTranslate')->where( ['en_md5' => $_item['en_md5']] )->find()  ) {
271                 echo $i++;
272                 M('shopifyTranslate')->add( $_item );
273             }  
274         }     
275     }
276     
277     //获取原始信息
278     private function getTranslatableResources( $type, $locale, $force = false )
279     {
280         $this->_setLog( 'getTranslatableResources start' . json_encode( func_get_args() ) );
281 
282         $cacheFile  = DATA_PATH . "ShopifyTranslate:to{$locale}:{$type}";
283         if( $force && file_exists( $cacheFile ) ) 
284             unlink( $cacheFile  );
285         
286         if( !file_exists( $cacheFile ) ) {
287             $hasNextPage    = false;
288             $after  = '';
289             do{
290                 $data   = <<<EOF
291     {
292         translatableResources(first: 50, resourceType: {$type} {$after}) {
293             edges {
294                 cursor
295                 node {
296                     resourceId
297                     translatableContent {
298                     key
299                     value
300                     digest
301                     locale
302                     }
303                     translations(locale: "{$locale}") 
304                     {
305                         locale
306                         key
307                         value
308                     }
309                 }
310             }
311             pageInfo {
312                 hasNextPage
313             }
314         }
315     }
316 EOF;
317 
318                 $ret    = $this->sendRequest( $data );
319                 $hasNextPage    = !empty( $ret['data']['translatableResources']['pageInfo']['hasNextPage'] ) ? true : false;
320                 file_put_contents( $cacheFile, json_encode( $ret ) . PHP_EOL, FILE_APPEND );
321                 foreach( $ret['data']['translatableResources']['edges'] as $edges ) {
322                     $after  = ", after: \"{$edges['cursor']}\"";
323                 }
324                 sleep(1);
325             }while( $hasNextPage );
326         }   
327 
328         $this->_setLog( 'getTranslatableResources over ~' . json_encode( func_get_args() ) );
329     }
330 
331     //get type
332     private function getTranslateType()
333     {
334         $rekey  = $this->_rePrefix . '::getTranslateType';
335         $data   = $this->_redis->get( $rekey );
336         if( $data )
337             return json_decode( $data, true );
338 
339         $data   = <<<EOF
340 {
341     __type(name: "TranslatableResourceType") {
342         enumValues {
343         name
344         }
345     }
346 }
347 EOF;
348 
349         $ret    = $this->sendRequest( $data );
350         if( isset( $ret['data']['__type']['enumValues'] ) ) {
351             $data   = [];
352             foreach( $ret['data']['__type']['enumValues'] as $_name ) {
353                 $data[]     = $_name['name'];
354             }
355             $this->_redis->set( $rekey, json_encode( $data ), 86400 );
356             return $data;
357         }else {
358             $this->_setLog( 'err :: 解析结果失败...' . $ret, Log::WARN );
359         }
360     }
361     
362 
363     
364     private function sendRequest( $data = '', $method = 'post', $urlExtend = 'graphql.json', $header = [], $needResHeader = false, $contentType = 'graphql' )
365     {
366         static $tryTimes = 1;
367         $tmpHeader  = [
368             'Cache-Controller: max-age=0', 
369             'Authorization: Basic ' . $this->_api['authorization'],
370             'X-Shopify-Storefront-Access-Token: ' . $this->_storefrontToken,
371             'Accept: application/json',
372         ];
373         if( is_array( $data ) && !empty( $data ) ) {
374             $data   = json_encode( $data );
375             $tmpHeader[]   = 'Content-Length: ' . strlen( $data );
376         }else {
377             $data   = (string)$data;
378         }
379 
380         if( !empty( $header ) ) {
381             $tmpHeader  = array_merge( $tmpHeader, $header );
382         }
383 
384         if( $contentType == 'graphql' ) {
385             $tmpHeader[]    = 'Content-Type:application/graphql';
386         }else {
387             $tmpHeader[]    = 'Content-Type:application/' . $contentType;
388         }
389 
390         $url    = "https://{$this->_api['key']}:{$this->_api['pwd']}@{$this->_api['url']}{$this->_api['version']}/{$urlExtend}";
391         $this->_setLog( '开始curl '. $method .' ,url == ' . $url . ',, param == ' . $data . ',, header == ' . var_export( $tmpHeader, true ) );
392         $ret    = (new Curl())->execute( $method, $url, $data, '',  $tmpHeader, '', '', $needResHeader );
393         if( is_array( $ret ) || $ret === false ) {
394             $tryTimes++;
395             if( $tryTimes >= 1 ) {
396                 $this->_curlLastErr     = "send curl error ..";
397                 if( isset( $ret[1] ) )
398                     $this->_curlLastErr     .= "{$ret[1]}";
399                 $this->_setLog( 'curl 请求shopify失败,请检查! ' . json_encode( $ret ), Log::WARN );
400                 return false;
401             }else {
402                 return $this->sendRequest( $method, $urlExtend, $data, $header, $needResHeader );
403             }
404         }
405 
406         $deRet    = json_decode( $ret, true );
407 
408         if( $needResHeader ) {
409             if( isset( $deRet['ret']['errors'] ) ) {
410                 $this->_curlLastErr     = $ret;
411                 $this->_setLog( '请求成功,但是返回了失败,.' . $ret, Log::WARN );
412                 return false;
413             }
414         }else {
415             if( isset( $deRet['errors'] )  || isset( $deRet['error'] ) ) {
416                 $this->_curlLastErr     = $ret;
417                 $this->_setLog( '请求成功,但是返回了失败,.' . $ret, Log::WARN );
418                 return false;
419             }
420         }
421 
422         // $this->_setLog( '请求成功了,' . $ret  );
423 
424         return $deRet;
425     }
426 
427     public function htmlMof()
428     {
429 
430         header('Access-Control-Allow-Origin: *');
431         header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');
432 
433         if( substr($_POST['html'], 0, 3) == '<p>' ) {
434             $_POST['html']  = substr( $_POST['html'], 3 );
435             $_POST['html']  = substr( $_POST['html'], 0, -4 );
436         }
437 
438         $ret    = M('shopifyTranslate')->where(['id' => $_POST['id']])->save( ['ja' => $_POST['html'],'mof_time' => time()] );
439         if( $ret !== false ) {
440             $this->setTranslate( $_POST['id'] );
441             echo json_encode( ['code' => 1] );
442         }else 
443             echo json_encode( ['code' => 0] );
444         die;
445     }
446 
447     private function _setLog( $msg, $level = Log::NOTICE )
448     {
449         Log::write( $msg . PHP_EOL, Log::NOTICE, '', durableLog( 'api-shopify-translate' ) );
450         return;
451 
452         if( !$this->_logque instanceof \SplQueue )
453             $this->_logque  = new \SplQueue();
454 
455         $this->_logque->enqueue( $msg . PHP_EOL );
456 
457         if( $level == Log::ERR ) {
458             //send mail
459             exit;
460         }
461     }
462 
463     public function __destruct()
464     {
465         if( $this->_logque instanceof \SplQueue ) {
466             $msg        = '';
467             while( !$this->_logque->isEmpty() ) {
468                 $msg    .= $this->_logque->dequeue();
469             }
470             Log::write( $msg, Log::NOTICE, '', durableLog( 'api-shopify-translate' ) );
471         }
472     }
473 
474 }

 

posted @ 2020-11-06 16:32  栋的博客  阅读(386)  评论(0编辑  收藏  举报
深入理解php php扩展开发 docker mongodb