高校生でプログラミングが好きなレオです!!
今回はリアルタイムで言語変換できるサービスを作ったので、
紹介とどのように作ったのか少し説明したいと思います。
※この記事は、Node.jsとPython,Socket.ioを使える前提で話を進めていきます。
1. アイディア
アイディアはWeChatの翻訳機能を使っていた時に思いつきました。
僕が中国でお買い物をする際に店員さんと、WeChatを使って会話をすることがあるのですが、リアルタイムではなく、毎度翻訳をかけないと行けなかったので、リアルタイムで翻訳できて、友達交換もしなくていいサービスを作ろう!と思ったのがきっかけです。
2. 使用した言語と技術
使用したプログラミング言語はNode.jsとPythonです。
主にNode.jsを使っていて、Node.js上でSocket.ioなどの通信部分やテンプレート(EJS)を表示しています。
Pythonはgoogletransというライブラリを使用するためにPythonを使用していて、
Node.jsとPythonの間はPython-Shellというライブラリを使ってデータのやりとりをしています。
3. ファイル構成
.
├── app.js
├── package-lock.json
├── package.json
├── public
│ ├── css
│ │ ├── chat
│ │ │ └── stylesheet.css
│ │ └── home
│ │ └── stylesheet.css
│ ├── img
│ │ └── button.svg
│ └── js
│ ├── chat
│ │ └── main.js
│ └── home
│ └── home.js
├── python
│ └── translate.py
└── templates
├── chat
│ └── index.ejs
└── home
└── index.ejs
ファイル構成は一般的なexpressの構成に言語変換用のpythonファイル(translate.py)を追加しました。
4. Node.js
varexpress=require('express')varapp=express()varhttp=require('http').createServer(app)vario=require('socket.io')(http,{path:'/message'})varuuid=require('node-uuid')var{PythonShell}=require('python-shell')app.use(express.static('public'))app.set("view engine","ejs")varroom=[]varlang={}app.get('/',function(req,res){res.render(__dirname+'/templates/home/index.ejs')})app.post('/transroom',function(req,res){varroomid=uuid.v4().split('-').join('')room.push(roomid)res.redirect('/transroom/'+roomid)})app.get('/transroom/:roomid',function(req,res){if(room.includes(req.params.roomid)){res.render(__dirname+'/templates/chat/index.ejs')}else{res.redirect('/')}})functiontrans_mes(mes,before_lang,trans_lang,send_user){if(1300>=mes.length){varpyshell=newPythonShell('./python/translate.py')pyshell.send(JSON.stringify({'transdata':mes,'translang':trans_lang,'sendlang':before_lang}))pyshell.on('message',function(data){if(data=='translate_error'){io.to(send_user).emit('message','servererror')}else{io.to(send_user).emit('message',JSON.parse(data))}})}else{io.to(send_user).emit('message','error')}}これがメインのNode.jsのコードです。
1行目〜6行目は必要なライブラリ、ExpressやSocket.io,Node-uuid,python-shellの読み込みを行っています。
7行目と8行目では、パブリックファイルのパス指定とテンプレートエンジンの指定を行っています。
16行目〜20行目では、ユニークのRoomIDを作成し、room配列にRoomIDを追加して、ユーザをリダイレクトしています。
22行目〜28行目では、room配列にリクエストされたRoomIDがあるか確認をして、あった場合はテンプレートを表示し、なかった場合はリダイレクトをしています。
function trans_mesでは、翻訳するメッセージ・送信したユーザの言語・送信先のユーザの言語・送信先のSocketIDを取得し、そのデータを使って、Python-ShellでPythonを実行し、翻訳されたデータをSocket.ioで送信しています。
ただ、googletransの使用上文字数制限があり、pythonを実行する前に文字数の確認を行なっています。
5. Socket.io
io.on('connection',function(socket){varroomid=socket.handshake.headers.referer.split('/')[4]if(roomid!=null){socket.join(roomid)varroom_user_count=io.sockets.adapter.rooms[roomid].lengthif(room_user_count==1){io.to(socket.id).emit('sys',{'roomid':roomid})}elseif(room_user_count==2){socket.broadcast.to(roomid).emit('sys','join_user')}elseif(room_user_count>=3){io.to(socket.id).emit('sys','redirect')}socket.on('message',function(msg,sendlang){varclient_list=io.sockets.adapter.rooms[roomid].socketsfor(varclientIdinclient_list){if(socket.id!=clientId&&lang[clientId]){trans_mes(msg,lang[socket.id],lang[clientId],clientId)}}})socket.on('lang',function(user_lang){lang[socket.id]=user_lang})socket.on('disconnect',function(){socket.leave(roomid)if(io.sockets.adapter.rooms[roomid]==null){varlist_room_id=room.indexOf(roomid)if(list_room_id>-1){room.splice(list_room_id,1)}}else{if(io.sockets.adapter.rooms[roomid].length==1){socket.broadcast.to(roomid).emit('sys',{'user_out':roomid})}}})}})これが、Socket.io部分です。
まず、最初にvar roomid = socket.handshake.headers.referer.split('/')[4]でリクエストされたRoomIDを取得して、socket.join(roomid)でユーザをルームに入室しています。
socket.on('message',function(msg,sendlang){varclient_list=io.sockets.adapter.rooms[roomid].socketsfor(varclientIdinclient_list){if(socket.id!=clientId&&lang[clientId]){trans_mes(msg,lang[socket.id],lang[clientId],clientId)}}})次にメッセージの機能ですが、var client_list = io.sockets.adapter.rooms[roomid].socketsの部分でRoomに入室しているユーザのリストを取得しています。
for(varclientIdinclient_list){if(socket.id!=clientId&&lang[clientId]){trans_mes(msg,lang[socket.id],lang[clientId],clientId)}}次に、ユーザのリストをFor分で分割し、if文を使って、送信者以外を選択し、function trans_mesを実行します。
これによって、送信者以外のユーザに翻訳されたメッセージが送信されます。
6. Python
fromgoogletransimportTranslatorimportjsonimportsystranslator=Translator()try:data=sys.stdin.readline()data_json=json.loads(data)lang=data_json['translang']sendlang=data_json['sendlang']translate=translator.translate(data_json['transdata'],src=sendlang,dest=lang)json=json.dumps({'trans_data':translate.text,'original_data':data_json['transdata'],'user_lang':lang,'trans_lang':translate.src},ensure_ascii=False)print(json)exceptExceptionase:print('translate_error')Pythonのコードは簡単で、sys.stdin.readline()でNode.jsで指定した情報を取得して、それをgoogletransを使って翻訳をし、Json形式でNode.jsに変換しています。
7. 最後に。
Socket.ioをここまでガッツリ書いたのが初めてだったのですが、結構簡単にかけて楽しかったです。
ただ、簡単なサービスな割にはコードが複雑になってしまったため、今後もアップデートを続けて行けたらなと思います(プルリク大歓迎です!!)
それと、もし良ければ、インストールして試してもらえると嬉しいです。
git clone https://github.com/lra21711214/transchat.git
cd transchat
npm install
node app.js
このようにGitインストールを行なってnpm installをするだけで試すことが可能なのでお願いします!!
※注意※
実際に本番環境で、使用する際は、Room配列やユーザの言語情報などをDBに保存するようにしたり、翻訳部分をAmazon TranslateやCloud Translation,DeepL APIなどを使用してください。
あくまで、お試し感覚で作成したため、本番環境向けではございません。