- Introduced locale parameter in GetAssistants and GetAssistant methods across various store implementations to support localized responses. - Updated handleChatLatest, handleAssistantList, handleAssistantDetail, and handleAssistantTags functions to utilize the new locale handling for better user experience. - Refactored GetPlaceholder method in the Assistant struct to accept a locale argument for dynamic placeholder translations. - Removed redundant i18n code and centralized translation logic for cleaner implementation.
222 lines
4.3 KiB
Go
222 lines
4.3 KiB
Go
package i18n
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/yaoapp/gou/application"
|
|
"github.com/yaoapp/gou/fs"
|
|
"github.com/yaoapp/kun/maps"
|
|
)
|
|
|
|
// Locales the locales
|
|
var Locales = map[string]map[string]I18n{}
|
|
|
|
// I18n the i18n struct
|
|
type I18n struct {
|
|
Locale string `json:"locale,omitempty" yaml:"locale,omitempty"`
|
|
Messages map[string]any `json:"messages,omitempty" yaml:"messages,omitempty"`
|
|
}
|
|
|
|
// Map the i18n map
|
|
type Map map[string]I18n
|
|
|
|
// Parse parse the input
|
|
func (i18n I18n) Parse(input any) any {
|
|
|
|
switch in := input.(type) {
|
|
case string:
|
|
trimed := strings.TrimSpace(in)
|
|
hasExp := strings.HasPrefix(trimed, "{{") && strings.HasSuffix(trimed, "}}")
|
|
if hasExp {
|
|
exp := strings.TrimSpace(strings.TrimPrefix(strings.TrimSuffix(trimed, "}}"), "{{"))
|
|
if _, ok := i18n.Messages[exp]; ok {
|
|
return i18n.Messages[exp]
|
|
}
|
|
return exp
|
|
}
|
|
|
|
if _, ok := i18n.Messages[trimed]; ok {
|
|
return i18n.Messages[trimed]
|
|
}
|
|
|
|
return in
|
|
|
|
case map[string]any:
|
|
new := map[string]any{}
|
|
for key, value := range in {
|
|
new[key] = i18n.Parse(value)
|
|
}
|
|
return new
|
|
|
|
case []any:
|
|
new := []any{}
|
|
for _, value := range in {
|
|
new = append(new, i18n.Parse(value))
|
|
}
|
|
return new
|
|
|
|
case []string:
|
|
new := []string{}
|
|
for _, value := range in {
|
|
new = append(new, i18n.Parse(value).(string))
|
|
}
|
|
return new
|
|
}
|
|
|
|
return input
|
|
}
|
|
|
|
// GetLocales get the locales from path
|
|
func GetLocales(path string) (Map, error) {
|
|
app, err := fs.Get("app")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
i18ns := Map{}
|
|
localesdir := filepath.Join(path, "locales")
|
|
if has, _ := app.Exists(localesdir); has {
|
|
locales, err := app.ReadDir(localesdir, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// load locales
|
|
for _, locale := range locales {
|
|
localeData, err := app.ReadFile(locale)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var messages maps.Map = map[string]any{}
|
|
err = application.Parse(locale, localeData, &messages)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
name := strings.ToLower(strings.TrimSuffix(filepath.Base(locale), ".yml"))
|
|
i18ns[name] = I18n{Locale: name, Messages: messages}
|
|
}
|
|
}
|
|
return i18ns, nil
|
|
}
|
|
|
|
// Flatten flatten the i18n map
|
|
func (i18ns Map) Flatten() Map {
|
|
new := Map{}
|
|
for lang, i18n := range i18ns {
|
|
new[lang] = I18n{Locale: lang, Messages: maps.MapOf(i18n.Messages).Dot()}
|
|
|
|
// Add short lang
|
|
parts := strings.Split(lang, "-")
|
|
|
|
// en
|
|
if parts[0] != lang {
|
|
new[parts[0]] = new[lang]
|
|
}
|
|
|
|
// us
|
|
if len(parts) > 1 {
|
|
new[parts[1]] = new[lang]
|
|
}
|
|
}
|
|
return new
|
|
}
|
|
|
|
// FlattenWithGlobal flatten the i18n map with global i18n
|
|
func (i18ns Map) FlattenWithGlobal() Map {
|
|
|
|
// New i18n map
|
|
new := Map{}
|
|
|
|
// Global i18n
|
|
globalI18ns, hasGlobal := Locales["__global__"]
|
|
|
|
// Extend the i18n map with global i18n
|
|
for lang, i18n := range i18ns {
|
|
new[lang] = I18n{Locale: lang, Messages: maps.MapOf(i18n.Messages).Dot()}
|
|
if hasGlobal {
|
|
if global, has := globalI18ns[lang]; has {
|
|
for key, value := range global.Messages {
|
|
if _, ok := new[lang].Messages[key]; !ok {
|
|
new[lang].Messages[key] = value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add short lang
|
|
parts := strings.Split(lang, "-")
|
|
|
|
// en
|
|
if parts[0] != lang {
|
|
new[parts[0]] = new[lang]
|
|
}
|
|
|
|
// us
|
|
if len(parts) > 1 {
|
|
new[parts[1]] = new[lang]
|
|
}
|
|
}
|
|
|
|
return new
|
|
}
|
|
|
|
// Translate translate the input
|
|
func Translate(assistantID string, locale string, input any) any {
|
|
|
|
locale = strings.ToLower(strings.TrimSpace(locale))
|
|
i18ns, has := Locales[assistantID]
|
|
if !has {
|
|
i18ns = map[string]I18n{}
|
|
}
|
|
|
|
i18n, has := i18ns[locale]
|
|
if !has {
|
|
parts := strings.Split(locale, "-")
|
|
if len(parts) > 1 {
|
|
i18n, has = i18ns[parts[1]]
|
|
}
|
|
if !has {
|
|
i18n, has = i18ns[parts[0]]
|
|
}
|
|
}
|
|
|
|
if !has {
|
|
var hasGlobal bool = false
|
|
i18ns, hasGlobal = Locales["__global__"]
|
|
if hasGlobal {
|
|
i18n, has = i18ns[locale]
|
|
}
|
|
}
|
|
|
|
if has {
|
|
return i18n.Parse(input)
|
|
}
|
|
|
|
return input
|
|
}
|
|
|
|
// TranslateGlobal translate the input with global i18n
|
|
func TranslateGlobal(locale string, input any) any {
|
|
locale = strings.ToLower(strings.TrimSpace(locale))
|
|
i18ns, has := Locales["__global__"]
|
|
if !has {
|
|
i18ns = map[string]I18n{}
|
|
}
|
|
|
|
i18n, has := i18ns[locale]
|
|
if !has {
|
|
parts := strings.Split(locale, "-")
|
|
if len(parts) > 1 {
|
|
i18n, has = i18ns[parts[1]]
|
|
}
|
|
if !has {
|
|
i18n, has = i18ns[parts[0]]
|
|
}
|
|
}
|
|
|
|
if has {
|
|
return i18n.Parse(input)
|
|
}
|
|
return input
|
|
}
|