# Localization

# Introduction

The Goyave framework provides a convenient way to support multiple languages within your application. Out of the box, Goyave only provides the en-US language.

# Writing language files

Language files are stored in the resources/lang directory.

. └── resources    └── lang       └── en-US (language name)       ├── fields.json (optional)       ├── locale.json (optional)       └── rules.json (optional)

Each language has its own directory and should be named with an ISO 639-1 (opens new window) language code. You can also append a variant to your languages: en-US, en-UK, fr-FR, fr-CA, ... Case is important.

Each language directory contains three files. Each file is optional.

  • fields.json: field names translations and field-specific rule messages.
  • locale.json: all other language lines.
  • rules.json: validation rules messages.

TIP

All directories in the resources/lang directory are automatically loaded when the server starts.

# Fields

The fields.json file contains the field names translations and their rule-specific messages. Translating field names helps making more expressive messages instead of showing the technical field name to the user. Rule-specific messages let you override a validation rule message for a specific field.

Example:

{
    "email": {
        "name": "email address",
        "rules": {
            "required": "You must provide an :field."
        }
    }
}

This fields.json file will change the validation message of the required validation rule to You must provide an email address.

TIP

Learn more about validation messages placeholders in the validation section.

# Locale

The locale.json file contains all language lines that are not related to validation. This is the place where you should write the language lines for your user interface or for the messages returned by your controllers.

Example:

{
    "product.created": "The product have been created with success.",
    "product.deleted": "The product have been deleted with success."
}

TIP

It is a good practice to use dot-separated names for language lines to help making them clearer and more expressive.

# Rules

The rules.json file contains the validation rules messages. These messages can have placeholders, which will be automatically replaced by the validator with dynamic values. If you write custom validation rules, their messages shall be written in this file.

Example:

{
    "integer": "The :field must be an integer.",
    "starts_with": "The :field must start with one of the following values: :values.",
    "same": "The :field and the :other must match."
}

# Type-dependent rules

The following rules have type-dependent messages. That means that their message is different depending on the type of the validated data.

  • min
  • max
  • size
  • greater_than
  • greater_than_equal
  • lower_than
  • lower_than_equal
  • between

Type-dependent rules must have a language line for the four following types:

  • string
  • numeric
  • array
  • file

Example:

{
    "min.string": "The :field must be at least :min characters.",
    "min.numeric": "The :field must be at least :min.",
    "min.array": "The :field must have at least :min items.",
    "min.file": "The :field must be at least :min KiB."
}

# Array validation

Each rule, except the file-related rules, can be used to validate array values. If a rule is used to validate an array value and doesn't pass, the rule message validation.rules.<rule_name>.array (or validation.rules.<rule_name>.<type>.array if the rule is type-dependent) is returned.

Example:

{
    "min.string.array": "The :field values must be at least :min characters.",
    "min.numeric.array": "The :field values must be at least :min.",
    "min.array.array": "The :field values must have at least :min items.",
    "digits.array": "The :field values must be digits only."
}

# Overrides

If you define the en-US language in your application, the default language lines will be overridden by the ones in your language files, and all the undefined ones will be kept.

It is possible to load a language directory manually from another location than the stardard resources/lang using the lang.Load() function. If the loaded language is already available in your application, the newly loaded one will override the previous in the same manner.

# Using localization

When an incoming request enters your application, the core language middleware checks if the Accept-Language header is set, and set the goyave.Request's Lang attribute accordingly. Localization is handled automatically by the validator.

To use the localization feature, import the lang package:

import "goyave.dev/goyave/v4/lang"

The main function of the localization feature is lang.Get(language, line string). This function lets you retrieve a language entry.

For validation rules and attributes messages, use the following dot-separated paths:

  • validation.rules.<rule_name>
  • validation.rules.<rule_name>.string
  • validation.rules.<rule_name>.numeric
  • validation.rules.<rule_name>.array
  • validation.rules.<rule_name>.file
  • validation.fields.<field_name>
  • validation.fields.<field_name>.<rule_name>

For normal lines, just use the name of the line. Note that if you have a line called "validation", it won't conflict with the dot-separated paths. If the line cannot be found, or the requested language is not available, the function will return the exact line attribute.

Example:

func ControllerHandler(response *goyave.Response, request *goyave.Request) {
    response.String(http.StatusOK, lang.Get(request.Lang, "my-custom-message"))
}

# Placeholders

Since v2.10.0

Language lines can contain placeholders. Placeholders are identified by a colon directly followed by the placeholder name:

"greetings": "Greetings, :username!"

The last parameter of the lang.Get() method is a variadic associative slice of placeholders and their replacement. In the following example, the placeholder :username will be replaced with the Name field in the user struct.

lang.Get("en-US", "greetings", ":username", user.Name) // "Greetings, Taylor!"

You can provide as many as you want:

lang.Get("en-US", "greetings-with-date", ":username", user.Name, ":day", "Monday") // "Greetings, Taylor! Today is Monday"

TIP

When a placeholder is given, all occurrences are replaced.

"popular": ":product are very popular. :product sales exceeded 1000 last week."
lang.Get("en-US", "popular", ":product", "Lawnmowers")
// "Lawnmowers are very popular. Lawnmowers sales exceeded 1000 last week."

# Localization reference

# lang.Get

Get a language line.

Parameters Return
lang string string
line string
placeholders ...string

Example:

fmt.Println(lang.Get("en-US", "my-custom-message")) // "my message"
fmt.Println(lang.Get("en-US", "validation.rules.greater_than.string")) // "The :field must be longer than the :other."
fmt.Println(lang.Get("en-US", "validation.fields.email")) // "email address"
fmt.Println(lang.Get("en-US", "greetings", ":username", user.Name)) // "Greetings, Taylor!"

# lang.Load

Load a language directory.

Parameters Return
language string void
path string

Example:

lang.Load("zh", "/path/to/chinese-lang")

# lang.IsAvailable

Returns true if the language is available.

Parameters Return
lang string bool

Example:

fmt.Println(lang.IsAvailable("zh")) // true

# lang.GetAvailableLanguages

Returns a slice of all loaded languages.

This can be used to generate different routes for all languages supported by your applications such as:

/en/products
/fr/produits
...
Parameters Return
lang string bool

Example:

fmt.Println(lang.GetAvailableLanguages()) // [en-US zh]

# lang.DetectLanguage

DetectLanguage detects the language to use based on the given lang string. The given lang string can use the HTTP "Accept-Language" header format.

  • If * is provided, the default language will be used.
  • If multiple languages are given, the first available language will be used, and if none are available, the default language will be used.
  • If no variant is given (for example "en"), the first available variant will be used.

For example, if en-US and en-UK are available and the request accepts en, en-US will be used.

Parameters Return
lang string string

Example:

fmt.Println(lang.DetectLanguage("en, fr-FR;q=0.9")) // "en-US"