Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
120 lines
3.2 KiB
Go
120 lines
3.2 KiB
Go
package company
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// DefaultLanguage is the content-language fallback when unset.
|
|
const DefaultLanguage = "en"
|
|
|
|
// ContentLanguages is the allowlist for companies.language (AI/product content).
|
|
// Keep in sync with apps/web/src/lib/content-languages.ts.
|
|
var ContentLanguages = []string{
|
|
"en", "fr", "de", "es", "it", "nl", "pt", "pl",
|
|
"cs", "sk", "hu", "ro", "bg", "hr", "sl",
|
|
"sv", "da", "fi", "el", "et", "lv", "lt", "mt", "ga",
|
|
"ja", // CJK plug-in slot (Japanese)
|
|
}
|
|
|
|
// contentLanguageLabels are English display names for AI prompt injection.
|
|
// Keep in sync with apps/web/src/lib/content-languages.ts labels.
|
|
var contentLanguageLabels = map[string]string{
|
|
"en": "English",
|
|
"fr": "French",
|
|
"de": "German",
|
|
"es": "Spanish",
|
|
"it": "Italian",
|
|
"nl": "Dutch",
|
|
"pt": "Portuguese",
|
|
"pl": "Polish",
|
|
"cs": "Czech",
|
|
"sk": "Slovak",
|
|
"hu": "Hungarian",
|
|
"ro": "Romanian",
|
|
"bg": "Bulgarian",
|
|
"hr": "Croatian",
|
|
"sl": "Slovenian",
|
|
"sv": "Swedish",
|
|
"da": "Danish",
|
|
"fi": "Finnish",
|
|
"el": "Greek",
|
|
"et": "Estonian",
|
|
"lv": "Latvian",
|
|
"lt": "Lithuanian",
|
|
"mt": "Maltese",
|
|
"ga": "Irish",
|
|
"ja": "Japanese",
|
|
}
|
|
|
|
var contentLanguageSet map[string]struct{}
|
|
|
|
func init() {
|
|
contentLanguageSet = make(map[string]struct{}, len(ContentLanguages))
|
|
for _, code := range ContentLanguages {
|
|
contentLanguageSet[code] = struct{}{}
|
|
}
|
|
}
|
|
|
|
// NormalizeLanguage trims and lowercases a content-language code.
|
|
func NormalizeLanguage(raw string) string {
|
|
return strings.ToLower(strings.TrimSpace(raw))
|
|
}
|
|
|
|
// IsAllowedLanguage reports whether code is in ContentLanguages (after normalize).
|
|
func IsAllowedLanguage(raw string) bool {
|
|
_, ok := contentLanguageSet[NormalizeLanguage(raw)]
|
|
return ok
|
|
}
|
|
|
|
// ParseLanguage validates and normalizes a content-language code.
|
|
// Empty input returns DefaultLanguage when allowEmptyAsDefault is true;
|
|
// otherwise empty is an error (use for explicit PATCH language fields).
|
|
func ParseLanguage(raw string, allowEmptyAsDefault bool) (string, error) {
|
|
code := NormalizeLanguage(raw)
|
|
if code == "" {
|
|
if allowEmptyAsDefault {
|
|
return DefaultLanguage, nil
|
|
}
|
|
return "", fmt.Errorf("language is required")
|
|
}
|
|
if !IsAllowedLanguage(code) {
|
|
return "", fmt.Errorf("unsupported language %q", code)
|
|
}
|
|
return code, nil
|
|
}
|
|
|
|
// LanguageLabel returns the English display name for a content-language code
|
|
// (for AI prompt injection). Empty/unknown codes fall back to English.
|
|
func LanguageLabel(raw string) string {
|
|
code, err := ParseLanguage(raw, true)
|
|
if err != nil {
|
|
code = DefaultLanguage
|
|
}
|
|
if label, ok := contentLanguageLabels[code]; ok {
|
|
return label
|
|
}
|
|
return contentLanguageLabels[DefaultLanguage]
|
|
}
|
|
|
|
// LoadLanguage returns companies.language for companyID, or DefaultLanguage.
|
|
func LoadLanguage(ctx context.Context, pool *pgxpool.Pool, companyID uuid.UUID) string {
|
|
if pool == nil {
|
|
return DefaultLanguage
|
|
}
|
|
var raw string
|
|
err := pool.QueryRow(ctx, `SELECT COALESCE(language, '') FROM companies WHERE id = $1`, companyID).Scan(&raw)
|
|
if err != nil {
|
|
return DefaultLanguage
|
|
}
|
|
code, err := ParseLanguage(raw, true)
|
|
if err != nil {
|
|
return DefaultLanguage
|
|
}
|
|
return code
|
|
}
|