33
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

高校生がリアルタイム言語変換のサービスを作ってGithubにPushしてみた。

Last updated at Posted at 2020-10-03

高校生でプログラミングが好きなレオです!!

今回はリアルタイムで言語変換できるサービスを作ったので、
紹介とどのように作ったのか少し説明したいと思います。

※この記事は、Node.jsとPython,Socket.ioを使える前提で話を進めていきます。

1. アイディア

アイディアはWeChatの翻訳機能を使っていた時に思いつきました。
僕が中国でお買い物をする際に店員さんと、WeChatを使って会話をすることがあるのですが、リアルタイムではなく、毎度翻訳をかけないと行けなかったので、リアルタイムで翻訳できて、友達交換もしなくていいサービスを作ろう!と思ったのがきっかけです。

2. 使用した言語と技術

使用したプログラミング言語はNode.jsとPythonです。
主にNode.jsを使っていて、Node.js上でSocket.ioなどの通信部分やテンプレート(EJS)を表示しています。
Pythonはgoogletransというライブラリを使用するために採用していて、
Node.jsとPythonの間はPython-Shellというライブラリを使ってデータのやりとりをしています。

3. ファイル構成

transchatのファイル構成
.
├── 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

app.js

var express = require('express')
var app = express()
var http = require('http').createServer(app)
var io = require('socket.io')(http,{path:'/message'})
var uuid = require('node-uuid')
var {PythonShell} = require('python-shell')
app.use(express.static('public'))
app.set("view engine", "ejs")
var room = []
var lang = {}

app.get('/', function(req,res){
	res.render(__dirname+'/templates/home/index.ejs')
})

app.post('/transroom', function(req,res){
	var roomid = 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('/')
	}
})

function trans_mes(mes, before_lang, trans_lang, send_user){
	if(1300 >= mes.length){
		var pyshell = new PythonShell('./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行目は必要なライブラリ、ExpressSocket.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

app.js
io.on('connection', function(socket){
	var roomid = socket.handshake.headers.referer.split('/')[4]
	if(roomid != null){
 		socket.join(roomid)
 		var room_user_count = io.sockets.adapter.rooms[roomid].length
 		if(room_user_count == 1){
 			io.to(socket.id).emit('sys', {'roomid':roomid})
 		}else if(room_user_count == 2){
 			socket.broadcast.to(roomid).emit('sys', 'join_user')
 		}else if(room_user_count >= 3){
 			io.to(socket.id).emit('sys', 'redirect')
 		}
 		
		socket.on('message', function(msg,sendlang){
			var client_list = io.sockets.adapter.rooms[roomid].sockets
			for (var clientId in client_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){
				var list_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)でユーザをルームに入室しています。

app.js
socket.on('message', function(msg,sendlang){
    var client_list = io.sockets.adapter.rooms[roomid].sockets
    for (var clientId in client_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に入室しているユーザのリストを取得しています。

app.rb
for (var clientId in client_list ) {
    if(socket.id != clientId && lang[clientId]){
        trans_mes(msg, lang[socket.id], lang[clientId], clientId)
    }
}

次に、ユーザのリストをFor分で分割し、if文を使って、送信者以外を選択し、function trans_mesを実行します。
これによって、送信者以外のユーザに翻訳されたメッセージが送信されます。

6. Python

python/translate.py
from googletrans import Translator
import json
import sys
translator = 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)
except Exception as e:
	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 cloneを行なってnpm installをするだけで試すことが可能なのでお願いします!!

※注意※
実際に本番環境で、使用する際は、Room配列やユーザの言語情報などをDBに保存するようにしたり、翻訳部分をAmazon TranslateCloud Translation,DeepL APIなどを使用してください。
あくまで、お試し感覚で作成したため、本番環境向けではございません。

33
25
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
25

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?