docs, modifying cli to use language packs
This commit is contained in:
parent
45f4d6e401
commit
68128b5dc6
112
README.md
112
README.md
|
@ -1,11 +1,84 @@
|
||||||
# D&D Language Generator
|
# D&D Name and Language Generator
|
||||||
|
|
||||||
This package is a fantasy language generator. By defining a number of characteristics about your imagined language -- the graphemes, their relative frequency distributions, the construction of syllables, and so on -- you can generate random but internally consistent gibberish that feels distinct, evocative, and appropriate to your setting.
|
This package is a fantasy language generator. By defining a number of characteristics about your imagined language -- the graphemes, their relative frequency distributions, the construction of syllables, and so on -- you can generate random but internally consistent gibberish that feels distinct, evocative, and appropriate to your setting.
|
||||||
|
|
||||||
## Quick Start
|
## Usage
|
||||||
|
|
||||||
|
The `fanlang` command-line utility supports three commands:
|
||||||
|
|
||||||
|
* **names**: generate names
|
||||||
|
* **text**: generate a paragraph of text
|
||||||
|
* **list**: list the supported language in the current language pack
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
|
||||||
```
|
```
|
||||||
>>> from language imported supported_languages
|
% fanlang --language=dwarvish names --count 5
|
||||||
|
Tiny Châ Pothesadottyr
|
||||||
|
Khâkhu Zhûdothir
|
||||||
|
Quiet Ke Vêdothir
|
||||||
|
Cû Tozhon
|
||||||
|
Big Pâ Thadottyr
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
% fanlang --language=dwarvish text
|
||||||
|
Cû ne do tho khâ tasha, vê wûva lû, ku phu thâ thê, tûko kê, pevo kâ têtetv zha
|
||||||
|
pataso keks khate? Fâ zhû shû yf pho pa me. Dupha dê thê khâ! Shikm tu! Cê
|
||||||
|
sâdêto. Dê yo nâ topho, my sû pida phe, vi phûtw châcho, po sotê?
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
% fanlang list
|
||||||
|
Abyssal
|
||||||
|
Celestial
|
||||||
|
Common
|
||||||
|
Draconic
|
||||||
|
Dwarvish
|
||||||
|
Elvish
|
||||||
|
Gnomish
|
||||||
|
Halfling
|
||||||
|
Infernal
|
||||||
|
Lizardfolk
|
||||||
|
Orcish
|
||||||
|
Undercommon
|
||||||
|
```
|
||||||
|
|
||||||
|
## Language Packs
|
||||||
|
|
||||||
|
A *Language Pack* is a python package that defines one or more language modules. The default language pack includes a number of D&D languages with rules built according to the conventions established by my D&D group over several years of play in our homebrew setting.
|
||||||
|
|
||||||
|
The default language pack is [language.languages](language/languages/); each submodule contains a README that describes the basic characteristics of the language, along with examples.
|
||||||
|
|
||||||
|
### Using Your Own Language Pack
|
||||||
|
|
||||||
|
You can override `fanlang`'s default language pack by specifying the `FANLANG_LANGUAGE_PACK` environment variable:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Create your ancient_elvish module in campaign/language_pack/ancient_elvish
|
||||||
|
% FANLANG_LANGUAGE_PACK=campaign.language_pack fanlang list
|
||||||
|
Ancient Elvish
|
||||||
|
``
|
||||||
|
|
||||||
|
### Setting the Default Language
|
||||||
|
|
||||||
|
'common' is the default language module. You can override this by setting the `FANLANG_DEFAULT_LANGUAGE` environment variable:
|
||||||
|
|
||||||
|
```
|
||||||
|
% FANLANG_DEFAULT_LANGUAGE=gnomish fanlang names --count=1
|
||||||
|
Jey Lea
|
||||||
|
```
|
||||||
|
|
||||||
|
You can read about creating custom language packs below.
|
||||||
|
|
||||||
|
|
||||||
|
## Library Quick Start
|
||||||
|
|
||||||
|
You can load all supported languages in a language pack using `language.load_langauge_pack()`:
|
||||||
|
|
||||||
|
```
|
||||||
|
>>> import language
|
||||||
|
>>> language_pack, supported_languages = language.load_language_pack()
|
||||||
>>> common = supported_languages['common']
|
>>> common = supported_languages['common']
|
||||||
>>> common.word(2)
|
>>> common.word(2)
|
||||||
['apsoo', 'nirtoet']
|
['apsoo', 'nirtoet']
|
||||||
|
@ -21,23 +94,38 @@ Proitsiiiy be itkif eesof detytaen. Ojaot tyskuaz apsoo nirtoet prenao.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Supported Languages
|
You can also load individual languages directly:
|
||||||
|
|
||||||
A number of D&D languages are defined, with rules built according to the
|
```
|
||||||
conventions established by my D&D group over several years of play in our
|
>>> from language.languages import common
|
||||||
homebrew setting. You can find all supported languages [in the languages
|
>>> common.Language.word(2)
|
||||||
submodule](language/languages/); each submodule contains a README that
|
['apsoo', 'nirtoet']
|
||||||
describes the basic characteristics of the language, along with examples.
|
>>> str(common.Name)
|
||||||
|
"Quiet" Gushi Murk Lirpusome
|
||||||
|
```
|
||||||
|
|
||||||
## Defining a Language
|
## Defining a New Language Pack
|
||||||
|
|
||||||
### Layout
|
Language packs are python packages with the following structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
language_pack:
|
||||||
|
__init__.py
|
||||||
|
language_name:
|
||||||
|
__init__.py
|
||||||
|
base.py
|
||||||
|
names.py
|
||||||
|
rules.py
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Languge Modules
|
||||||
|
|
||||||
A language consists of several submodules:
|
A language consists of several submodules:
|
||||||
|
|
||||||
* `base.py`, which contains grapheme definitions and the `Language` subclasses;
|
* `base.py`, which contains grapheme definitions and the `Language` subclasses;
|
||||||
* `names.py`, which defines the `NameGenerator` subclasses; and
|
* `names.py`, which defines the `NameGenerator` subclasses; and
|
||||||
* `rules.py`, which is optional but defines the rules all words in the language must follow.
|
* `rules.py`, which is optional, and defines the rules all words in the language must follow.
|
||||||
|
|
||||||
|
|
||||||
### Language Construction
|
### Language Construction
|
||||||
|
|
|
@ -1 +1,26 @@
|
||||||
from .languages import supported_languages
|
import importlib
|
||||||
|
import os
|
||||||
|
import pkgutil
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from types import ModuleType
|
||||||
|
|
||||||
|
language_pack = None
|
||||||
|
supported_languages = None
|
||||||
|
|
||||||
|
|
||||||
|
def _import_submodules(module):
|
||||||
|
pkgs = pkgutil.iter_modules(module.__path__)
|
||||||
|
for loader, module_name, is_pkg in pkgs:
|
||||||
|
yield importlib.import_module(f"{module.__name__}.{module_name}")
|
||||||
|
|
||||||
|
|
||||||
|
def load_language_pack(module_name: str = "") -> ModuleType:
|
||||||
|
if not module_name:
|
||||||
|
module_name = os.getenv("FANLANG_LANGUAGE_PACK", "language.languages")
|
||||||
|
language_pack = importlib.import_module(module_name)
|
||||||
|
_import_submodules(language_pack)
|
||||||
|
supported_languages = dict(
|
||||||
|
(module.__name__.split(".")[-1], module) for module in list(_import_submodules(sys.modules[module_name]))
|
||||||
|
)
|
||||||
|
return language_pack, supported_languages
|
||||||
|
|
|
@ -1,20 +1,23 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import random
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
|
|
||||||
|
import language
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
from rich.logging import RichHandler
|
from rich.logging import RichHandler
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.markdown import Markdown
|
from rich.markdown import Markdown
|
||||||
|
|
||||||
from language import supported_languages
|
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
|
|
||||||
app_state = {}
|
app_state = {}
|
||||||
|
|
||||||
|
default_language = os.environ.get("FANLANG_DEFAULT_LANGUAGE", "common")
|
||||||
|
language_pack, supported_languages = language.load_language_pack()
|
||||||
|
|
||||||
Supported = Enum("Supported", ((k, k) for k in supported_languages.keys()))
|
Supported = Enum("Supported", ((k, k) for k in supported_languages.keys()))
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,21 +37,47 @@ def print_sample(lang: str, module: ModuleType) -> None:
|
||||||
|
|
||||||
@app.callback()
|
@app.callback()
|
||||||
def main(
|
def main(
|
||||||
language: Supported = typer.Option("common", help="The language to use."),
|
language: Supported = typer.Option(
|
||||||
|
default=default_language,
|
||||||
|
help="The language to use."
|
||||||
|
),
|
||||||
):
|
):
|
||||||
app_state["language"] = supported_languages[language.name]
|
app_state["language"] = supported_languages[language.name]
|
||||||
|
|
||||||
debug = os.getenv("DEBUG", None)
|
debug = os.getenv("FANLANG_DEBUG", None)
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
format="%(message)s",
|
format="%(name)s %(message)s",
|
||||||
level=logging.DEBUG if debug else logging.INFO,
|
level=logging.DEBUG if debug else logging.INFO,
|
||||||
handlers=[RichHandler(rich_tracebacks=True, tracebacks_suppress=[typer])],
|
handlers=[RichHandler(rich_tracebacks=True, tracebacks_suppress=[typer])],
|
||||||
)
|
)
|
||||||
|
logging.getLogger('markdown_it').setLevel(logging.ERROR)
|
||||||
|
logging.debug(f"Loaded language pack {language_pack}.")
|
||||||
|
logging.debug(f"Default language: {default_language}.")
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def words(count: int = typer.Option(50, help="The number of words to generate.")):
|
def text(count: int = typer.Option(50, help="The number of words to generate.")):
|
||||||
print(" ".join(list(app_state["language"].Language.word(count))))
|
|
||||||
|
phrases = []
|
||||||
|
phrase = []
|
||||||
|
for word in app_state["language"].Language.word(count):
|
||||||
|
phrase.append(str(word))
|
||||||
|
if len(phrase) >= random.randint(1, 12):
|
||||||
|
phrases.append(' '.join(phrase))
|
||||||
|
phrase = []
|
||||||
|
if phrase:
|
||||||
|
phrases.append(' '.join(phrase))
|
||||||
|
|
||||||
|
paragraph = phrases[0].capitalize()
|
||||||
|
for phrase in phrases[1:]:
|
||||||
|
if random.choice([0, 0, 1]):
|
||||||
|
paragraph = paragraph + random.choice('?!.') + ' ' + phrase.capitalize()
|
||||||
|
else:
|
||||||
|
paragraph = paragraph + ', ' + phrase
|
||||||
|
paragraph = paragraph + random.choice('?!.')
|
||||||
|
|
||||||
|
console = Console(width=80)
|
||||||
|
console.print(paragraph)
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import importlib
|
|
||||||
import pkgutil
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def import_submodules(module):
|
|
||||||
pkgs = pkgutil.iter_modules(module.__path__)
|
|
||||||
for loader, module_name, is_pkg in pkgs:
|
|
||||||
yield importlib.import_module(f"{module.__name__}.{module_name}")
|
|
||||||
|
|
||||||
|
|
||||||
supported_languages = dict(
|
|
||||||
(module.__name__.split(".")[-1], module) for module in list(import_submodules(sys.modules[__name__]))
|
|
||||||
)
|
|
Loading…
Reference in New Issue
Block a user