Language and whisper obfuscation system

Evennia contrib - Griatch 2015

This module is intented to be used with an emoting system (such as contrib/rpsystem.py). It offers the ability to obfuscate spoken words in the game in various ways:

  • Language: The language functionality defines a pseudo-language map

    to any number of languages. The string will be obfuscated depending on a scaling that (most likely) will be input as a weighted average of the language skill of the speaker and listener.

  • Whisper: The whisper functionality will gradually “fade out” a

    whisper along as scale 0-1, where the fading is based on gradually removing sections of the whisper that is (supposedly) easier to overhear (for example “s” sounds tend to be audible even when no other meaning can be determined).


from evennia.contrib import rplanguage

# need to be done once, here we create the "default" lang

say = "This is me talking."
whisper = "This is me whispering.

print rplanguage.obfuscate_language(say, level=0.0)
<<< "This is me talking."
print rplanguage.obfuscate_language(say, level=0.5)
<<< "This is me byngyry."
print rplanguage.obfuscate_language(say, level=1.0)
<<< "Daly ly sy byngyry."

result = rplanguage.obfuscate_whisper(whisper, level=0.0)
<<< "This is me whispering"
result = rplanguage.obfuscate_whisper(whisper, level=0.2)
<<< "This is m- whisp-ring"
result = rplanguage.obfuscate_whisper(whisper, level=0.5)
<<< "---s -s -- ---s------"
result = rplanguage.obfuscate_whisper(whisper, level=0.7)
<<< "---- -- -- ----------"
result = rplanguage.obfuscate_whisper(whisper, level=1.0)
<<< "..."

Custom languages

To set up new languages, you need to run add_language() helper function in this module. The arguments of this function (see below) are used to store the new language in the database (in the LanguageHandler, which is a type of Script).

If you want to remember the language definitions, you could put them all in a module along with the add_language call as a quick way to rebuild the language on a db reset:

# a stand-alone module somewhere under mygame. Just import this
# once to automatically add the language!

from evennia.contrib.rpg.rpsystem import rplanguage
grammar = (...)
vowels = "eaouy"
# etc

rplanguage.add_language(grammar=grammar, vowels=vowels, ...)

The variables of add_language allows you to customize the “feel” of the semi-random language you are creating. Especially the word_length_variance helps vary the length of translated words compared to the original. You can also add your own dictionary and “fix” random words for a list of input words.


Below is an example module creating “elvish”, using “rounder” vowels and sounds:

# vowel/consonant grammar possibilities
grammar = ("v vv vvc vcc vvcc cvvc vccv vvccv vcvccv vcvcvcc vvccvvcc "
           "vcvvccvvc cvcvvcvvcc vcvcvvccvcvv")

# all not in this group is considered a consonant
vowels = "eaoiuy"

# you need a representative of all of the minimal grammars here, so if a
# grammar v exists, there must be atleast one phoneme available with only
# one vowel in it
phonemes = ("oi oh ee ae aa eh ah ao aw ay er ey ow ia ih iy "
            "oy ua uh uw y p b t d f v t dh s z sh zh ch jh k "
            "ng g m n l r w")

# how much the translation varies in length compared to the original. 0 is
# smallest, higher values give ever bigger randomness (including removing
# short words entirely)
word_length_variance = 1

# if a proper noun (word starting with capitalized letter) should be
# translated or not. If not (default) it means e.g. names will remain
# unchanged across languages.
noun_translate = False

# all proper nouns (words starting with a capital letter not at the beginning
# of a sentence) can have either a postfix or -prefix added at all times
noun_postfix = "'la"

# words in dict will always be translated this way. The 'auto_translations'
# is instead a list or filename to file with words to use to help build a
# bigger dictionary by creating random translations of each word in the
# list *once* and saving the result for subsequent use.
manual_translations = {"the":"y'e", "we":"uyi", "she":"semi", "he":"emi",
                      "you": "do", 'me':'mi','i':'me', 'be':"hy'e", 'and':'y'}

rplanguage.add_language(key="elvish", phonemes=phonemes, grammar=grammar,
                         noun_postfix=noun_postfix, vowels=vowels,

This will produce a decicively more “rounded” and “soft” language than the default one. The few manual_translations also make sure to make it at least look superficially “reasonable”.

The auto_translations keyword is useful, this accepts either a list or a path to a file of words (one per line) to automatically create fixed translations for according to the grammatical rules. This allows to quickly build a large corpus of translated words that never change (if this is desired).

exception evennia.contrib.rpg.rpsystem.rplanguage.LanguageError[source]

Bases: RuntimeError

exception evennia.contrib.rpg.rpsystem.rplanguage.LanguageExistsError[source]

Bases: evennia.contrib.rpg.rpsystem.rplanguage.LanguageError

class evennia.contrib.rpg.rpsystem.rplanguage.LanguageHandler(*args, **kwargs)[source]

Bases: evennia.scripts.scripts.DefaultScript

This is a storage class that should usually not be created on its own. It’s automatically created by a call to obfuscate_language or add_language below.

Languages are implemented as a “logical” pseudo- consistent language algorith here. The idea is that a language is built up from phonemes. These are joined together according to a “grammar” of possible phoneme- combinations and allowed characters. It may sound simplistic, but this allows to easily make “similar-sounding” languages. One can also custom-define a dictionary of some common words to give further consistency. Optionally, the system also allows an input list of common words to be loaded and given random translations. These will be stored to disk and will thus not change. This gives a decent “stability” of the language but if the goal is to obfuscate, this may allow players to eventually learn to understand the gist of a sentence even if their characters can not. Any number of languages can be created this way.

This nonsense language will partially replace the actual spoken language when so desired (usually because the speaker/listener don’t know the language well enough).


Called when script is first started

add(key='default', phonemes='ea oh ae aa eh ah ao aw ai er ey ow ia ih iy oy ua uh uw a e i u y p b t d f v t dh s z sh zh ch jh k ng g m n l r w', grammar='v cv vc cvv vcc vcv cvcc vccv cvccv cvcvcc cvccvcv vccvccvc cvcvccvv cvcvcvcvv', word_length_variance=0, noun_translate=False, noun_prefix='', noun_postfix='', vowels='eaoiuy', manual_translations=None, auto_translations=None, force=False)[source]

Add a new language. Note that you generally only need to do this once per language and that adding an existing language will re-initialize all the random components to new permanent values.

  • key (str, optional) – The name of the language. This will be used as an identifier for the language so it should be short and unique.

  • phonemes (str, optional) – Space-separated string of all allowed phonemes in this language. If either of the base phonemes (c, v, cc, vv) are present in the grammar, the phoneme list must at least include one example of each.

  • grammar (str) – All allowed consonant (c) and vowel (v) combinations allowed to build up words. Grammars are broken into the base phonemes (c, v, cc, vv) prioritizing the longer bases. So cvv would be a the c + vv (would allow for a word like ‘die’ whereas cvcvccc would be c+v+c+v+cc+c (a word like ‘galosch’).

  • word_length_variance (real) – The variation of length of words. 0 means a minimal variance, higher variance may mean words have wildly varying length; this strongly affects how the language “looks”.

  • noun_translate (bool, optional) – If a proper noun should be translated or not. By default they will not, allowing for e.g. the names of characters to be understandable. A ‘noun’ is identified as a capitalized word not at the start of a sentence. This simple metric means that names starting a sentence always will be translated (- but hey, maybe the fantasy language just never uses a noun at the beginning of sentences, who knows?)

  • noun_prefix (str, optional) – A prefix to go before every noun in this language (if any).

  • noun_postfix (str, optuonal) – A postfix to go after every noun in this language (if any, usually best to avoid combining with noun_prefix or language becomes very wordy).

  • vowels (str, optional) – Every vowel allowed in this language.

  • manual_translations (dict, optional) – This allows for custom-setting certain words in the language to mean the same thing. It is on the form {real_word: fictional_word}, for example {“the”, “y’e”} .

  • auto_translations (str or list, optional) – These are lists words that should be auto-translated with a random, but fixed, translation. If a path to a file, this file should contain a list of words to produce translations for, one word per line. If a list, the list’s elements should be the words to translate. The manual_translations will always override overlapping translations created automatically.

  • force (bool, optional) – Unless true, will not allow the addition of a language that is already created.


LanguageExistsError – Raised if trying to adding a language with a key that already exists, without force being set.


The word_file is for example a word-frequency list for the N most common words in the host language. The translations will be random, but will be stored persistently to always be the same. This allows for building a quick, decently-sounding fictive language that tend to produce the same “translation” (mostly) with the same input sentence.

translate(text, level=0.0, language='default')[source]

Translate the text according to the given level.

  • text (str) – The text to translate

  • level (real) – Value between 0.0 and 1.0, where 0.0 means no obfuscation (text returned unchanged) and 1.0 means full conversion of every word. The closer to 1, the shorter words will be translated.

  • language (str) – The language key identifier.


text (str) – A translated string.

exception DoesNotExist

Bases: evennia.scripts.scripts.DefaultScript.DoesNotExist

exception MultipleObjectsReturned

Bases: evennia.scripts.scripts.DefaultScript.MultipleObjectsReturned

path = 'evennia.contrib.rpg.rpsystem.rplanguage.LanguageHandler'
typename = 'LanguageHandler'
evennia.contrib.rpg.rpsystem.rplanguage.obfuscate_language(text, level=0.0, language='default')[source]

Main access method for the language parser.

  • text (str) – Text to obfuscate.

  • level (real, optional) – A value from 0.0-1.0 determining the level of obfuscation where 0 means no obfuscation (string returned unchanged) and 1.0 means the entire string is obfuscated.

  • language (str, optional) – The identifier of a language the system understands.


translated (str) – The translated text.


Access function to creating a new language. See the docstring of LanguageHandler.add for list of keyword arguments.


Returns all available language keys.


languages (list) – List of key strings of all available languages.

evennia.contrib.rpg.rpsystem.rplanguage.obfuscate_whisper(whisper, level=0.0)[source]

Obfuscate whisper depending on a pre-calculated level (that may depend on distance, listening skill etc)

  • whisper (str) – The whisper string to obscure. The entire string will be considered in the obscuration.

  • level (real, optional) – This is a value 0-1, where 0 means not obscured (whisper returned unchanged) and 1 means fully obscured.