Skip to main content

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!"

PyPI version Python 3.8+ License: MIT

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:


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)

Source distribution for gloser 1.1.0
File Size Uploaded
gloser-1.1.0.tar.gz 63.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gloser 1.1.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page