Skip to content

grokify/structured-locale

Structured Locale

Build Status Lint Status Go Report Card Docs Visualization License

A Go library for locale-related data and functionality with zero external dependencies.

Features

  • BCP 47 Tag Parsing: Parse and normalize language tags like en-US, zh-Hans-CN
  • Locale Fallback: Automatic fallback chains (e.g., fr-CAfren)
  • Translation Bundles: Message translation with template variable substitution
  • CLDR Pluralization: Full support for plural categories (zero, one, two, few, many, other)
  • Embedded Defaults: Ships with translations for 6 locales (en, de, es, fr, ja, zh)
  • Override Support: Customize any embedded data with your own translations

Installation

go get github.com/grokify/structured-locale

Usage

Locale Tag Parsing

import "github.com/grokify/structured-locale/locale"

// Parse a BCP 47 tag
tag, err := locale.Parse("zh-Hans-CN")
if err != nil {
    log.Fatal(err)
}

fmt.Println(tag.Language) // "zh"
fmt.Println(tag.Script)   // "Hans"
fmt.Println(tag.Region)   // "CN"
fmt.Println(tag.String()) // "zh-Hans-CN"

// Get parent for fallback
parent := tag.Parent() // "zh-Hans"

Locale Fallback

import "github.com/grokify/structured-locale/locale"

// Get fallback chain for a locale
chain := locale.FallbackChain("fr-CA", "en")
// Returns: ["fr-CA", "fr", "en"]

Message Translation

import "github.com/grokify/structured-locale/messages"

// Create a bundle with embedded defaults
bundle := messages.NewBundle()

// Get a localizer for French
loc := bundle.Localizer("fr")

// Simple translation
fmt.Println(loc.T("category.added"))     // "Ajouté"
fmt.Println(loc.T("category.fixed"))     // "Corrigé"

// Translation with variables
fmt.Println(loc.Tf("greeting", map[string]any{
    "Name": "Alice",
})) // "Bonjour, Alice!"

// Plural translation
fmt.Println(loc.Tn("items.count", 1))  // "1 élément"
fmt.Println(loc.Tn("items.count", 5))  // "5 éléments"

Custom Translations

import "github.com/grokify/structured-locale/messages"

// Load custom translations from a file
bundle := messages.NewBundle()
err := bundle.LoadFile("fr", "custom-fr.json")
if err != nil {
    log.Fatal(err)
}

// Or load from embedded data
//go:embed locales/*.json
var localesFS embed.FS

err = bundle.LoadFS(localesFS, "locales")

Translation File Format

Translation files use a simple JSON format:

[
  {
    "id": "category.added",
    "translation": "Added"
  },
  {
    "id": "items.count",
    "translation": {
      "one": "{{.Count}} item",
      "other": "{{.Count}} items"
    }
  }
]

See schema/messages-v1.schema.json for the full JSON Schema.

Supported Locales

Built-in translations are provided for:

Code Language
en English
de German
es Spanish
fr French
ja Japanese
zh Chinese

Packages

Package Description
locale BCP 47 tag parsing, normalization, fallback logic
messages Translation bundles, pluralization, message formatting

Roadmap

See PRD.md for the full roadmap including planned packages for countries, phones, currencies, languages, dates, numbers, and addresses.

License

MIT License - see LICENSE for details.

About

A Go library for locale-related data and functionality with zero external dependencies.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors

Languages