Gloser
The simplest way to add internationalization to your Python app
from gloser import Gloser
g = Gloser("translations.yaml")
print(g.t("welcome", name="Alice", locale="es")) # → "¡Bienvenida, Alice!"
72 languages • Zero config • Simple yet powerful
Why Gloser?
- ✨ 5-minute setup — No complex configuration. Load a YAML file and start translating.
- 🌍 72 languages out of the box — Built-in locale defaults with pluralization rules, date/time formatting, and number formatting for 72 languages.
- 🎯 Developer-friendly — Natural Python API, clean YAML syntax, full type hints, and intuitive conventions.
- 🚀 Modern patterns — Built for web apps with thread-safe, stateless per-request locale handling.
Quick Start
1. Install
pip install gloser
2. Create your translations (translations.yaml)
welcome:
en: "Welcome, {name}!"
es: "¡Bienvenida, {name}!"
fr: "Bienvenue, {name}!"
items:
en:
one: "You have {count} item"
other: "You have {count} items"
es:
one: "Tienes {count} artículo"
other: "Tienes {count} artículos"
fr:
one: "Vous avez {count} article"
other: "Vous avez {count} articles"
3. Use in your code
from gloser import Gloser
g = Gloser("translations.yaml")
# Simple translation with t() shorthand
print(g.t("welcome", name="Alice", locale="en"))
# → "Welcome, Alice!"
# Automatic pluralization
print(g.t("items", count=1, locale="fr"))
# → "Vous avez 1 article"
print(g.t("items", count=5, locale="es"))
# → "Tienes 5 artículos"
That's it! You're internationalized.
Feature Showcase
Flexible YAML Structure
Gloser supports multiple YAML formats — use whatever works best for you:
Option 1: Single file with all translations (shown above)
welcome:
en: "Welcome!"
es: "¡Bienvenida!"
Option 2: One file per locale
# en.yaml
.locale: en
welcome: "Welcome!"
goodbye: "Goodbye!"
# es.yaml
.locale: es
welcome: "¡Bienvenida!"
goodbye: "¡Adiós!"
Option 3: Multi-document YAML
---
en:
welcome: "Welcome!"
---
es:
welcome: "¡Bienvenida!"
Mix and match: Load multiple files with different formats — Gloser merges them intelligently!
g = Gloser()
g.load_yaml_files("common.yaml", "en.yaml", "es.yaml")
🔢 Smart Pluralization
Gloser handles pluralization automatically using built-in rules for 72 languages, including complex ones like Polish with three forms:
from gloser import Gloser
g = Gloser("translations.yaml") # Auto-loads locale defaults on demand
# English: simple (one/other)
g.t("files", count=1, locale="en") # → "You have 1 file"
g.t("files", count=5, locale="en") # → "You have 5 files"
# Polish: complex (one/few/many)
g.t("files", count=1, locale="pl") # → "Masz 1 plik"
g.t("files", count=2, locale="pl") # → "Masz 2 pliki"
g.t("files", count=5, locale="pl") # → "Masz 5 plików"
g.t("files", count=22, locale="pl") # → "Masz 22 pliki"
No configuration needed — rules for Arabic, Russian, Slovenian, and 69 other languages are built-in!
📅 Intelligent Date/Time Formatting
Locale-aware date formatting with natural, localized output.
from datetime import date
from gloser import Gloser
g = Gloser() # Auto-loads date formats on demand
# Different locales, different formats
g.format_date(date(2025, 1, 9), "long", locale="en")
# → "January 9, 2025"
g.format_date(date(2025, 1, 9), "long", locale="de")
# → "9. Januar 2025"
g.format_date(date(2025, 1, 9), "long", locale="fr")
# → "9 janvier 2025"
g.format_date(date(2025, 1, 9), "long", locale="es")
# → "9 de enero de 2025"
You can also use date formatting within translations:
event_date:
en: "Event on {date:long}"
de: "Veranstaltung am {date:long}"
g.t("event_date", date=date(2025, 1, 9), locale="en")
# → "Event on January 9, 2025"
Built-in format styles: short, medium, long, full — all configured per locale.
Supported variables: {YYYY}, {MM}, {DD}, {monthname}, {mnm}, {dayname}, {dnm}, {hh}, {mm}, {ss}, {AMPM}, and more.
💰 Number Formatting
Automatic thousands and decimal separators for all locales.
from gloser import Gloser
g = Gloser() # Uses built-in number formats for all locales
# Different locales use different separators
g.t("Price: {price:,.2f}", price=1234.56, locale="en")
# → "Price: 1,234.56"
g.t("Price: {price:,.2f}", price=1234.56, locale="de")
# → "Price: 1.234,56" (Germany uses . for thousands, , for decimal)
g.t("Price: {price:,.2f}", price=1234.56, locale="fr")
# → "Price: 1 234,56" (France uses space for thousands, , for decimal)
📋 Array-Style Lookups
Perfect for enums, weekdays, months, and fixed lists. In your YAML:
planet:
en:
1: Mercury
2: Venus
3: Earth
4: Mars
es:
1: Mercurio
2: Venus
3: Tierra
4: Marte
Then use it in code:
g = Gloser("translations.yaml")
g.t("planet", 3, locale="en") # → "Earth"
g.t("planet", 3, locale="es") # → "Tierra"
🌐 Web-Ready: Stateless Locale Handling
Perfect for Flask, FastAPI, Django, and other web frameworks.
# Initialize once at app startup
gloser = Gloser("translations.yaml")
# Get locale-specific translator per request
@app.route("/")
def index():
locale = request.headers.get("Accept-Language", "en")[:2]
t = gloser[locale] # Thread-safe, stateless
return t("welcome", name=current_user.name)
🥇 Ordinals
"1st, 2nd, 3rd" formatting with built-in rules for English, French, Spanish, and more.
from gloser import Gloser
g = Gloser("translations.yaml") # Auto-loads ordinal rules on demand
g.t("place", position=1, locale="en") # → "1st place"
g.t("place", position=2, locale="en") # → "2nd place"
g.t("place", position=3, locale="en") # → "3rd place"
g.t("place", position=11, locale="en") # → "11th place"
g.t("place", position=22, locale="en") # → "22nd place"
🛡️ Graceful Fallback
Missing translations? Gloser falls back to your default locale automatically.
# translations.yaml
hello:
en: "Hello!"
es: "¡Hola!"
welcome:
en: "Welcome!"
# Spanish translation missing
from gloser import Gloser
g = Gloser("translations.yaml", default_locale="en")
# Spanish translation exists
g.t("hello", locale="es") # → "¡Hola!"
# Falls back to English when Spanish is missing
g.t("welcome", locale="es") # → "Welcome!"
72 Languages Ready to Go
Gloser includes built-in locale defaults for 72 languages with pluralization rules, date/time formatting, number formatting, month names, and weekday names:
View all supported languages
| af - Afrikaans | am - Amharic | ar - Arabic | az - Azerbaijani | be - Belarusian |
| bg - Bulgarian | bn - Bengali | bs - Bosnian | ca - Catalan | cs - Czech |
| da - Danish | de - German | dz - Dzongkha | el - Greek | en - English |
| es - Spanish | et - Estonian | fa - Persian | fi - Finnish | fr - French |
| ha - Hausa | he - Hebrew | hi - Hindi | hr - Croatian | hu - Hungarian |
| hy - Armenian | id - Indonesian | ig - Igbo | is - Icelandic | it - Italian |
| ja - Japanese | ka - Georgian | kk - Kazakh | km - Khmer | ko - Korean |
| ku - Kurdish | ky - Kyrgyz | lo - Lao | lt - Lithuanian | lv - Latvian |
| mn - Mongolian | ms - Malay | my - Burmese | nb - Norwegian Bokmål | ne - Nepali |
| nl - Dutch | nn - Norwegian Nynorsk | no - Norwegian | pl - Polish | pt - Portuguese |
| ro - Romanian | ru - Russian | si - Sinhala | sk - Slovak | sl - Slovenian |
| sn - Shona | so - Somali | sq - Albanian | sr - Serbian | sv - Swedish |
| sw - Swahili | ta - Tamil | th - Thai | tl - Tagalog | tr - Turkish |
| uk - Ukrainian | uz - Uzbek | vi - Vietnamese | xh - Xhosa | yo - Yoruba |
| zh - Chinese | zu - Zulu |
Automatic deep merge: Your custom translations merge with built-in defaults, so you only need to specify what's different.
Examples
Check out the examples/ directory for complete working examples:
- example.py — Basic usage with multiple locales
- example_web_context.py — Web application pattern (Flask/FastAPI)
- example_plurals.py — Complex pluralization rules
- example_date_formatting.py — Date and time formatting
- example_number_formatting.py — Number formatting with locale separators
- example_ordinals.py — Ordinal numbers ("1st, 2nd, 3rd")
- example_arrays.py — Array-style lookups for enums and lists
Installation & Requirements
pip install gloser
Requirements:
- Python 3.8 or higher
- PyYAML 6.0 or higher (automatically installed)
Optional dependencies for development:
pip install gloser[dev] # Includes pytest, pytest-cov
API Overview
Basic Usage
Note: Gloser provides t() as a convenient shorthand for translate(). Both work identically — use whichever you prefer!
from gloser import Gloser
# Initialize with default locale
g = Gloser(default_locale="en")
# Load translations from YAML
g.load_yaml("translations.yaml")
# Or load multiple files
g.load_yaml_files("common.yaml", "app.yaml")
# Or pass file(s) to constructor
g = Gloser("translations.yaml", default_locale="en")
# Translate with t() shorthand (recommended for brevity)
g.t("welcome", name="Alice", locale="en")
# Or use translate() if you prefer explicit naming
g.translate("welcome", name="Alice", locale="en")
# Or set current locale
g.set_locale("es")
g.translate("welcome", name="Alice") # Uses Spanish
g.t("welcome", name="Alice") # Shorthand works too
Web Application Pattern
# Initialize once at startup
gloser = Gloser("translations.yaml")
# Get locale-specific translator per request (stateless, thread-safe)
t = gloser["es"] # or gloser.for_locale("es")
message = t("welcome", name="Alice")
Controlling Locale Defaults
By default, Gloser auto-loads built-in locale defaults as needed. You can control this behavior:
# Auto-load defaults on demand (default behavior)
g = Gloser("translations.yaml")
# Don't load any built-in defaults
g = Gloser("translations.yaml", load_defaults=False)
# Load specific locales immediately
g = Gloser("translations.yaml", load_defaults=["en", "es", "de"])
# Load all 72 locale defaults at startup
g = Gloser("translations.yaml")
g.load_defaults() # Loads all available locale defaults
# Load additional defaults at runtime
g.load_defaults("fr", "it") # Load French and Italian defaults
Testing & Quality
Gloser is thoroughly tested with 203 tests covering:
- ✅ All pluralization rules (Slavic, Semitic, Romance, Baltic, Germanic languages)
- ✅ Date and time formatting (72 locales)
- ✅ Number formatting with locale separators
- ✅ Ordinal numbers
- ✅ Array-style lookups
- ✅ Web application patterns
- ✅ Fallback behavior
- ✅ Edge cases and error handling
Run tests:
pytest
pytest --cov=gloser # With coverage report
Contributing
Contributions are welcome! Whether it's:
- 🐛 Bug reports
- 💡 Feature requests
- 📝 Documentation improvements
- 🌍 New locale defaults
- ✨ Code contributions
Please open an issue or submit a pull request on GitHub.
License
MIT License - see LICENSE file for details.
Why "Gloser"?
"Gloser" is Norwegian for "glossary" — a fitting name for a translation library! 📚🇳🇴
Made with ❤️ by developers who believe i18n should be simple.
Metadata
Release files for gloser 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gloser-1.1.0.tar.gz | 63.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gloser-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 133.0 kB
Release files / gloser-1.1.0.tar.gz
| Download URL | gloser-1.1.0.tar.gz |
|---|---|
| Size | 63.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d017447be24012456a186706fce77f1c2b869f3cb367f4e8259ed7ca9a78444a
|
|
BLAKE2b-256 checksum How to use checksums |
ea6e3c4df9980dc7a356b0b5bbec7ae2a0213e13b65e4c6aaf037e01bc3aba3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.3
|
Release files / gloser-1.1.0-py3-none-any.whl
| Download URL | gloser-1.1.0-py3-none-any.whl |
|---|---|
| Size | 69.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c2aec87264c736372e447ac70027f1902e1948a76ac29afc6654fdb3cbea3194
|
|
BLAKE2b-256 checksum How to use checksums |
733f577302e75b00f0185abb599d7457cb98bd2d7ea73a086d45482f31d4b182
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.3
|