loader.go

  1package i18n
  2
  3import (
  4	"fmt"
  5	"os"
  6	"path"
  7	"path/filepath"
  8	"strings"
  9)
 10
 11// LoadTranslations loads all translation files into a bundle.
 12// First attempts to load from embedded files, then checks for external files.
 13func LoadTranslations(bundle *Bundle) error {
 14	// Load from embedded files
 15	if err := loadFromEmbedded(bundle); err != nil {
 16		return fmt.Errorf("%w: embedded load failed: %w", ErrLoadFailed, err)
 17	}
 18
 19	return nil
 20}
 21
 22// loadFromEmbedded loads translation files from the embedded filesystem.
 23func loadFromEmbedded(bundle *Bundle) error {
 24	entries, err := localeFS.ReadDir("locales")
 25	if err != nil {
 26		return err
 27	}
 28
 29	for _, entry := range entries {
 30		if entry.IsDir() {
 31			continue
 32		}
 33
 34		filename := entry.Name()
 35		if !strings.HasSuffix(filename, ".json") {
 36			continue
 37		}
 38
 39		// Read file (embed.FS always uses forward slashes, even on Windows)
 40		data, err := localeFS.ReadFile(path.Join("locales", filename))
 41		if err != nil {
 42			continue
 43		}
 44
 45		// Extract language code from filename (e.g., "en.json" -> "en")
 46		lang := strings.TrimSuffix(filename, ".json")
 47
 48		// Load into bundle
 49		if err := loadLanguageFile(bundle, lang, data); err != nil {
 50			return err
 51		}
 52	}
 53
 54	return nil
 55}
 56
 57// LoadFromDirectory loads translation files from a directory on disk.
 58// This allows overriding embedded translations with external files.
 59func LoadFromDirectory(bundle *Bundle, dir string) error {
 60	entries, err := os.ReadDir(dir)
 61	if err != nil {
 62		return fmt.Errorf("%w: %w", ErrLoadFailed, err)
 63	}
 64
 65	for _, entry := range entries {
 66		if entry.IsDir() {
 67			continue
 68		}
 69
 70		filename := entry.Name()
 71		if !strings.HasSuffix(filename, ".json") {
 72			continue
 73		}
 74
 75		// Read file
 76		path := filepath.Join(dir, filename)
 77		data, err := os.ReadFile(path)
 78		if err != nil {
 79			continue
 80		}
 81
 82		// Extract language code
 83		lang := strings.TrimSuffix(filename, ".json")
 84
 85		// Load into bundle
 86		if err := loadLanguageFile(bundle, lang, data); err != nil {
 87			return err
 88		}
 89	}
 90
 91	return nil
 92}
 93
 94// loadLanguageFile parses and loads a single language file into the bundle.
 95func loadLanguageFile(bundle *Bundle, lang string, data []byte) error {
 96	messages, err := ParseJSON(data)
 97	if err != nil {
 98		return fmt.Errorf("%w: language %s: %w", ErrParseFailed, lang, err)
 99	}
100
101	return bundle.AddMessages(lang, messages)
102}