├── .gitignore ├── LICENSE ├── README.md ├── bin ├── fanyi └── fy ├── composer.json ├── index.php └── lib ├── console.php ├── functions.php ├── print.php └── source.json /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | composer.lock 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2019 jaggle 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # Fanyi 4 | 5 | A PHP version of [afc163/fanyi](https://github.com/afc163/fanyi) 6 | 7 | > A 🇨🇳 and 🇺🇸 translate tool in your command line. 8 | 9 | ![jaggle/fanyi](https://i.loli.net/2019/12/05/XkxtKRfSJumsIUQ.png) 10 | 11 |
12 | 13 | ## Install 14 | 15 | ```bash 16 | $ composer global require jjsty1e/fanyi 17 | ``` 18 | 19 | > I suggest you to use [cgr](https://github.com/consolidation/cgr) to install this command to avoide dependices conflicts. 20 | 21 | then add `$HOME/.composer/vendor/bin` to `PATH` if you haven't yet. 22 | 23 | ## Usage 24 | 25 | ```bash 26 | $ fanyi word 27 | ``` 28 | 29 | For short: 30 | 31 | ```bash 32 | $ fy word 33 | ``` 34 | 35 | Translation data is fetched from [iciba.com](http://iciba.com) and [fanyi.youdao.com](http://fanyi.youdao.com), and only support translation between Chinese and English. 36 | 37 | In Mac/Linux bash, words will be pronounced by `say` command. 38 | 39 | Translate one word. 40 | 41 | ```bash 42 | $ fanyi love 43 | ``` 44 | 45 | ```bash 46 | love 英[ lʌv ] 美[ lʌv ] ~ iciba.com 47 | 48 | - vt.& vi. 爱,热爱;爱戴;喜欢;赞美,称赞; 49 | - vt. 喜爱;喜好;喜欢;爱慕; 50 | - n. 爱情,爱意;疼爱;热爱;爱人,所爱之物; 51 | 52 | 1. Love is the radical of lovely , loveliness , and loving. 53 | Love是lovely, loveliness 及loving的词根. 54 | 2. She rhymes " love " with " dove ". 55 | 她将 " love " 与 " dove " 两字押韵. 56 | 3. In sports, love means nil. 57 | 体育中, love的意思是零. 58 | 4. It's been years since any hazardous - waste site as dramatic as Love Canal has been discovered. 59 | 自Love运河中发现触目惊心的危险废料堆放场所以来,已过去多年. 60 | 5. Is love, love, love oaths , evanescent love, absolutely love, love always can not escape this rule. 61 | 是爱,示爱, 誓爱, 逝爱, 绝爱, 爱情始终逃不过这个规律. 62 | 63 | -------- 64 | 65 | love 英[ lʌv ] 美[ lʌv ] ~ fanyi.youdao.com 66 | 67 | - n. 爱;爱情;喜好;(昵称)亲爱的;爱你的;心爱的人;钟爱之物;零分 68 | - v. 爱恋(某人);关爱;喜欢(某物或某事);忠于 69 | - n. (Love) (英、菲、瑞、美)洛夫(人名) 70 | 71 | 1. love 72 | 爱, 爱情, 爱心 73 | 2. Endless Love 74 | 无尽的爱, 蓝色生死恋, 不了情 75 | 3. puppy love 76 | 早恋, 青春期恋爱, 初恋 77 | 78 | -------- 79 | ``` 80 | 81 | More words. 82 | 83 | ```bash 84 | $ fanyi make love 85 | ``` 86 | 87 | Support Chinese, even sentence. 88 | 89 | ```bash 90 | $ fanyi 和谐 91 | ``` 92 | 93 | ```bash 94 | $ fanyi 那只敏捷的棕毛狐狸跃过那只懒狗 95 | ``` 96 | -------------------------------------------------------------------------------- /bin/fanyi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | =5.6.0", 8 | "guzzlehttp/guzzle": "^6.4", 9 | "symfony/console": "^3.4", 10 | "symfony/process": "^3.4" 11 | }, 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Jaggle", 16 | "email": "singviy@gmail.com" 17 | } 18 | ], 19 | "bin": ["bin/fanyi", "bin/fy"] 20 | } 21 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | addArgument(new InputArgument('word', InputArgument::OPTIONAL, 'the word to translate')); 39 | $inputDef->addOption(new InputOption('help', 'h', InputOption::VALUE_OPTIONAL, 'show this message.', false)); 40 | $inputDef->addOption(new InputOption('version', 'v', InputOption::VALUE_OPTIONAL, 'show version number', false)); 41 | 42 | $input = new ArgvInput([$app, $word], $inputDef); 43 | $word = $input->getArgument('word'); 44 | 45 | if (strlen($word) === 0) { 46 | if ($input->getOption('version') !== false) { 47 | return $print->output->writeln(FANYI_VERSION); 48 | } 49 | 50 | // same as $input->getOption('help') !== false 51 | $print->output->writeln(''); 52 | $print->output->writeln('Examples:'); 53 | $print->output->writeln(' $ fanyi word'); 54 | $print->output->writeln(' $ fanyi world peace'); 55 | $print->output->writeln(' $ fanyi 你好'); 56 | return $print->output->writeln(''); 57 | } 58 | 59 | try { 60 | $process = new Process('say ' . urldecode($word)); 61 | $process->start(); 62 | } 63 | catch (\Exception $e) {} 64 | 65 | $request = new Client(); 66 | 67 | $promises = []; 68 | 69 | // iciba 70 | $promises[] = $request->getAsync(str_replace('${word}', $word, $source->iciba)) 71 | ->then( 72 | function (ResponseInterface $response) use ($print) { 73 | if ($response->getStatusCode() === 200) { 74 | return parse_xml($response->getBody(), function ($err, $result) use ($print) { 75 | if (!$err) { 76 | return call_user_func($print->iciba, $result); 77 | } 78 | }); 79 | } 80 | }, $print->error 81 | ); 82 | 83 | // youdao 84 | $promises[] = $request->getAsync(str_replace('${word}', $word, $source->youdao)) 85 | ->then( 86 | function (ResponseInterface $response) use ($print) { 87 | if ($response->getStatusCode() === 200) { 88 | if (($result = json_decode($response->getBody()))) { 89 | return call_user_func($print->youdao, $result); 90 | } 91 | } 92 | }, $print->error 93 | ); 94 | 95 | // dictionaryapi 96 | $promises[] = $request->getAsync(str_replace('${word}', $word, $source->dictionaryapi), ['timeout' => 12]) 97 | ->then( 98 | function (ResponseInterface $response) use ($print) { 99 | if ($response->getStatusCode() === 200) { 100 | return parse_xml($response->getBody(), function ($err, $result) use ($print) { 101 | if ($err) { 102 | return null; 103 | } 104 | 105 | return call_user_func($print->dictionaryapi, $result); 106 | }); 107 | } 108 | }, $print->error 109 | ); 110 | 111 | Promise\settle($promises)->wait(); 112 | $process->wait(); 113 | -------------------------------------------------------------------------------- /lib/console.php: -------------------------------------------------------------------------------- 1 | getFormatter()->setStyle('magenta', new OutputFormatterStyle('magenta', 'default')); 14 | $output->getFormatter()->setStyle('error', new OutputFormatterStyle('red', 'default')); 15 | $output->getFormatter()->setStyle('question', new OutputFormatterStyle('cyan', 'default')); 16 | $output->getFormatter()->setStyle('gray', new OutputFormatterStyle('cyan', 'default')); 17 | $output->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan', 'default')); 18 | $output->getFormatter()->setStyle('bold', new OutputFormatterStyle('default', 'default', ['bold'])); 19 | 20 | return $output; 21 | -------------------------------------------------------------------------------- /lib/functions.php: -------------------------------------------------------------------------------- 1 | $output, 6 | 'error' => function ($message) use ($output) { 7 | $output->writeln("{$message}"); 8 | } 9 | ]; 10 | 11 | $export->iciba = function ($data, $options = null) use ($output) { 12 | $output->writeln(''); 13 | $ukPs = $usPs = $ps = ''; 14 | 15 | if (!empty($data->ps)) { 16 | if (is_array($data->ps)) { 17 | if (!empty($data->ps[0])) $ukPs = "英[ {$data->ps[0]} ] "; 18 | if (!empty($data->ps[1])) $usPs = "美[ {$data->ps[1]} ] "; 19 | } else { 20 | $usPs = "美[ {$data->ps} ] "; 21 | } 22 | } 23 | 24 | if ($ukPs || $usPs) { 25 | $ps = ' ' . "{$ukPs}{$usPs}"; 26 | } 27 | $output->write(' ' . $data->key . ''); 28 | $output->writeln($ps . ' ~ iciba.com'); 29 | $output->writeln(''); 30 | 31 | if (!empty($data->pos)) { 32 | $trans = []; 33 | if (is_array($data->pos)) { 34 | foreach ($data->pos as $key => $item) { 35 | $trans[$key][0] = trim($item); 36 | } 37 | 38 | foreach ($data->acceptation as $key => $item) { 39 | $trans[$key][1] = trim($item); 40 | } 41 | } 42 | if (is_string($data->pos)) { 43 | $trans[] = [$data->pos, (string)$data->acceptation]; 44 | } 45 | if (!empty($trans)) { 46 | foreach ($trans as $item) { 47 | $item = array_map('trim', $item); 48 | $output->writeln(" - {$item[0]} {$item[1]}"); 49 | } 50 | $output->writeln(''); 51 | } 52 | } 53 | 54 | if (!empty($data->sent)) { 55 | foreach ($data->sent as $key => $item) { 56 | $key++; 57 | $orig = trim(str_replace($data->key, "{$data->key}", $item->orig)); 58 | $orig = trim(str_replace(ucfirst($data->key), "" . ucfirst($data->key) . "", $orig)); 59 | $trans = trim($item->trans); 60 | $output->writeln(" {$key}. {$orig}"); 61 | $output->writeln(str_pad(' ', 4) . "{$trans}"); 62 | } 63 | } 64 | 65 | $output->writeln(''); 66 | $output->writeln(' --------'); 67 | }; 68 | 69 | $export->youdao = function ($data, $options = null) use ($output) { 70 | $output->writeln(''); 71 | $cnPtic = $hkPtic = $usPtic = $ptic = ''; 72 | if (!empty($data->basic->{'uk-phonetic'})) $hkPtic = "英[ {$data->basic->{'uk-phonetic'}} ] "; 73 | if (!empty($data->basic->{'us-phonetic'})) $usPtic = "美[ {$data->basic->{'us-phonetic'}} ] "; 74 | if (empty($data->basic->{'uk-phonetic'}) && 75 | empty($data->basic->{'us-phonetic'}) && 76 | !empty($data->basic->phonetic) 77 | ) { 78 | $cnPtic = "[ {$data->basic->phonetic} ] "; 79 | } 80 | if ($hkPtic || $usPtic) { 81 | $ptic = ' ' . "$cnPtic{$hkPtic}{$usPtic}"; 82 | } 83 | $output->write(' ' . $data->query . ''); 84 | $output->writeln($ptic . ' ~ fanyi.youdao.com'); 85 | $output->writeln(''); 86 | 87 | if (!empty($data->basic->explains)) { 88 | foreach ($data->basic->explains as $item) { 89 | $output->writeln(" - {$item}"); 90 | } 91 | $output->writeln(''); 92 | } 93 | 94 | if (!empty($data->web)) { 95 | foreach ($data->web as $index => $item) { 96 | $index++; 97 | $key = trim(str_replace($data->query, "{$data->query}", $item->key)); 98 | $key = trim(str_replace(ucfirst($data->query), "" . ucfirst($data->query) . "", $key)); 99 | $value = implode(', ', $item->value); 100 | $output->writeln(" {$index}. {$key}"); 101 | $output->writeln(str_pad(' ', 4) . "{$value}"); 102 | } 103 | } 104 | 105 | if (!empty($data->translation)) { 106 | $data->translation = (array)$data->translation; 107 | foreach ($data->translation as $trans) { 108 | $output->writeln(" - {$trans}"); 109 | } 110 | } 111 | 112 | $output->writeln(''); 113 | $output->writeln(' --------'); 114 | }; 115 | 116 | $export->dictionaryapi = function ($data, $options = null) use ($output) { 117 | $output->writeln(''); 118 | if (empty($data->entry)) return null; 119 | if (!is_array($data->entry)) { 120 | $data->entry = [$data->entry]; 121 | } 122 | $word = $data->entry[0]->hw; 123 | $output->writeln(' ' . $word . ' ~ dictionaryapi.com'); 124 | $output->writeln(''); 125 | $allStrings = []; 126 | foreach ($data->entry as $key => $item) { 127 | if (!isset($item->def) || !isset($item->def->dt)) continue; 128 | $strings = $item->def->dt; 129 | if (is_string($strings)) { 130 | $strings = [$strings]; 131 | } 132 | $strings = array_filter($strings, function ($val) { 133 | return is_string($val) && strlen(trim($val, ' :')) > 0; 134 | }); 135 | $strings = array_map(function ($val) { 136 | return trim($val, ' :'); 137 | }, $strings); 138 | $allStrings = array_merge($allStrings, $strings); 139 | } 140 | 141 | foreach ($allStrings as $key => $string) { 142 | if ($key <= 9) { 143 | $string = trim(str_replace($word, "{$word}", $string)); 144 | $string = trim(str_replace(ucfirst($word), "" . ucfirst($word) . "", $string)); 145 | $output->writeln(" - {$string}"); 146 | } 147 | } 148 | 149 | $output->writeln(''); 150 | $output->writeln(' --------'); 151 | }; 152 | 153 | return $export; 154 | -------------------------------------------------------------------------------- /lib/source.json: -------------------------------------------------------------------------------- 1 | { 2 | "iciba": "http://dict-co.iciba.com/api/dictionary.php?key=D191EBD014295E913574E1EAF8E06666&w=${word}", 3 | "youdao": "http://fanyi.youdao.com/openapi.do?keyfrom=node-fanyi&key=110811608&type=data&doctype=json&version=1.1&q=${word}", 4 | "dictionaryapi": "http://www.dictionaryapi.com/api/v1/references/collegiate/xml/${word}?key=82c5d495-ccf0-4e72-9051-5089e85c2975" 5 | } 6 | --------------------------------------------------------------------------------