Skip to main content

Internationalization

VEF includes an application i18n layer for framework messages, validation output, and error responses.

Supported Languages

The framework currently embeds:

Language code
zh-CN
en

Default application language:

SettingValue
default languagezh-CN

i18n.GetSupportedLanguages() returns a copy of the supported-language slice, so modifying the returned slice does not mutate framework state. The current shipped en.json and zh-CN.json catalogs contain the same 195 message IDs.

Public API

The complete public API for github.com/coldsmirk/vef-framework-go/i18n and github.com/coldsmirk/vef-framework-go/i18n/locales is:

APIPurpose
i18n.DefaultLanguagedefault language constant (zh-CN)
i18n.GetSupportedLanguages()return a copy of the supported language codes (zh-CN, en)
i18n.IsLanguageSupported(code)report whether a language code is supported
i18n.CurrentLanguage()read the current global language code
i18n.SetLanguage(code)atomically switch the process-level global translator
i18n.T(messageID, data...)translate with graceful fallback to the message ID
i18n.Te(messageID, data...)translate with explicit error reporting
i18n.New(config)create a dedicated translator instance from i18n.Config
i18n.Configconstructor config type for dedicated translators
i18n.Config.Localesembedded locale file set used by i18n.New(config)
i18n.Translatorinterface implemented by dedicated translator instances
i18n.Translator.T(messageID, data...)translate with graceful fallback on a dedicated translator
i18n.Translator.Te(messageID, data...)translate with explicit errors on a dedicated translator
i18n.ErrUnsupportedLanguagesentinel wrapped when a requested language is unsupported
i18n.ErrMessageIDEmptysentinel returned when Te receives an empty message ID
locales.EmbedLocalesframework-shipped embedded locale file set

Most application code only needs i18n.T(...).

Translator Model

The public abstraction is i18n.Translator:

MethodPurpose
T(messageID, data...)translate and fall back to the message ID
Te(messageID, data...)translate and return explicit errors

Translation behavior is intentionally simple:

CallBehavior
i18n.T("known_key")returns the localized message from the active global translator
i18n.T("missing_key")logs the translation error and returns "missing_key" as the fallback
i18n.Te("known_key")returns the localized message and a nil error
i18n.Te("missing_key")returns "" and an error wrapping the localization failure
i18n.Te("")returns "" and i18n.ErrMessageIDEmpty

templateData is variadic for call-site convenience, but the implementation uses only the first map[string]any value. Additional maps are ignored.

Configuration Model

The constructor config type is:

TypePurpose
i18n.Configsupplies embedded locale files through Locales embed.FS
locales.EmbedLocalesdefault embedded locale file set

locales.EmbedLocales embeds the framework's shipped *.json locale files, including the built-in zh-CN and en catalogs.

i18n.New(config) creates an independent i18n.Translator. It chooses the language from VEF_I18N_LANGUAGE or i18n.DefaultLanguage when the translator is constructed, and it does not change the process-level global translator used by package-level i18n.T(...) and i18n.Te(...).

i18n.Config.Locales must provide every supported locale file. With the current supported-language list, that means both zh-CN.json and en.json; an empty embed.FS or a missing supported file makes i18n.New(config) return an error.

Error Sentinels

ErrorMeaning
i18n.ErrUnsupportedLanguagerequested language code is not supported
i18n.ErrMessageIDEmptytranslation message ID is empty

i18n.SetLanguage(...) wraps i18n.ErrUnsupportedLanguage, so callers can use errors.Is(err, i18n.ErrUnsupportedLanguage). i18n.Te("") returns i18n.ErrMessageIDEmpty directly, so errors.Is(err, i18n.ErrMessageIDEmpty) also works.

Language Selection

At startup, the framework chooses the language in this order:

PrioritySource
1VEF_I18N_LANGUAGE
2framework default language (zh-CN)

VEF_I18N_LANGUAGE is exposed by the config package as config.EnvI18NLanguage; it is not an i18n package API.

During package initialization, the global translator is created from locales.EmbedLocales. If the embedded catalogs cannot be loaded, initialization panics because framework messages would otherwise be unusable.

i18n.SetLanguage(...) can switch the process-level translator later, which is especially useful in tests. Passing an empty string to i18n.SetLanguage("") re-reads VEF_I18N_LANGUAGE and falls back to i18n.DefaultLanguage if the environment variable is empty. When the language changes, the active locale file set is preserved.

Package-level i18n.T(...) and i18n.Te(...) load the active translator through an atomic pointer, and i18n.SetLanguage(...) publishes a fresh immutable translation state through the same pointer. This makes concurrent translation calls and language switches race-free, although a request can naturally observe either the old language or the new language around the switch boundary.

Where i18n Appears Automatically

Even if you never call the i18n package directly, it already affects:

AreaEffect
result.Ok(...) and result.Err(...)localized success and error messages
validation outputtranslated field labels and custom rule messages
built-in resource errorslocalized framework responses

So i18n in VEF is not only for UI text. It is part of the backend response contract.

Minimal Example

package userinfo

import (
"github.com/gofiber/fiber/v3"

"github.com/coldsmirk/vef-framework-go/i18n"
"github.com/coldsmirk/vef-framework-go/result"
"github.com/coldsmirk/vef-framework-go/security"
)

func UserInfoNotImplemented(ctx fiber.Ctx) error {
return result.ErrNotImplemented(
i18n.T(security.ErrMessageUserInfoLoaderNotImplemented),
)
}

Validation Integration

Validation integrates with i18n in two important ways:

MechanismEffect
label_i18nfield labels are translated
custom validator rule translationsframework custom rules return localized messages

That makes request validation output much more readable in real applications.

Practical Advice

  • use i18n.T(...) for ordinary application code
  • use i18n.Te(...) only when you need explicit translation error handling
  • treat i18n.SetLanguage(...) as mainly a testing or controlled runtime helper
  • remember that result messages are already localized, so backend responses are part of your i18n surface

Next Step

Read Validation to see how translated field labels surface in request errors.