[agent/codex:gpt-5.4-mini] Read ~/spec/code/core/go/i18n/RFC.md fully. Find ONE feature... #17

Merged
Virgil merged 1 commit from agent/read---spec-code-core-go-i18n-rfc-md-ful into dev 2026-04-01 06:08:09 +00:00
2 changed files with 142 additions and 1 deletions

View file

@ -393,7 +393,9 @@ func applyRegularPlural(noun string) string {
return noun + "s"
}
// Article returns the appropriate indefinite article ("a" or "an").
// Article returns the appropriate article for the current language.
// English falls back to phonetic "a"/"an" heuristics. Locale grammar data
// can override this with language-specific article forms.
//
// Article("file") // "a"
// Article("error") // "an"
@ -404,6 +406,9 @@ func Article(word string) string {
return ""
}
lower := core.Lower(core.Trim(word))
if article, ok := articleForCurrentLanguage(lower, word); ok {
return article
}
for key := range consonantSounds {
if core.HasPrefix(lower, key) {
return "a"
@ -420,6 +425,107 @@ func Article(word string) string {
return "a"
}
func articleForCurrentLanguage(lowerWord, originalWord string) (string, bool) {
lang := currentLangForGrammar()
data := GetGrammarData(lang)
if data == nil {
return "", false
}
if article, ok := articleByGender(data, lowerWord, originalWord, lang); ok {
return article, true
}
if article, ok := articleFromGrammarForms(data, originalWord); ok {
return article, true
}
return "", false
}
func articleByGender(data *GrammarData, lowerWord, originalWord, lang string) (string, bool) {
if len(data.Articles.ByGender) == 0 {
return "", false
}
forms, ok := data.Nouns[lowerWord]
if !ok || forms.Gender == "" {
return "", false
}
article, ok := data.Articles.ByGender[forms.Gender]
if !ok || article == "" {
return "", false
}
return maybeElideArticle(article, originalWord, lang), true
}
func articleFromGrammarForms(data *GrammarData, word string) (string, bool) {
if data.Articles.IndefiniteDefault == "" && data.Articles.IndefiniteVowel == "" {
return "", false
}
if usesVowelSoundArticle(word) && data.Articles.IndefiniteVowel != "" {
return data.Articles.IndefiniteVowel, true
}
if data.Articles.IndefiniteDefault != "" {
return data.Articles.IndefiniteDefault, true
}
if data.Articles.IndefiniteVowel != "" {
return data.Articles.IndefiniteVowel, true
}
return "", false
}
func maybeElideArticle(article, word, lang string) string {
if !isFrenchLanguage(lang) {
return article
}
switch core.Lower(article) {
case "le", "la", "de", "je", "me", "te", "se", "ne":
if startsWithVowelSound(word) {
return "l'"
}
}
return article
}
func usesVowelSoundArticle(word string) bool {
lower := core.Lower(core.Trim(word))
if lower == "" {
return false
}
for key := range consonantSounds {
if core.HasPrefix(lower, key) {
return false
}
}
for key := range vowelSounds {
if core.HasPrefix(lower, key) {
return true
}
}
for _, r := range lower {
return isVowel(r)
}
return false
}
func startsWithVowelSound(word string) bool {
lower := core.Lower(core.Trim(word))
if lower == "" {
return false
}
r := []rune(lower)
switch r[0] {
case 'a', 'e', 'i', 'o', 'u', 'y',
'à', 'â', 'ä', 'æ', 'é', 'è', 'ê', 'ë',
'î', 'ï', 'ô', 'ö', 'ù', 'û', 'ü', 'œ', 'h':
return true
}
return false
}
func isFrenchLanguage(lang string) bool {
lang = core.Lower(lang)
return lang == "fr" || core.HasPrefix(lang, "fr-")
}
func isVowel(r rune) bool {
switch unicode.ToLower(r) {
case 'a', 'e', 'i', 'o', 'u':

View file

@ -279,6 +279,41 @@ func TestArticle(t *testing.T) {
}
}
func TestArticleFrenchLocale(t *testing.T) {
prev := Default()
svc, err := New()
if err != nil {
t.Fatalf("New() failed: %v", err)
}
SetDefault(svc)
t.Cleanup(func() {
SetDefault(prev)
})
if err := SetLanguage("fr"); err != nil {
t.Fatalf("SetLanguage(fr) failed: %v", err)
}
tests := []struct {
word string
want string
}{
{"branche", "la"},
{"enfant", "l'"},
{"fichier", "le"},
{"inconnu", "un"},
}
for _, tt := range tests {
t.Run(tt.word, func(t *testing.T) {
got := Article(tt.word)
if got != tt.want {
t.Errorf("Article(%q) = %q, want %q", tt.word, got, tt.want)
}
})
}
}
func TestTitle(t *testing.T) {
tests := []struct {
input string