Astonishing Language Translator Using the Mighty Python 3.10

ZennDogg
8 min readNov 30, 2021
Image by the Author

I live in a multi-language household. We all communicate quite well but sometimes there is a bit of a language barrier. This problem usually pops up with letters and correspondence.

I recently came across an article for a simple text translator in just 4 lines of code. So, of course, I had to expand the program and build it into a GUI-based application. Also, since I always hard code the UI, the file is far from 4 lines of code. Also of note, I recently upgraded from PyQt5 to PyQt6.

Let’s begin with the import statements.

import sys, os
from PyQt6.QtCore import pyqtSignal, QObject, Qt
from PyQt6.QtGui import QFont, QColor
from PyQt6.QtWidgets import (
QApplication,
QPushButton,
QMainWindow,
QListWidget,
QTextEdit,
QLabel,
)
from googletrans import Translator, LANGUAGES

googletrans has a history of not working very well. After searching StackOverflow.com, I found a version of googletrans that does work well with python 3.10.

pip install googletrans==3.1.0a0

The next thing is to write the first of two classes used in this program. It’s a very short class to capture a particular PyQt6 signal.

class Communicate(QObject):
closeApp = pyqtSignal()

The second class is the main GUI class. Here the GUI is initialized and the size and fonts used are defined here.

class LanguageTranslator(QMainWindow):

def __init__(self):
super().__init__()
self.left = 1150
self.top = 40
self.width = 400
self.height = 225
self.comm = Communicate()
self.comm.closeApp.connect( self.close )
self.translator = Translator()

canda_10 = QFont( "Candalara", 10 )
canda_11 = QFont( "Candalara", 11 )

Readers of my blog know I have a particular style I use for all my GUIs. This style is an all-black background without using the normal window constructs. Below is the code used for the GUI portion of this style.

        self.setWindowFlags( Qt.WindowType.FramelessWindowHint )
self.setGeometry( self.left, self.top, self.width, self.height )
self.setAutoFillBackground( True )
self.setStyleSheet( 'background-color: black' )

The next section finishes defining the styles used for the buttons on the GUI.

        btn_style = "QPushButton {color: rgba(125,125,125,255); background-color: darkBlue}"        new_style = "QPushButton {color: rgba(125,125,125,255);}"        qdim = "QPushButton {color: rgba(54, 136, 200, 250); " "background-color: black; }"

Now we need to define the two labels used to display the source and destination languages. These languages are chosen from a set of lists (code to follow shortly). We hide the labels initially as they are not part of the opening sequence for the GUI.

        self.src = QLabel( self )
self.src.setGeometry( 40, 40, 100, 25 )
self.src.setFont( canda_11 )
self.src.setStyleSheet("QLabel {color: rgba(125,125,125,255); background-color: black;}")
self.src.hide()

self.dst = QLabel( self )
self.dst.setGeometry( 230, 40, 100, 25 )
self.dst.setFont( canda_11 )
self.dst.setStyleSheet( "QLabel {color: rgba(125,125,125,255); background-color: black;}" )
self.dst.hide()

Notice on the opening image the two column headers for choosing the language. These are actually buttons that are clicked to display the drop-down list of languages. Here is their code.

        self.source = QPushButton(self)
self.source.setStyleSheet(new_style)
self.source.setFont(canda_10)
self.source.setText("Choose Source Language:")
self.source.setGeometry(0, 10, 200, 23)
self.source.clicked.connect(self.source_language)
self.destin= QPushButton(self)
self.destin.setStyleSheet(new_style)
self.destin.setFont(canda_10)
self.destin.setText("Choose Destination Language:")
self.destin.setGeometry(200, 10, 200, 23)
self.destin.clicked.connect(self.destin_language)

I always use a symbol as an ‘Exit’ button.

        symbol = u"\u2592"  # My signature "EXIT" button symbol
self.exit = QPushButton(self)
self.exit.setStyleSheet(qdim)
self.exit.setFont(canda_11)
self.exit.setText(symbol)
self.exit.setGeometry(375, 190, 20, 20)
self.exit.clicked.connect(self.mousePressEvent)

It takes six lines of code to define a button. Imagine writing a scientific calendar with 40+ buttons. That’s 240 lines of code for just buttons. I wrote a short_cut function for defining buttons that is one line:

def qtbutton(self, name, x, y, w, h, style, font, title, connection):
self.name = QPushButton(self)
self.name.setStyleSheet(style)
self.name.setFont(font)
self.name.setText(title)
self.name.setGeometry(x, y, w, h)
self.name.clicked.connect(connection)

Here is what the code for the above three buttons looks like in my file.

qtbutton(self, "exit", 375, 190, 20, 20, qdim, canda_11, symbol, self.mousePressEvent)qtbutton(self, 'source',0, 10, 200, 23 , new_style, canda_10, "Choose Source Language:", self.source_language)qtbutton(self, 'destin', 200, 10, 200, 23, new_style, canda_10, "Choose Destination Language:", self.destin_language)

We just saved 200 lines of code for our theoretical scientific calculator! There is one caveat to this function. If, for some reason, the button needs to be called later, it won’t work using this function. Luckily for us, most buttons are static and will not be called. This is why I used the long-form version of the function for the next two buttons. They are called from elsewhere in the program.

        self.cleard = QPushButton(self)
self.cleard.setGeometry(200, 185, 80, 30)
self.cleard.setFont( canda_11 )
self.cleard.setText( "Clear" )
self.cleard.setStyleSheet( "QPushButton {color: gray; background-color: darkBlue}" )
self.cleard.clicked.connect( self.clear )
self.cleard.hide()

self.translated = QPushButton(self)
self.translated.setGeometry(115, 185, 80, 30)
self.translated.setFont( canda_11 )
self.translated.setText("Translate")
self.translated.setStyleSheet("QPushButton {color: gray; background-color: darkBlue}")
self.translated.clicked.connect(self.translate)
self.translated.hide()

The ‘Clear’ button resets the GUI to its original state. The ‘Translate’ button calls the translate function. Again, these widgets are hidden until needed.

Next, we have two list widgets; one for choosing the source language, the other for the destination language.

        self.source = QListWidget( self)
self.source.setGeometry( 10, 40, 175, 175 )
self.source.resetVerticalScrollMode()
self.source.setFont( canda_10 )
self.source.setStyleSheet("QListWidget {color: rgba(125,125,125,255); background-color: black;}")
self.source.hide()

self.destin = QListWidget( self )
self.destin.setGeometry( 210, 40, 175, 175 )
self.destin.resetVerticalScrollMode()
self.destin.setFont( canda_10 )
self.destin.setStyleSheet( "QListWidget {color: rgba(125,125,125,255); background-color: black;}" )
self.destin.hide()

Lastly, our GUI requires an input area and an output area. We use two QTextEdit boxes for this.

        self.src_box = QTextEdit( self)
self.src_box.setTextColor(QColor('#d6e3ec' ))
self.src_box.setFont( canda_11 )
self.src_box.setGeometry( 10, 75, 380, 100 )
self.src_box.hide()

self.dst_box = QTextEdit( self )
self.dst_box.setTextColor(QColor('#d6e3ec' ))
self.dst_box.setFont( canda_11 )
self.dst_box.setGeometry( 10, 75, 380, 100 )
self.dst_box.hide()

Since there are two list widgets with the same information, this function is used to fill both depending on whether called from the source language request or the destination language request. So, var will be either self.source or self.destin.

    def fill_list_widget(self, var):
# Using count like this prevents index errors.
count = 0
for item in list( LANGUAGES.values() ):
if count < len(list( LANGUAGES.values())):
var.insertItem( count, item )
count += 1
else:
pass

Now we need to display those lists when requested. The first line in each function calls the fill_list_widget() function. When a language is clicked, another function is called.

    # Displays the list of languages for the source language.
def
source_language(self):
self.fill_list_widget( self.source )
self.source.show()
# When chosen language is clicked.
self
.source.clicked.connect( self.src_on_click)

# Displays the list of languages for the destination language.
def
destin_language(self):
self.fill_list_widget( self.destin)
self.destin.show()
# When chosen language is clicked.
self
.destin.clicked.connect( self.dst_on_click )

Once a language has been selected, the list closes and the chosen language is displayed in its label. Then the appropriate text area is displayed.

Once the destination language is chosen, the “Clear” and “Translate” buttons are displayed at the bottom of the GUI.

    # Displays the chosen source language.
def
src_on_click(self):
self.source.hide()
choice = self.source.currentItem()
self.src.setText(choice.text())
self.src.show()

# Displays the chosen destination language.
def
dst_on_click(self):
self.destin.hide()
choice = self.destin.currentItem()
self.dst.setText(choice.text())
self.dst.show()
self.translated.show()
self.cleard.show()
self.src_box.show()
self.src_box.setFocus()

The function for the “Clear” button hides everything, returning the GUI to its opening state.

    def clear(self):
self.destin.hide()
self.source.hide()
self.dst_box.hide()
self.src_box.hide()
self.dst.hide()
self.src.hide()
self.cleard.hide()
self.translated.hide()

The “Translate” button calls the translate function. This function closes the source text box and opens the destination text box with the translated text displayed.

    def translate(self):
translated = self.translator.translate(self.src_box.toPlainText(), dest=self.dst.text())
self.dst_box.setText(translated.text )
self.src_box.hide()
self.dst_box.show()

The “Exit” button calls a function named mousePressEvent(). This closes the app.

    def mousePressEvent(self):
self.comm.closeApp.emit()

And finally, we need to actually run the app.

if __name__ == '__main__':
app = QApplication([])
ex = LanguageTranslator()
ex.show()
sys.exit(app.exec())
Source language
Destination language

If you enjoy reading stories like these and want to support me as a writer, consider subscribing to Medium for $5 a month. As a member, you have unlimited access to stories on Medium. If you sign up using my link, I’ll earn a small commission.

Full code:

import sys, os
from PyQt6.QtCore import pyqtSignal, QObject, Qt
from PyQt6.QtGui import QFont, QColor
from PyQt6.QtWidgets import (
QApplication,
QPushButton,
QMainWindow,
QListWidget,
QTextEdit,
QLabel,
)
from googletrans import Translator, LANGUAGES


class Communicate(QObject):
closeApp = pyqtSignal()


class LanguageTranslator(QMainWindow):

def __init__(self):
super().__init__()
self.left = 1150
self.top = 40
self.width = 400
self.height = 225
self.comm = Communicate()
self.comm.closeApp.connect( self.close )
self.translator = Translator()

canda_10 = QFont( "Candalara", 10 )
canda_11 = QFont( "Candalara", 11 )

self.setWindowFlags( Qt.WindowType.FramelessWindowHint )
self.setGeometry( self.left, self.top, self.width, self.height )
self.setAutoFillBackground( True )
self.setStyleSheet( 'background-color: black' ))
qdim = "QPushButton {color: rgba(54, 136, 200, 250); " "background-color: black; }"

btn_style = "QPushButton {color: rgba(125,125,125,255); background-color: darkBlue}"
new_style = "QPushButton {color: rgba(125,125,125,255);}"

self.src = QLabel( self )
self.src.setGeometry( 40, 40, 100, 25 )
self.src.setFont( canda_11 )
self.src.setStyleSheet("QLabel {color: rgba(125,125,125,255); background-color: black;}")
self.src.hide()

self.dst = QLabel( self )
self.dst.setGeometry( 230, 40, 100, 25 )
self.dst.setFont( canda_11 )
self.dst.setStyleSheet( "QLabel {color: rgba(125,125,125,255); background-color: black;}" )
self.dst.hide()

symbol = u"\u2592" # My signature "EXIT" button symbol
qtbutton( self, "exit", 375, 190, 20, 20, qdim, canda_11, symbol, self.mousePressEvent )
qtbutton(self, 'source',0, 10, 200, 23 , new_style, canda_10, "Choose Source Language:", self.source_language)
qtbutton(self, 'destin', 200, 10, 200, 23, new_style, canda_10, "Choose Destination Language:", self.destin_language )

self.cleard = QPushButton(self)
self.cleard.setGeometry(200, 185, 80, 30)
self.cleard.setFont( canda_11 )
self.cleard.setText( "Clear" )
self.cleard.setStyleSheet( "QPushButton {color: gray; background-color: darkBlue}" )
self.cleard.clicked.connect( self.clear )
self.cleard.hide()

self.translated = QPushButton(self)
self.translated.setGeometry(115, 185, 80, 30)
self.translated.setFont( canda_11 )
self.translated.setText("Translate")
self.translated.setStyleSheet("QPushButton {color: gray; background-color: darkBlue}")
self.translated.clicked.connect(self.translate)
self.translated.hide()

self.source = QListWidget( self)
self.source.setGeometry( 10, 40, 175, 175 )
self.source.resetVerticalScrollMode()
self.source.setFont( canda_10 )
self.source.setStyleSheet("QListWidget {color: rgba(125,125,125,255); background-color: black;}")
self.source.hide()

self.destin = QListWidget( self )
self.destin.setGeometry( 210, 40, 175, 175 )
self.destin.resetVerticalScrollMode()
self.destin.setFont( canda_10 )
self.destin.setStyleSheet( "QListWidget {color: rgba(125,125,125,255); background-color: black;}" )
self.destin.hide()

self.src_box = QTextEdit( self)
self.src_box.setTextColor(QColor('#d6e3ec' ))
self.src_box.setFont( canda_11 )
self.src_box.setGeometry( 10, 75, 380, 100 )
self.src_box.hide()

self.dst_box = QTextEdit( self )
self.dst_box.setTextColor(QColor('#d6e3ec' ))
self.dst_box.setFont( canda_11 )
self.dst_box.setGeometry( 10, 75, 380, 100 )
self.dst_box.hide()


# Since there are two list widgets with the same information,
# this function is used to fill both depending on 'var',
# ie: self.source or self.destin.
def
fill_list_widget(self, var):
# Using count like this prevents index errors.
count = 0
for item in list( LANGUAGES.values() ):
if count < len(list( LANGUAGES.values())):
var.insertItem( count, item )
count += 1
else:
pass

# Displays the list of languages for the source language.
def
source_language(self):
self.fill_list_widget( self.source )
self.source.show()
# When chosen language is clicked.
self
.source.clicked.connect( self.src_on_click)

# Displays the list of languages for the destination language.
def
destin_language(self):
self.fill_list_widget( self.destin)
self.destin.show()
# When chosen language is clicked.
self
.destin.clicked.connect( self.dst_on_click )

# Displays the chosen source language.
def
src_on_click(self):
self.source.hide()
choice = self.source.currentItem()
self.src.setText(choice.text())
self.src.show()

# Displays the chosen destination language.
def
dst_on_click(self):
self.destin.hide()
choice = self.destin.currentItem()
self.dst.setText(choice.text())
self.dst.show()
self.translated.show()
self.cleard.show()
self.src_box.show()
self.src_box.setFocus()

# Start over.
def
clear(self):
self.destin.hide()
self.source.hide()
self.dst_box.hide()
self.src_box.hide()
self.dst.hide()
self.src.hide()
self.cleard.hide()
self.translated.hide()

# Exit the program.
def
mousePressEvent(self):
self.comm.closeApp.emit()

# Function that translates the text.
def
translate(self):
translated = self.translator.translate(self.src_box.toPlainText(), dest=self.dst.text())
self.dst_box.setText(translated.text )
self.src_box.hide()
self.dst_box.show()


if __name__ == '__main__':
app = QApplication([])
ex = LanguageTranslator()
ex.show()
sys.exit(app.exec())

--

--

ZennDogg

Retired military, Retired US Postal Service, Defender of the US Constitution from all enemies, foreign and domestic, Self-taught in python