Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add translator package #461

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions jsk_3rdparty/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<run_depend>respeaker_ros</run_depend>
<run_depend>ros_google_cloud_language</run_depend>
<run_depend>ros_speech_recognition</run_depend>
<run_depend>ros_translator</run_depend>
<run_depend>rospatlite</run_depend>
<run_depend>rosping</run_depend>
<run_depend>rostwitter</run_depend>
Expand Down
35 changes: 35 additions & 0 deletions ros_translator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 2.8.3)
project(ros_translator)

find_package(catkin REQUIRED COMPONENTS dynamic_reconfigure catkin_virtualenv)

if(NOT (("$ENV{ROS_DISTRO}" STREQUAL "noetic") OR
("$ENV{ROS_DISTRO}" STREQUAL "melodic")))
message(WARNINIG "ros_translator can only be built for melodic and noetic")
return()
endif()

catkin_python_setup()

catkin_generate_virtualenv(
PYTHON_INTERPRETER python3
USE_SYSTEM_PACKAGE FALSE
CHECK_VENV FALSE
)


generate_dynamic_reconfigure_options(
cfg/ROSTranslator.cfg
)

catkin_package()

catkin_install_python(PROGRAMS
scripts/ros_translator_node.py
scripts/demo_input_node.py
scripts/demo_output_node.py
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
)

install(FILES requirements.txt
DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION})
52 changes: 52 additions & 0 deletions ros_translator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# ros_translator

A ROS package for language translation.
This package uses Python package [deep-translator](https://pypi.org/project/deep-translator) as a backend.

## Demo

```bash
roslaunch ros_translator demo
```

And then, please input any text to `xterm` prompt. You will get a translated text.

```bash
Input any text >沙羅双樹の花の色、盛者必衰の理をあらはす
```

```bash
[INFO] [WallTime: 1682560820.715069] [node:/demo_output_node] [func:DEMO.callback]: Translated text: The color of the flowers of the sal tree reveals the reason why prosperity must decline
```

## `ros_translator_node.py` Interface

### Subsriber

* `~input_text` (`std_msgs/String`)

Input text to be translated.

### Publisher

* `~output_text` (`std_msgs/String`)

Output text translated.

### Parameters

* `~translator` (`String`, default: `google`)

Backend translation web service

* `~from_language` (`String`, default: `auto`)

Input language

* `~to_language` (`String`, default: `en`)

Target language

* `~api_key` (`String`, default: ``)

API key for some backend web services
13 changes: 13 additions & 0 deletions ros_translator/cfg/ROSTranslator.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env python
PACKAGE = "ros_translator"

from dynamic_reconfigure.parameter_generator_catkin import *

gen = ParameterGenerator()

gen.add('translator', str_t, 0, 'Backend Translator Service', 'google')
gen.add('from_language', str_t, 0, 'Input language', 'auto')
gen.add('to_language', str_t, 0, 'Output Language', 'en')
gen.add('api_key', str_t, 0, 'api_key for translator', '')

exit(gen.generate(PACKAGE, 'ros_translator', 'ROSTranslator'))
15 changes: 15 additions & 0 deletions ros_translator/launch/demo.launch
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<launch>

<node pkg="ros_translator" type="ros_translator_node.py" name="ros_translator" output="screen">
</node>

<node pkg="ros_translator" type="demo_output_node.py" name="demo_output_node" output="screen">
<remap from="~text" to="/ros_translator/output_text"/>
</node>

<node pkg="ros_translator" type="demo_input_node.py" name="demo_input_node" output="screen"
launch-prefix="xterm -e">
<remap from="~text" to="/ros_translator/input_text"/>
</node>

</launch>
26 changes: 26 additions & 0 deletions ros_translator/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0"?>
<package format="2">
<name>ros_translator</name>
<version>2.1.24</version>
<description>The ros_translator package</description>

<maintainer email="[email protected]">Koki Shinjo</maintainer>
<maintainer email="[email protected]">Kei Okada</maintainer>
<author email="[email protected]">Koki Shinjo</author>

<license>BSD</license>

<buildtool_depend>catkin</buildtool_depend>

<depend>rospy</depend>

<build_depend>dynamic_reconfigure</build_depend>
<build_depend>catkin_virtualenv</build_depend>

<exec_depend>std_msgs</exec_depend>
<exec_depend>xterm</exec_depend>

<export>
<pip_requirements>requirements.txt</pip_requirements>
</export>
</package>
Empty file.
89 changes: 89 additions & 0 deletions ros_translator/python/ros_translator/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import rospy
from std_msgs.msg import String
from deep_translator import (
GoogleTranslator,
MicrosoftTranslator,
PonsTranslator,
LingueeTranslator,
MyMemoryTranslator,
YandexTranslator,
PapagoTranslator,
#DeeplTranslator,
#QcriTranslator,
single_detection,
batch_detection)
from dynamic_reconfigure.server import Server
from ros_translator.cfg import ROSTranslatorConfig

engines = {
'google': GoogleTranslator,
'microsoft': MicrosoftTranslator,
'pons': PonsTranslator,
'linguee': LingueeTranslator,
'mymemory': MyMemoryTranslator,
'yandex': YandexTranslator,
'papago': PapagoTranslator,
#'deepl': DeeplTranslator,
#'qcri': QcriTranslator,
}


class ROSTranslator(object):

def __init__(self):

self.from_language = rospy.get_param('~from_language', 'auto')
self.to_language = rospy.get_param('~to_language', 'en')
self.translator = rospy.get_param('~translator', 'google')
self.api_key = rospy.get_param('~api_key', '')
self.engine = None
self.update_engine()

self.param_srv = Server(ROSTranslatorConfig, self.param_callback)
self.pub = rospy.Publisher('~output_text', String, queue_size=1)
self.sub = rospy.Subscriber('~input_text', String, self.callback)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about adding actionlib server?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think actionlib server is not necessary since if we just want to translate some text in one node. It is enough to use deep-translator library directory.

def update_engine(self):

if self.translator in engines:
if self.translator == 'yandex':
self.engine = engines[self.translator](self.api_key)
elif self.translator == 'microsoft':
self.engine = engines[self.translator](api_key=self.api_key,
target=self.to_language)
else:
self.engine = engines[self.translator](source=self.from_language,
target=self.to_language)
else:
rospy.logerr('Unknown translator backend: {}'.format(self.translator))
self.engine = None

def translate(self, text):

if self.engine is None:
rospy.logwarn('Engine not loaded.')
return ''

if self.translator == 'yandex':
return self.engine.translate(source=self.from_language,
target=self.to_language,
text=text)
elif self.translator == 'microsoft':
return self.engine.translate(text)
else:
return self.engine.translate(text)

def callback(self, msg):

output_msg = String()
output_msg.data = self.translate(msg.data)
self.pub.publish(output_msg)

def param_callback(self, config, level):

self.from_language = config['from_language']
self.to_language = config['to_language']
self.translator = config['translator']
self.api_key = config['api_key']
self.update_engine()
return config
1 change: 1 addition & 0 deletions ros_translator/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
deep_translator==1.5.0
25 changes: 25 additions & 0 deletions ros_translator/scripts/demo_input_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python

import rospy
from std_msgs.msg import String


class DEMO(object):

def __init__(self):

self.pub = rospy.Publisher('~text', String, queue_size=1)

def prompt(self):

while not rospy.is_shutdown():

text = input('Input any text >')
msg = String(data=text)
self.pub.publish(msg)


if __name__ == '__main__':
rospy.init_node('demo_input')
node = DEMO()
node.prompt()
21 changes: 21 additions & 0 deletions ros_translator/scripts/demo_output_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python

import rospy
from std_msgs.msg import String


class DEMO(object):

def __init__(self):

self.sub = rospy.Subscriber('~text', String, self.callback)

def callback(self, msg):

rospy.loginfo('Translated text: {}'.format(msg.data))


if __name__ == '__main__':
rospy.init_node('demo_output')
node = DEMO()
rospy.spin()
10 changes: 10 additions & 0 deletions ros_translator/scripts/ros_translator_node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python

import rospy
from ros_translator.core import ROSTranslator

if __name__ == '__main__':

rospy.init_node('ros_translator')
node = ROSTranslator()
rospy.spin()
7 changes: 7 additions & 0 deletions ros_translator/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from distutils.core import setup
from catkin_pkg.python_setup import generate_distutils_setup

d = generate_distutils_setup(packages=['ros_translator'],
package_dir={'': 'python'})

setup(**d)