feat(04-02): select CLDR and Laravel plural forms with PHP parameters
- Use go-i18n only to pick a category label, then substitute :name/:Name/:NAME
- Treat YAML maps as plurals when all keys are locale CLDR categories
- Parse pipe strings with {n} and [a,b]/[a,*] conditions at catalog load
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -96,3 +96,96 @@ func (p *langPlugin) Requires() []string { return nil }
|
||||
func (p *langPlugin) Register(*backpack.App) error { return nil }
|
||||
func (p *langPlugin) Boot(*backpack.App) error { return nil }
|
||||
func (p *langPlugin) LangFS() fs.FS { return p.fsys }
|
||||
|
||||
func TestPluralSmoke(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fsys := fstest.MapFS{
|
||||
"lang/en/lang.yaml": {Data: []byte("" +
|
||||
"albums:\n one: \":count album\"\n other: \":count albums\"\n" +
|
||||
"items: \"{0} No items|{1} One item|[2,*] :count items\"\n" +
|
||||
"helloName: \"hi :name :Name :NAME\"\n" +
|
||||
"raw:\n other: \"{{.Count}} bottles\"\n")},
|
||||
"lang/pl/lang.yaml": {Data: []byte("" +
|
||||
"albums:\n one: \":count album\"\n few: \":count albumy\"\n many: \":count albumów\"\n other: \":count albumu\"\n" +
|
||||
"posts: \":count wpis|:count wpisy|:count wpisów|:count wpisu\"\n")},
|
||||
}
|
||||
cat := NewCatalog()
|
||||
if err := cat.Load("golem15.hello", fsys); err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
tr := NewTranslator(cat, Options{})
|
||||
|
||||
pl := map[any]string{1: "1 album", 2: "2 albumy", 5: "5 albumów", 22: "22 albumy", 0: "0 albumów", 1.5: "1.5 albumu"}
|
||||
for n, want := range pl {
|
||||
if got := tr.ChoiceIn("pl", "golem15.hello::lang.albums", n, nil); got != want {
|
||||
t.Fatalf("pl albums %v = %q, want %q", n, got, want)
|
||||
}
|
||||
}
|
||||
en := map[any]string{1: "1 album", 2: "2 albums", 0: "0 albums", 1.5: "1.5 albums"}
|
||||
for n, want := range en {
|
||||
if got := tr.ChoiceIn("en", "golem15.hello::lang.albums", n, nil); got != want {
|
||||
t.Fatalf("en albums %v = %q, want %q", n, got, want)
|
||||
}
|
||||
}
|
||||
if got := tr.ChoiceIn("pl", "golem15.hello::lang.posts", 1, nil); got != "1 wpis" {
|
||||
t.Fatalf("pl posts 1 = %q", got)
|
||||
}
|
||||
if got := tr.ChoiceIn("pl", "golem15.hello::lang.posts", 2, nil); got != "2 wpisy" {
|
||||
t.Fatalf("pl posts 2 = %q", got)
|
||||
}
|
||||
if got := tr.ChoiceIn("pl", "golem15.hello::lang.posts", 5, nil); got != "5 wpisów" {
|
||||
t.Fatalf("pl posts 5 = %q", got)
|
||||
}
|
||||
if got := tr.ChoiceIn("pl", "golem15.hello::lang.posts", 22, nil); got != "22 wpisy" {
|
||||
t.Fatalf("pl posts 22 = %q", got)
|
||||
}
|
||||
if got := tr.ChoiceIn("en", "golem15.hello::lang.items", 0, nil); got != "No items" {
|
||||
t.Fatalf("items 0 = %q", got)
|
||||
}
|
||||
if got := tr.ChoiceIn("en", "golem15.hello::lang.items", 1, nil); got != "One item" {
|
||||
t.Fatalf("items 1 = %q", got)
|
||||
}
|
||||
if got := tr.ChoiceIn("en", "golem15.hello::lang.items", 5, nil); got != "5 items" {
|
||||
t.Fatalf("items 5 = %q", got)
|
||||
}
|
||||
if got := tr.ChoiceIn("en", "golem15.hello::lang.helloName", 1, map[string]string{"name": "alice"}); got != "hi alice Alice ALICE" {
|
||||
t.Fatalf("params = %q", got)
|
||||
}
|
||||
if got := tr.ChoiceIn("en", "golem15.hello::lang.raw", 2, nil); got != "{{.Count}} bottles" {
|
||||
t.Fatalf("go-i18n must not evaluate YAML as a template, got %q", got)
|
||||
}
|
||||
|
||||
t.Run("invalid category for locale", func(t *testing.T) {
|
||||
err := NewCatalog().Load("golem15.hello", fstest.MapFS{
|
||||
"lang/en/lang.yaml": {Data: []byte("albums:\n few: x\n other: y\n")},
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "invalid plural category") {
|
||||
t.Fatalf("want invalid category, got %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("missing other", func(t *testing.T) {
|
||||
err := NewCatalog().Load("golem15.hello", fstest.MapFS{
|
||||
"lang/en/lang.yaml": {Data: []byte("albums:\n one: x\n")},
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "requires other") {
|
||||
t.Fatalf("want missing other, got %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("malformed pipe", func(t *testing.T) {
|
||||
err := NewCatalog().Load("golem15.hello", fstest.MapFS{
|
||||
"lang/en/lang.yaml": {Data: []byte("items: \"{x} nope|{1} one\"\n")},
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "malformed pipe") {
|
||||
t.Fatalf("want malformed pipe, got %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("incomplete pipe", func(t *testing.T) {
|
||||
err := NewCatalog().Load("golem15.hello", fstest.MapFS{
|
||||
"lang/en/lang.yaml": {Data: []byte("items: \":count post|:count posts|:count extra\"\n")},
|
||||
})
|
||||
if err == nil || !strings.Contains(err.Error(), "incomplete pipe") {
|
||||
t.Fatalf("want incomplete pipe, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user