Загрузка данных


package i18n

import (
	"embed"
	"encoding/json"
	"fmt"
	"sync"

	"github.com/nicksnyder/go-i18n/v2/i18n"
	"golang.org/x/text/language"
)

//go:embed translations/*.json
var translationFS embed.FS

// Locale represents supported locales
type Locale string

const (
	// LocaleEN represents English locale
	LocaleEN Locale = "en"
	// LocaleRU represents Russian locale
	LocaleRU Locale = "ru"
)

// DefaultLocale is the default locale used when none is specified
const DefaultLocale = LocaleEN

// MessageKey represents a translatable message identifier
type MessageKey string

// Message keys for all user-facing messages
const (
	// Error messages
	MsgCompanyAlreadyExists        MessageKey = "company_already_exists"
	MsgUserPowersNotConfirmed      MessageKey = "user_powers_not_confirmed"
	MsgNoMCHDFilesProvided         MessageKey = "no_mchd_files_provided"
	MsgNoSignedFileProvided        MessageKey = "no_signed_file_provided"
	MsgFailedToCreateCompany       MessageKey = "failed_to_create_company"
	MsgMCHDValidationFailed        MessageKey = "mchd_validation_failed"
	MsgRegulationsValidationFailed MessageKey = "regulations_validation_failed"
	MsgRegulationsSignerMismatch   MessageKey = "regulations_signer_mismatch"
	MsgModeratorTaskFailed         MessageKey = "moderator_task_failed"
	MsgInvalidTaxID                MessageKey = "invalid_tax_id"
	MsgUnknownStep                 MessageKey = "unknown_step"
	MsgUnknownAction               MessageKey = "unknown_action"
	MsgActionFailed                MessageKey = "action_failed"
	MsgFailedToDetermineNextStep   MessageKey = "failed_to_determine_next_step"

	// Notification messages
	MsgVerificationStartedSubject MessageKey = "verification_started_subject"
	MsgVerificationStartedBody    MessageKey = "verification_started_body"
)

var (
	bundle     *i18n.Bundle
	localizers map[Locale]*i18n.Localizer
	initOnce   sync.Once
)

// supportedLocales maps our Locale type to golang.org/x/text/language tags
var supportedLocales = map[Locale]language.Tag{
	LocaleEN: language.English,
	LocaleRU: language.Russian,
}

// initialize loads all translation files and creates localizers
func initialize() {
	initOnce.Do(func() {
		bundle = i18n.NewBundle(language.English)
		bundle.RegisterUnmarshalFunc("json", json.Unmarshal)

		// Load translation files derived from supportedLocales
		for locale := range supportedLocales {
			file := fmt.Sprintf("translations/active.%s.json", locale)
			// Embedded files should always load successfully; panic if not
			if _, err := bundle.LoadMessageFileFS(translationFS, file); err != nil {
				panic(fmt.Sprintf("failed to load %s: %v", file, err))
			}
		}

		// Create localizers for each supported locale
		localizers = make(map[Locale]*i18n.Localizer)
		for locale, tag := range supportedLocales {
			localizers[locale] = i18n.NewLocalizer(bundle, tag.String())
		}
	})
}

// getLocalizer returns the localizer for the given locale with fallback to default
func getLocalizer(locale Locale) *i18n.Localizer {
	initialize()

	if locale == "" {
		locale = DefaultLocale
	}

	if loc, ok := localizers[locale]; ok {
		return loc
	}

	return localizers[DefaultLocale]
}

// GetMessage returns a translated message for the given key and locale.
// If the locale or key is not found, it falls back to the default locale.
func GetMessage(locale Locale, key MessageKey) string {
	return GetMessageWithData(locale, key, nil)
}

// GetMessageWithData returns a translated message with template data.
// Template data is a map that will be used to substitute placeholders in the message.
// For example: {"CompanyName": "Acme Corp"} will replace {{.CompanyName}} in the message.
func GetMessageWithData(locale Locale, key MessageKey, data map[string]interface{}) string {
	localizer := getLocalizer(locale)
	if localizer == nil {
		return string(key)
	}

	msg, err := localizer.Localize(&i18n.LocalizeConfig{
		MessageID:    string(key),
		TemplateData: data,
	})
	if err != nil {
		return string(key)
	}

	return msg
}

// IsValidLocale checks if the given locale is supported
func IsValidLocale(locale Locale) bool {
	_, ok := supportedLocales[locale]
	return ok
}