YAML-driven bot configuration, keyboards, and i18n with the layout subpackage.
The layout subpackage (gopkg.in/telebot.v4/layout) lets you define your bot’s settings, buttons, markups, inline results, and localized text in a single YAML file rather than scattering them through Go code. Template rendering, i18n, and bot settings are all handled for you.
// New parses the given layout file.func New(path string, funcs ...template.FuncMap) (*Layout, error)
This reads and parses the YAML file at path. Optional template.FuncMap arguments extend the built-in template functions available in your YAML templates.
// NewFromFS parses the layout from the given fs.FS.// It allows reading layout from the go:embed filesystem.func NewFromFS(fsys fs.FS, path string, funcs ...template.FuncMap) (*Layout, error)
Wraps tele.Btn with YAML support. Can be a simple reply button (just a text string) or an extended inline/reply button with unique, callback_data, web_app, etc.
Markup
A named keyboard layout (reply or inline) defined as rows of button references. Supports resize_keyboard, one_time_keyboard, force_reply, and remove_keyboard.
Result
A named inline query result template. Supports all Telegram result types (article, photo, audio, video, gif, etc.) with Go template rendering.
Config
A typed map interface over the config: section of the YAML file. Provides String, Int, Bool, Duration, ChatID, and other typed accessors.
Call lt.Middleware to register a Telebot middleware that assigns a locale to each incoming context. The middleware cleans up the locale when the handler returns.
// LocaleFunc is the function used to fetch the locale of the recipient.type LocaleFunc func(tele.Recipient) string// Middleware builds a telebot middleware to make localization work.func (lt *Layout) Middleware(defaultLocale string, localeFunc ...LocaleFunc) tele.MiddlewareFunc
b.Use(lt.Middleware("en", func(r tele.Recipient) string { // load the user's preferred locale from your database loc, _ := db.UserLocale(r.Recipient()) return loc}))
If localeFunc is omitted (or returns an empty string), defaultLocale is used.
// Locale returns the context locale.func (lt *Layout) Locale(c tele.Context) (string, bool)// SetLocale allows you to change a locale for the passed context.func (lt *Layout) SetLocale(c tele.Context, locale string)
By default the layout looks for locale files in the locales/ directory next to the YAML config. Each file is named <locale>.yml and contains flat or nested key-value pairs that support Go templates:
# locales/en.ymlarticle_message: This is an article.nested: example: |- This is {{ . }}.
The locales_dir setting key changes this directory:
// Text returns a text whose locale is dependent on the context.// The optional argument is passed to the text/template engine.func (lt *Layout) Text(c tele.Context, k string, args ...interface{}) string// TextLocale is like Text but takes an explicit locale.func (lt *Layout) TextLocale(locale, k string, args ...interface{}) string
// Markup returns a *tele.ReplyMarkup whose locale depends on the context.func (lt *Layout) Markup(c tele.Context, k string, args ...interface{}) *tele.ReplyMarkup// MarkupLocale is like Markup but takes an explicit locale.func (lt *Layout) MarkupLocale(locale, k string, args ...interface{}) *tele.ReplyMarkup
The layout automatically detects whether the markup is inline or reply based on the button types in use. An error is returned if you mix inline and reply buttons in the same markup.
// Button returns a *tele.Btn whose locale depends on the context.// Use this to build dynamic markups at runtime.func (lt *Layout) Button(c tele.Context, k string, args ...interface{}) *tele.Btn// Callback returns a callback endpoint, used with b.Handle.func (lt *Layout) Callback(k string) tele.CallbackEndpoint
// Register a handler for the "stop" inline buttonb.Handle(lt.Callback("stop"), onStop)// Build buttons dynamically and assemble your own markupbtns := make([]tele.Btn, len(items))for i, item := range items { btns[i] = *lt.Button(c, "item", struct { Number int Item Item }{Number: i, Item: item})}m := b.NewMarkup()m.Inline(m.Row(btns...))
// Result returns a tele.Result whose locale depends on the context.func (lt *Layout) Result(c tele.Context, k string, args ...interface{}) tele.Result// ResultLocale is like Result but takes an explicit locale.func (lt *Layout) ResultLocale(locale, k string, args ...interface{}) tele.Result
The config: section is exposed through the Config struct embedded in Layout:
// String returns a field cast to string.func (c *Config) String(k string) string// Int returns a field cast to int.func (c *Config) Int(k string) int// Int64 returns a field cast to int64.func (c *Config) Int64(k string) int64// Bool returns a field cast to bool.func (c *Config) Bool(k string) bool// Duration returns a field cast to time.Duration.func (c *Config) Duration(k string) time.Duration// ChatID returns a field cast to tele.ChatID.func (c *Config) ChatID(k string) tele.ChatID// Get returns a child map field wrapped in Config.func (c *Config) Get(k string) *Config// Unmarshal parses the whole config into a struct.func (c *Config) Unmarshal(v interface{}) error
Use lt.Commands() or lt.CommandsLocale() to register bot commands with localized descriptions:
// Commands returns a list of tele.Command for b.SetCommands.func (lt *Layout) Commands() []tele.Command// CommandsLocale returns localized tele.Commands for a given locale.func (lt *Layout) CommandsLocale(locale string, args ...interface{}) []tele.Command
# bot.ymlcommands: /start: '{{ text `cmdStart` }}'