Skip to content

Internationalization

Goshtoso uses English for built-in labels and messages. To use another language, supply an expressions.Set with text from your application’s translations or configuration. Each field belongs to a component, so you can replace a few labels or provide a complete set.

See the live examples to try five languages and compare request, application, and component settings.

Application defaults

Configure application defaults once
import "github.com/araihu/goshtoso/expressions"

renderer := expressions.NewRenderer(expressions.Set{
    Pagination: expressions.Pagination{
        PreviousLabel:  "Anterior",
        PreviousAriaLabel: "Página anterior",
        NextLabel:      "Próxima",
        NextAriaLabel:     "Próxima página",
    },
    CodeBlock: expressions.CodeBlock{
        CopyLabel:   "Copiar",
        CopiedLabel: "Copiado!",
        ErrorText:   "Não foi possível copiar",
    },
})

// Reuse the renderer in HTTP handlers, background rendering, or static generation.
err := renderer.Render(r.Context(), w, Page())

Create a renderer with the defaults you want to share across your application. It fills any missing expressions with English. The same renderer can serve multiple requests; if you update its configuration, replace it using your application’s usual concurrency controls.

Request preferences

Choose the user’s expressions in your request handler and add them to the context. These values take precedence over the renderer’s defaults for that render.

Override per request
ctx := expressions.With(r.Context(), userExpressions)
err := renderer.Render(ctx, w, Page())

You can layer system, tenant, and user preferences with successive expressions.With calls. Each call replaces only the fields you supply. Ordinary component.Render(ctx, w) also reads these values, so a configured renderer is optional.

For HTMX, pass the same expressions to every page and fragment handler. A renderer’s defaults apply only when you render through it; handlers that render directly need both system defaults and user overrides in their request context. Middleware is a convenient place to set them.

Component overrides

Use WithExpressions when a component needs its own wording. The method returns a new component value, leaving the original unchanged.

Override one component
control := pagination.Pagination(pagination.Config{
    CurrentPage: 2,
    TotalPages:  10,
}).WithExpressions(expressions.Pagination{
    NextLabel: "Continue",
})

An explicit label in a component’s config takes precedence over WithExpressions. For example, search.Config.Placeholder still controls that search field. The navbar.SecondaryRow helper uses SecondaryConfig.AriaLabel for its label.

Each expression is resolved in this order:

  1. An existing explicit component config field, where one exists.
  2. The nearest component’s WithExpressions value.
  3. Request/render-context expressions.
  4. Configured renderer defaults.
  5. Built-in English.

Overrides also apply to nested components. A child can supply its own values; siblings outside that component keep their defaults. Empty strings and nil functions mean “inherit.” Use the component’s content or visibility options to hide content.

Counts and changing messages

For messages that include a count or page number, supply a function that returns the whole message. Your translation library can handle plural rules, number formatting, and word order inside that function.

Complete dynamic messages
set := expressions.Set{
    Combobox: expressions.Combobox{
        SelectedLabel: func(count int) string {
            return translator.SelectedCount(count)
        },
    },
    Pagination: expressions.Pagination{
        PageAriaLabel: func(page int) string {
            return translator.PageAriaLabel(page)
        },
    },
}

Carousel and client-side Combobox prepare their possible count messages during rendering, then use them as the user interacts. Count functions must handle zero and positive counts. Render the component again when its available options change. Copy-button feedback, mobile navigation, and file-selection messages also use the text supplied during rendering.

When the user changes languages, render the page or affected fragments again. Existing browser content keeps its previous text until it is replaced. Keep any state captured by message functions immutable, or synchronize access when sharing it between requests.

To load translations from JSON, YAML, or embedded files, see Expression files, including the schema and complete property reference.

Application content

Expressions cover the text built into Goshtoso components. Your application still supplies its own titles, navigation items, table data, and validation messages, and handles language selection and right-to-left layout.