mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-09 14:28:01 -05:00
• Add Brazilian Portuguese (pt-BR) translation file • Add European Portuguese (pt-PT) translation file • Implement BCP 47 locale normalization system • Create fallback chain for language variants • Add default variant mapping for Portuguese • Update help text to show variant examples • Add comprehensive test suite for variants • Create documentation for i18n variant architecture
95 lines
2.7 KiB
Go
95 lines
2.7 KiB
Go
package i18n
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
|
|
"golang.org/x/text/language"
|
|
)
|
|
|
|
// detectSystemLocale detects the system locale using standard Unix environment variables.
|
|
// Follows the POSIX priority order for locale environment variables:
|
|
// 1. LC_ALL (highest priority - overrides all others)
|
|
// 2. LC_MESSAGES (for messages specifically)
|
|
// 3. LANG (general locale setting)
|
|
// 4. Returns empty string if none are set or valid
|
|
//
|
|
// This implementation follows POSIX standards and Unix best practices for locale detection.
|
|
func detectSystemLocale() string {
|
|
// Check environment variables in priority order
|
|
envVars := []string{"LC_ALL", "LC_MESSAGES", "LANG"}
|
|
|
|
for _, envVar := range envVars {
|
|
if value := os.Getenv(envVar); value != "" {
|
|
locale := normalizeLocale(value)
|
|
if locale != "" && isValidLocale(locale) {
|
|
return locale
|
|
}
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// normalizeLocale converts various locale formats to BCP 47 language tags.
|
|
// Examples:
|
|
// - "en_US.UTF-8" -> "en-US"
|
|
// - "fr_FR@euro" -> "fr-FR"
|
|
// - "zh_CN.GB2312" -> "zh-CN"
|
|
// - "C" or "POSIX" -> "" (invalid, falls back to default)
|
|
func normalizeLocale(locale string) string {
|
|
// Handle special cases
|
|
if locale == "C" || locale == "POSIX" || locale == "" {
|
|
return ""
|
|
}
|
|
|
|
// Remove encoding and modifiers
|
|
// Examples: en_US.UTF-8@euro -> en_US
|
|
locale = strings.Split(locale, ".")[0] // Remove encoding (.UTF-8)
|
|
locale = strings.Split(locale, "@")[0] // Remove modifiers (@euro)
|
|
|
|
// Convert underscore to hyphen for BCP 47 compliance
|
|
// en_US -> en-US
|
|
locale = strings.ReplaceAll(locale, "_", "-")
|
|
|
|
// Ensure proper BCP 47 casing: language-REGION
|
|
parts := strings.Split(locale, "-")
|
|
if len(parts) >= 2 {
|
|
// Lowercase language, uppercase region
|
|
parts[0] = strings.ToLower(parts[0])
|
|
parts[1] = strings.ToUpper(parts[1])
|
|
locale = strings.Join(parts[:2], "-") // Only keep language-REGION
|
|
} else if len(parts) == 1 {
|
|
// Language only, lowercase it
|
|
locale = strings.ToLower(parts[0])
|
|
}
|
|
|
|
return locale
|
|
}
|
|
|
|
// isValidLocale checks if a locale string can be parsed as a valid language tag.
|
|
func isValidLocale(locale string) bool {
|
|
if locale == "" {
|
|
return false
|
|
}
|
|
|
|
// Use golang.org/x/text/language to validate
|
|
_, err := language.Parse(locale)
|
|
return err == nil
|
|
}
|
|
|
|
// getPreferredLocale returns the best locale to use based on user preferences.
|
|
// Priority order:
|
|
// 1. Explicit language flag (if provided)
|
|
// 2. System environment variables (LC_ALL, LC_MESSAGES, LANG)
|
|
// 3. Default fallback (empty string, which triggers "en" in Init)
|
|
func getPreferredLocale(explicitLang string) string {
|
|
// If explicitly set via flag, use that
|
|
if explicitLang != "" {
|
|
return explicitLang
|
|
}
|
|
|
|
// Otherwise try to detect from system environment
|
|
return detectSystemLocale()
|
|
}
|