gotext

repository·master·Indexed 19 days ago

https://github.com/leonelquinteros/gotext

A native Go implementation of GNU Gettext utilities providing thread-safe internationalization (i18n) and localization (l10n) support using PO and MO files. It includes the xgotext CLI tool for automating translation string extraction from Go source code and supports complex plural forms, contexts (msgctxt), and multiple translation domains.

Tokens
12.9K
Snippets
63
Records
85
Agent score
60%

What's inside gotext

  1. Overview of gotext

    master

    gotext

    gotext is a native Go implementation of the GNU Gettext utilities designed for internationalization (i18n) and localization (l10n). It is built to be thread-safe and suitable for high-performance, concurrent applications like web servers.

    Key Features:

    • Native Go: Operates without external dependencies or CGO.
    • Thread-safe: Safe for concurrent use.
    • Gettext Compatible: Full support for standard .po and .mo files, including complex plural forms and contexts.
    • CLI Support: Includes the xgotext tool to automate string extraction from source code.
  2. Locales directory structure

    master

    The package expects a standard GNU Gettext directory structure. It supports automatic language simplification (e.g., falling back from en_UK to en).

    Expected structure:

    /path/to/locales
      /en_US
        /LC_MESSAGES
          default.po
      /es_ES
        default.po
  3. Leverage context (`msgctxt`) to resolve ambiguity

    master

    Use context (msgctxt) when the same string has different meanings depending on its usage. This prevents incorrect translations for short, ambiguous strings.

    Implementation in Go: Use gotext.GetC(msgid, context) to provide the context.

    Implementation in PO files: The context is defined using the msgctxt keyword.

    // In code:
    gotext.GetC("Open", "File menu")
    gotext.GetC("Open", "Lock status")
    
    // In PO file:
    msgctxt "File menu"
    msgid "Open"
    msgstr "Abrir"
    
    msgctxt "Lock status"
    msgid "Open"
    msgstr "Abierto"
  4. Thread safety and global configuration

    master

    The gotext package is thread-safe. You can safely share a Locale object across multiple goroutines, such as within HTTP handlers.

    Warning: Avoid calling gotext.Configure to change global package state after your application has already started, as this can lead to race conditions or unexpected behavior.

  5. How xgotext string extraction works

    master
    The xgotext tool parses Go source files to identify function calls matching the default keywords (or custom ones provided via -k). It extracts unique msgid and msgctxt pairs and writes them to the specified output file. If the output file already exists, xgotext preserves existing translations while adding new strings.
  6. How plural form resolution works

    master

    The gotext pluralization workflow follows these three steps:

    1. Header Parsing: When a PO file is loaded, gotext parses the Plural-Forms header to understand the language's rules.
    2. Expression Evaluation: When a function like GetN is called, gotext evaluates the plural expression from the header using the provided quantity n.
    3. Result Indexing: The result of the expression (an integer index like 0, 1, or 2) is used to select the corresponding msgstr[n] entry from the translation file.
  7. Organize your directory structure for translations

    master

    To ensure compatibility and maintainability, follow the standard Gettext directory structure. Place your .po and .mo files inside an LC_MESSAGES directory under the language code.

    Recommended Structure:

    /locales
      /en_US
        /LC_MESSAGES
          default.po
          errors.po
      /es_ES
        /LC_MESSAGES
          default.po
          errors.po

    Best Practices:

    • LC_MESSAGES: Always use this directory or place files directly under the language code.
    • Simplified Codes: Provide fallback locales (e.g., provide es if es_AR and es_ES share many strings) to reduce duplication.
  8. Configure character encoding in .po files

    master

    The encoding of a .po file is determined by the charset parameter within the Content-Type header. While gotext respects this header, it is highly recommended to use UTF-8 to ensure seamless compatibility with Go's internal string representation and to avoid manual conversion issues.

    msgid ""
    msgstr ""
    "Content-Type: text/plain; charset=UTF-8\n"
    "Content-Transfer-Encoding: 8bit\n"
  9. Quick start with the package-level API

    master

    For simple use cases, use the package-level functions. You must first call gotext.Configure to specify the base path for your locales, the target language (e.g., en_US), and the default domain name.

    Once configured, you can use gotext.Get to retrieve translations. It supports standard fmt package syntax for dynamic variables.

    package main
    
    import (
        "fmt"
        "github.com/leonelquinteros/gotext"
    )
    
    func main() {
        // Configure package: locales path, language, and domain
        gotext.Configure("/path/to/locales", "en_US", "default")
    
        // Simple translation
        fmt.Println(gotext.Get("Hello, world!"))
    
        // Translation with variables
        fmt.Println(gotext.Get("Hello, %s!", "Gopher"))
    }
  10. Use the Package-Level API for simple applications

    master

    For simple applications with a single primary language and domain, you can use the package-level functions directly.

    1. Configure: Call gotext.Configure(localesPath, languageCode, defaultDomain) to set up the global state.
    2. Translate: Use gotext.Get(message) to retrieve translations. You can use standard fmt style verbs (like %s) for dynamic variables.
    package main
    
    import (
        "fmt"
        "github.com/leonelquinteros/gotext"
    )
    
    func main() {
        // 1. Configure the package
        // Path to locales, language code, and default domain
        gotext.Configure("locales", "en_US", "default")
    
        // 2. Use it!
        fmt.Println(gotext.Get("Hello, world!"))
        
        // 3. Use it with variables
        name := "Gopher"
        fmt.Println(gotext.Get("Hello, %s!", name))
    }