Locale Starter

Introduction

The Locale Starter provides internationalization (i18n) support for Hiboot applications. It allows you to create multilingual applications by managing translations through configuration files.

Installation

Import the locale starter in your application:

import "github.com/hidevopsio/hiboot/pkg/starter/locale"

Configuration

Enable locale in your application.yml:

app:
  profiles:
    include:
    - locale

locale:
  default: en-US
  supported:
    - en-US
    - zh-CN

Or enable it programmatically:

package main

import (
	"github.com/hidevopsio/hiboot/pkg/app"
	"github.com/hidevopsio/hiboot/pkg/app/web"
	"github.com/hidevopsio/hiboot/pkg/starter/locale"
)

func main() {
	web.NewApplication().
		SetProperty(app.ProfilesInclude, locale.Profile).
		Run()
}

Translation Files

Create translation files in the config/i18n directory:

Project Structure

config/
└── i18n/
    ├── en-US.yml
    └── zh-CN.yml

config/i18n/en-US.yml

messages:
  greeting: "Hello, {name}!"
  welcome: "Welcome to our application"

errors:
  not_found: "Resource not found"
  unauthorized: "You are not authorized"
  validation:
    required: "This field is required"
    email: "Please enter a valid email address"

user:
  create_success: "User created successfully"
  delete_success: "User deleted successfully"

config/i18n/zh-CN.yml

messages:
  greeting: "你好,{name}!"
  welcome: "欢迎使用我们的应用"

errors:
  not_found: "资源未找到"
  unauthorized: "您未获得授权"
  validation:
    required: "此字段为必填项"
    email: "请输入有效的电子邮件地址"

user:
  create_success: "用户创建成功"
  delete_success: "用户删除成功"

Basic Usage

Inject Locale Service

package controller

import (
	"github.com/hidevopsio/hiboot/pkg/app"
	"github.com/hidevopsio/hiboot/pkg/at"
	"github.com/hidevopsio/hiboot/pkg/model"
	"github.com/hidevopsio/hiboot/pkg/starter/locale"
)

type userController struct {
	at.RestController
	at.RequestMapping `value:"/user"`

	locale locale.Service
}

func init() {
	app.Register(newUserController)
}

func newUserController(locale locale.Service) *userController {
	return &userController{
		locale: locale,
	}
}

func (c *userController) Post(request *CreateUserRequest) (model.Response, error) {
	// Create user...

	response := new(model.BaseResponse)
	response.SetMessage(c.locale.Get("user.create_success"))
	return response, nil
}

Get Translation with Parameters

func (c *userController) GetGreeting(name string) (model.Response, error) {
	// Get translation with placeholder replacement
	greeting := c.locale.GetWithParams("messages.greeting", map[string]string{
		"name": name,
	})

	response := new(model.BaseResponse)
	response.SetData(greeting)
	return response, nil
}

Language Detection

From Accept-Language Header

The locale service automatically detects the user’s preferred language from the Accept-Language HTTP header:

func (c *userController) Get() (model.Response, error) {
	// Get locale from request context
	lang := c.Ctx.GetHeader("Accept-Language")

	// Use locale service with specific language
	message := c.locale.GetForLocale(lang, "messages.welcome")

	response := new(model.BaseResponse)
	response.SetData(message)
	return response, nil
}

From Query Parameter

Allow users to specify language via query parameter:

func (c *userController) Get(lang string) (model.Response, error) {
	if lang == "" {
		lang = "en-US" // Default
	}

	message := c.locale.GetForLocale(lang, "messages.welcome")

	response := new(model.BaseResponse)
	response.SetData(message)
	return response, nil
}

API Reference

locale.Service Interface

type Service interface {
	// Get returns the translation for the current locale
	Get(key string) string

	// GetWithParams returns the translation with placeholder replacement
	GetWithParams(key string, params map[string]string) string

	// GetForLocale returns the translation for a specific locale
	GetForLocale(locale, key string) string

	// GetForLocaleWithParams returns the translation with params for a specific locale
	GetForLocaleWithParams(locale, key string, params map[string]string) string

	// SetLocale sets the current locale
	SetLocale(locale string)

	// GetLocale returns the current locale
	GetLocale() string
}

Error Messages Example

package controller

import (
	"github.com/hidevopsio/hiboot/pkg/app"
	"github.com/hidevopsio/hiboot/pkg/at"
	"github.com/hidevopsio/hiboot/pkg/model"
	"github.com/hidevopsio/hiboot/pkg/starter/locale"
)

type apiController struct {
	at.RestController
	at.RequestMapping `value:"/api"`

	locale locale.Service
}

func init() {
	app.Register(newApiController)
}

func newApiController(locale locale.Service) *apiController {
	return &apiController{
		locale: locale,
	}
}

func (c *apiController) GetById(id uint64) (model.Response, error) {
	resource, err := findResource(id)
	if err != nil {
		response := new(model.BaseResponse)
		response.SetCode(404)
		response.SetMessage(c.locale.Get("errors.not_found"))
		return response, nil
	}

	response := new(model.BaseResponse)
	response.SetData(resource)
	return response, nil
}

Validation Messages

Integrate with validation for localized error messages:

func (c *userController) Post(request *CreateUserRequest) (model.Response, error) {
	// Validate request
	if request.Email == "" {
		response := new(model.BaseResponse)
		response.SetCode(400)
		response.SetMessage(c.locale.Get("errors.validation.required"))
		return response, nil
	}

	if !isValidEmail(request.Email) {
		response := new(model.BaseResponse)
		response.SetCode(400)
		response.SetMessage(c.locale.Get("errors.validation.email"))
		return response, nil
	}

	// Continue with user creation...
}

Configuration Reference

Property Description Default
locale.default Default locale en-US
locale.supported List of supported locales ["en-US"]

Best Practices

  1. Organize translations: Group related translations under common prefixes
  2. Use placeholders: Use {param} syntax for dynamic content
  3. Provide fallbacks: Always have a default locale with complete translations
  4. Keep keys consistent: Use the same keys across all locale files
  5. Avoid hardcoding: Never hardcode user-facing strings in code

What’s Next?