3
\$\begingroup\$

I wrote a python script that converts English to Entean language from an anime called "The Devil is a part timer".

This is the second version of the script which was first posted here Translating from English to Entean. This version is compatible with both Python 2 and 3.This version uses the str.maketrans method which is faster and more Pythonic.

In the anime, the language in Ente Isla is called Entean, which is basically most of the consonants in the English alphabet switched around. Vowels and the consonants 'L', 'N', 'Q' keep their original position. ABCDEFGHIJKLMNOPQRSTUVWXYZ is changed to AZYXEWVTISRLPNOMQKJHUGFDCB. For example, 'human' is 'tupan', and 'world' becomes 'foklx'.

import sys

PY2 = sys.version_info[0] == 2 #Returns True if Python version is 2 

if PY2:
    from string import maketrans
else:
    maketrans = str.maketrans

FULL_ENGLISH_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
FULL_ENTEAN_ALPHABET =  "AZYXEWVTISRLPNOMQKJHUGFDCB"

FULL_ENGLISH_ALPHABET += FULL_ENGLISH_ALPHABET.lower()
FULL_ENTEAN_ALPHABET += FULL_ENTEAN_ALPHABET.lower()

tran_tab = maketrans(FULL_ENGLISH_ALPHABET, FULL_ENTEAN_ALPHABET)


def to_entean(text):
    return(text.translate(tran_tab))


def main(text=None, with_output=True):

    if text is None:
        if PY2:
            text = raw_input("Enter English text: ")
        else:
            text = input("Enter English text: ")

    if with_output:    
        print(text)

    entean_text = to_entean(text)

    if with_output:
        print(entean_text)


if __name__ == "__main__":
    main()

If you interested the updated code can be found at https://github.com/Logmytech/English-to-Entean

\$\endgroup\$
2
\$\begingroup\$

Nothing much to say here, the code is clean, read much easier than the first version and adheres to standard practices.

I personally would have used string.ascii_letters instead of building FULL_ENGLISH_ALPHABET.

And it might be neater to use only one if with_output, and since we’re at it, why not return the computed value from main as well, so that if with_output is False, the function still do something:

def main(text=None, with_output=True):
    if text is None:
        if PY2:
            text = raw_input("Enter English text: ")
        else:
            text = input("Enter English text: ")

    entean_text = to_entean(text)
    if with_output:    
        print(text)
        print(entean_text)

    return entean_text
\$\endgroup\$
  • \$\begingroup\$ If you don't want the with_output, you simply call to_entean directly... \$\endgroup\$ – holroy Nov 27 '15 at 12:13
  • \$\begingroup\$ The with_output is a remniscent of my code when making the different version of previous version as equal as possible, and an easy way to both do live testing and execution time tests. \$\endgroup\$ – holroy Nov 27 '15 at 12:14
5
\$\begingroup\$

As you've taken most of our advice to heart, there isn't much to complain on stylewise, but a few pointers from my personal opinion:

  • If possible, avoid doing function call for constants, and avoid redefining constants – You set FULL_ENGLISH_ALPHABET = FULL_ENGLISH_ALPHABET + FULL_ENGLISH_ALPHABET.lower(). Both the redefining of a constant, and doing the lower() on previous version could and should be avoided in my opinion. Just set it to the correct version directly, you know in advance what it is going to be.
  • Write out variable names – Type out in full the tran_tab variable, and since you're not going to change it, uppercase it, i.e make it ENGLISH_TO_ENTEAN which also encompasses the use instead of just translation_table which is kind of meaningless.
  • Add docstrings to functions and module – You could/should add documentation to your module, to help understand what this module does

Extend to translate both ways

A natural extension now is to extend it so that it can translate both ways. In addition to the to_english(text) function, I would also make a translate_to(language, text) function so that you can make functions which can choose which way you want to translate.

Look into unit testing and/or doctest

Another possible extension is to add testing of your methods, to ensure proper translation and handling of different cases. Loads of alternatives exists, but I'm going to point you towards unittest and doctest. Both of these are included in vanilla Python. The former can be useful for slightly more complex cases, but I've opted for doctest in my review, as it has a nice side effect of displaying with the docstrings how to use the different functions.

Doctest can be invoked on your module in a variety of ways, but the two versions I've been using the most are either to call it direcly from the command line using python -m doctest my_module.py or through calling a simple do_doctest() function from the main code. The former doesn't require anything besides the actual docstrings, and if you use python -m doctest -v my_module.py you get to see every test it performs (and not only failing tests) and the summary.

Try it out with code below, and change some of the stuff related to >>> lines, and response on following line.

Make main() useful

I know I was the one proposing the main() function you're using, but now that it is maturing, I would make it into a loop asking for which language to translate to and text to translate. This way you'll get a meaning full script to run, in addition to a meaningful module to use.

Note how I used a little trick to 'alias' input = raw_input if in Python 2, so I wouldn't need to do the if PY2 all over my script. Consider also looking into an extra module handling Python 2 and Python 3 like the six module.

Revamped code

Here is my version implementing all of the above suggestions:

"""
In the anime, "The Devil is a part timer", the language in Ente Isla is
called Entean, which is basically most of the consonants in the English
alphabet switched around. Vowels and the consonants 'L', 'N', 'Q' keep 
their original position, whilst the rest is simply reversed.

The full alphabet then is that each letter of ABCDEFGHIJKLMNOPQRSTUVWXYZ 
is changed to AZYXEWVTISRLPNOMQKJHUGFDCB. For example, 'human' is 'tupan', 
and 'world' becomes 'foklx'. Other characters are kept as is.

Some typical usage situation offered by module:

    >>> to_entean("Text, to be translated, into to Entean!")
    'Hedh, ho ze hkanjlahex, inho ho Enhean!'

    >>> to_english("Tupan foklx!")
    'Human world!'

    >>> translate_to('entean', 'Kind of cool')
    'Rinx ow yool'
"""

import sys

# Set to True if Python major version is 2
PY2 = sys.version_info[0] == 2 

if PY2:
    from string import maketrans    

    # Alias input to simplify input routines
    input = raw_input
else:
    maketrans = str.maketrans


ENGLISH_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
ENTEAN_ALPHABET = "AZYXEWVTISRLPNOMQKJHUGFDCBazyxewvtisrlpnomqkjhugfdcb"

ENGLISH_TO_ENTEAN = maketrans(ENGLISH_ALPHABET, ENTEAN_ALPHABET)
ENTEAN_TO_ENGLISH = maketrans(ENTEAN_ALPHABET, ENGLISH_ALPHABET)


def to_entean(text):
    """Return text translated from English to Entean.

    >>> to_entean("Some random text to translate, with punctuation chars!")
    'Jope kanxop hedh ho hkanjlahe, fiht munyhuahion ytakj!'

    >>> to_entean(ENGLISH_ALPHABET) == ENTEAN_ALPHABET
    True
    """
    return text.translate(ENGLISH_TO_ENTEAN)


def to_english(text):
    """Return text translated from English to Entean.

    >>> to_english("Noh jo eajc ho unxekjhanx, kivth?")
    'Not so easy to understand, right?'

    >>> to_english(ENTEAN_ALPHABET) == ENGLISH_ALPHABET
    True
    """
    return text.translate(ENTEAN_TO_ENGLISH)


ALL_TRANSLATIONS = {
    'entean' : to_entean,
    'english' : to_english
    }

def translate_to(language, text):
    """Return translated text according to mode.

    >>> translate_to('english', 'Tupan foklx')
    'Human world'

    >>> translate_to('entean', 'Human world')
    'Tupan foklx'

    >>> translate_to('norwegian', 'This will fail!')
    Traceback (most recent call last):
    ValueError: Unknown translation language.
    """

    translate_function = ALL_TRANSLATIONS.get(language, None)

    if translate_function:
        return translate_function(text)
    else:
        raise ValueError("Unknown translation language.")


def do_doctest():
    """Utility method to run all doctests from within script."""

    import doctest
    doctest.testmod()


def main():
    """Loop asking for language and text to translate."""

    print(' Translation utility '.center(72, '*'))
    print('\nLegal languages: {}'.format(', '.join(ALL_TRANSLATIONS)))

    while True:
        to_language = input("Language to translate to (or 'quit'): ").lower().strip()

        if to_language == 'quit':
            break

        text = input("Enter text to translate: ")

        print('Translated text: {}'.format(translate_to(to_language, text)))


if __name__ == "__main__":
    do_doctest()
    main()
\$\endgroup\$
1
\$\begingroup\$

Pushing further on @Mathias Ettinger remark about building FULL_ENGLISH_ALPHABET, you could construct both alphabets in the program.

For instance into a function generate_trans_table(exceptions):

  • generate the full english alphabet
  • remove the exception letters, which for entean language is lnqaeiou
  • reverse the string and store it into a second variable, which will be the second language
  • append the exception letters to both strings, so that they are at the same position in the english and entean alphabets
  • append the uppercased strings to themselves
  • generate and return the translation table, or just return the two strings to let the program choose the direction of the translation. Or even return a tuple of translation table to represent to and from translations

This way you can easily generate variants of the language :-)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.