[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/go/i18n/RFC.md fully. Find features de... #131
3 changed files with 135 additions and 21 deletions
58
hooks.go
58
hooks.go
|
|
@ -18,6 +18,12 @@ type missingKeyHandlersState struct {
|
|||
type localeRegistration struct {
|
||||
fsys fs.FS
|
||||
dir string
|
||||
id int
|
||||
}
|
||||
|
||||
type localeProviderRegistration struct {
|
||||
provider LocaleProvider
|
||||
id int
|
||||
}
|
||||
|
||||
// LocaleProvider supplies one or more locale filesystems to the default service.
|
||||
|
|
@ -26,10 +32,12 @@ type LocaleProvider interface {
|
|||
}
|
||||
|
||||
var (
|
||||
registeredLocales []localeRegistration
|
||||
registeredLocaleProviders []LocaleProvider
|
||||
registeredLocalesMu sync.Mutex
|
||||
localesLoaded bool
|
||||
registeredLocales []localeRegistration
|
||||
registeredLocaleProviders []localeProviderRegistration
|
||||
registeredLocalesMu sync.Mutex
|
||||
localesLoaded bool
|
||||
nextLocaleRegistrationID int
|
||||
nextLocaleProviderID int
|
||||
)
|
||||
|
||||
// RegisterLocales registers a filesystem containing locale files.
|
||||
|
|
@ -42,12 +50,18 @@ var (
|
|||
// i18n.RegisterLocales(localeFS, "locales")
|
||||
// }
|
||||
func RegisterLocales(fsys fs.FS, dir string) {
|
||||
reg := localeRegistration{fsys: fsys, dir: dir}
|
||||
registeredLocalesMu.Lock()
|
||||
defer registeredLocalesMu.Unlock()
|
||||
registeredLocales = append(registeredLocales, localeRegistration{fsys: fsys, dir: dir})
|
||||
if svc := defaultService.Load(); svc != nil {
|
||||
nextLocaleRegistrationID++
|
||||
reg.id = nextLocaleRegistrationID
|
||||
registeredLocales = append(registeredLocales, reg)
|
||||
svc := defaultService.Load()
|
||||
registeredLocalesMu.Unlock()
|
||||
if svc != nil {
|
||||
if err := svc.LoadFS(fsys, dir); err != nil {
|
||||
log.Printf("i18n: RegisterLocales failed to load %q: %v", dir, err)
|
||||
} else {
|
||||
svc.markLocaleRegistrationLoaded(reg.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,26 +73,41 @@ func RegisterLocaleProvider(provider LocaleProvider) {
|
|||
if provider == nil {
|
||||
return
|
||||
}
|
||||
reg := localeProviderRegistration{provider: provider}
|
||||
registeredLocalesMu.Lock()
|
||||
registeredLocaleProviders = append(registeredLocaleProviders, provider)
|
||||
nextLocaleProviderID++
|
||||
reg.id = nextLocaleProviderID
|
||||
registeredLocaleProviders = append(registeredLocaleProviders, reg)
|
||||
svc := defaultService.Load()
|
||||
registeredLocalesMu.Unlock()
|
||||
if svc := defaultService.Load(); svc != nil {
|
||||
loadLocaleProvider(svc, provider)
|
||||
if svc != nil {
|
||||
loadLocaleProvider(svc, reg)
|
||||
}
|
||||
}
|
||||
|
||||
func loadRegisteredLocales(svc *Service) {
|
||||
if svc == nil {
|
||||
return
|
||||
}
|
||||
registeredLocalesMu.Lock()
|
||||
locales := append([]localeRegistration(nil), registeredLocales...)
|
||||
providers := append([]LocaleProvider(nil), registeredLocaleProviders...)
|
||||
providers := append([]localeProviderRegistration(nil), registeredLocaleProviders...)
|
||||
registeredLocalesMu.Unlock()
|
||||
|
||||
for _, reg := range locales {
|
||||
if svc != nil && svc.hasLocaleRegistrationLoaded(reg.id) {
|
||||
continue
|
||||
}
|
||||
if err := svc.LoadFS(reg.fsys, reg.dir); err != nil {
|
||||
log.Printf("i18n: loadRegisteredLocales failed to load %q: %v", reg.dir, err)
|
||||
continue
|
||||
}
|
||||
svc.markLocaleRegistrationLoaded(reg.id)
|
||||
}
|
||||
for _, provider := range providers {
|
||||
if svc != nil && svc.hasLocaleProviderLoaded(provider.id) {
|
||||
continue
|
||||
}
|
||||
loadLocaleProvider(svc, provider)
|
||||
}
|
||||
|
||||
|
|
@ -87,15 +116,16 @@ func loadRegisteredLocales(svc *Service) {
|
|||
registeredLocalesMu.Unlock()
|
||||
}
|
||||
|
||||
func loadLocaleProvider(svc *Service, provider LocaleProvider) {
|
||||
if svc == nil || provider == nil {
|
||||
func loadLocaleProvider(svc *Service, provider localeProviderRegistration) {
|
||||
if svc == nil || provider.provider == nil {
|
||||
return
|
||||
}
|
||||
for _, src := range provider.LocaleSources() {
|
||||
for _, src := range provider.provider.LocaleSources() {
|
||||
if err := svc.LoadFS(src.FS, src.Dir); err != nil {
|
||||
log.Printf("i18n: loadLocaleProvider failed to load %q: %v", src.Dir, err)
|
||||
}
|
||||
}
|
||||
svc.markLocaleProviderLoaded(provider.id)
|
||||
}
|
||||
|
||||
// OnMissingKey registers a handler for missing translation keys.
|
||||
|
|
|
|||
|
|
@ -187,6 +187,43 @@ func TestSetDefault_Good_LoadsQueuedRegisteredLocales(t *testing.T) {
|
|||
assert.Equal(t, "loaded via setdefault", got)
|
||||
}
|
||||
|
||||
func TestSetDefault_Good_LoadsRegisteredLocalesIntoFreshService(t *testing.T) {
|
||||
registeredLocalesMu.Lock()
|
||||
savedLocales := registeredLocales
|
||||
savedProviders := registeredLocaleProviders
|
||||
savedLoaded := localesLoaded
|
||||
registeredLocales = nil
|
||||
registeredLocaleProviders = nil
|
||||
localesLoaded = false
|
||||
registeredLocalesMu.Unlock()
|
||||
defer func() {
|
||||
registeredLocalesMu.Lock()
|
||||
registeredLocales = savedLocales
|
||||
registeredLocaleProviders = savedProviders
|
||||
localesLoaded = savedLoaded
|
||||
registeredLocalesMu.Unlock()
|
||||
}()
|
||||
|
||||
fs := fstest.MapFS{
|
||||
"locales/en.json": &fstest.MapFile{
|
||||
Data: []byte(`{"fresh.registration": "fresh value"}`),
|
||||
},
|
||||
}
|
||||
RegisterLocales(fs, "locales")
|
||||
|
||||
first, err := New()
|
||||
require.NoError(t, err)
|
||||
SetDefault(first)
|
||||
require.Equal(t, "fresh value", first.T("fresh.registration"))
|
||||
|
||||
second, err := New()
|
||||
require.NoError(t, err)
|
||||
SetDefault(second)
|
||||
|
||||
got := second.T("fresh.registration")
|
||||
assert.Equal(t, "fresh value", got)
|
||||
}
|
||||
|
||||
func TestInit_LoadsRegisteredLocales(t *testing.T) {
|
||||
// Save and restore global service state.
|
||||
registeredLocalesMu.Lock()
|
||||
|
|
|
|||
61
service.go
61
service.go
|
|
@ -30,6 +30,8 @@ type Service struct {
|
|||
formality Formality
|
||||
location string
|
||||
handlers []KeyHandler
|
||||
loadedLocales map[int]struct{}
|
||||
loadedProviders map[int]struct{}
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
|
|
@ -102,10 +104,12 @@ func NewWithFS(fsys fs.FS, dir string, opts ...Option) (*Service, error) {
|
|||
// NewWithLoader creates a new i18n service with a custom loader.
|
||||
func NewWithLoader(loader Loader, opts ...Option) (*Service, error) {
|
||||
s := &Service{
|
||||
loader: loader,
|
||||
messages: make(map[string]map[string]Message),
|
||||
fallbackLang: "en",
|
||||
handlers: DefaultHandlers(),
|
||||
loader: loader,
|
||||
messages: make(map[string]map[string]Message),
|
||||
fallbackLang: "en",
|
||||
handlers: DefaultHandlers(),
|
||||
loadedLocales: make(map[int]struct{}),
|
||||
loadedProviders: make(map[int]struct{}),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
|
|
@ -192,10 +196,9 @@ func SetDefault(s *Service) {
|
|||
return
|
||||
}
|
||||
registeredLocalesMu.Lock()
|
||||
loaded := localesLoaded
|
||||
hasRegistrations := len(registeredLocales) > 0
|
||||
hasRegistrations := len(registeredLocales) > 0 || len(registeredLocaleProviders) > 0
|
||||
registeredLocalesMu.Unlock()
|
||||
if !loaded && hasRegistrations {
|
||||
if hasRegistrations {
|
||||
loadRegisteredLocales(s)
|
||||
}
|
||||
}
|
||||
|
|
@ -833,6 +836,50 @@ func (s *Service) AddLoader(loader Loader) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) hasLocaleRegistrationLoaded(id int) bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
if len(s.loadedLocales) == 0 {
|
||||
return false
|
||||
}
|
||||
_, ok := s.loadedLocales[id]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *Service) markLocaleRegistrationLoaded(id int) {
|
||||
if id == 0 || s == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
if s.loadedLocales == nil {
|
||||
s.loadedLocales = make(map[int]struct{})
|
||||
}
|
||||
s.loadedLocales[id] = struct{}{}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Service) hasLocaleProviderLoaded(id int) bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
if len(s.loadedProviders) == 0 {
|
||||
return false
|
||||
}
|
||||
_, ok := s.loadedProviders[id]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *Service) markLocaleProviderLoaded(id int) {
|
||||
if id == 0 || s == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
if s.loadedProviders == nil {
|
||||
s.loadedProviders = make(map[int]struct{})
|
||||
}
|
||||
s.loadedProviders[id] = struct{}{}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func translateOK(messageID, value string) bool {
|
||||
if value == "" {
|
||||
return false
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue