From dd136858f1ea40ad3c94191d647487fa4f31926c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 18 Oct 2024 20:33:49 +0200 Subject: Adding upstream version 9.0.0. Signed-off-by: Daniel Baumann --- modules/translation/i18n/i18n_test.go | 204 ++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 modules/translation/i18n/i18n_test.go (limited to 'modules/translation/i18n/i18n_test.go') diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go new file mode 100644 index 0000000..244f6ff --- /dev/null +++ b/modules/translation/i18n/i18n_test.go @@ -0,0 +1,204 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package i18n + +import ( + "html/template" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLocaleStore(t *testing.T) { + testData1 := []byte(` +.dot.name = Dot Name +fmt = %[1]s %[2]s + +[section] +sub = Sub String +mixed = test value; %s +`) + + testData2 := []byte(` +fmt = %[2]s %[1]s + +[section] +sub = Changed Sub String +`) + + ls := NewLocaleStore() + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, nil)) + require.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2, nil)) + ls.SetDefaultLang("lang1") + + lang1, _ := ls.Locale("lang1") + lang2, _ := ls.Locale("lang2") + + result := lang1.TrString("fmt", "a", "b") + assert.Equal(t, "a b", result) + + result = lang2.TrString("fmt", "a", "b") + assert.Equal(t, "b a", result) + + result = lang1.TrString("section.sub") + assert.Equal(t, "Sub String", result) + + result = lang2.TrString("section.sub") + assert.Equal(t, "Changed Sub String", result) + + langNone, _ := ls.Locale("none") + result = langNone.TrString(".dot.name") + assert.Equal(t, "Dot Name", result) + + result2 := lang2.TrHTML("section.mixed", "a&b") + assert.EqualValues(t, `test value; a&b`, result2) + + langs, descs := ls.ListLangNameDesc() + assert.ElementsMatch(t, []string{"lang1", "lang2"}, langs) + assert.ElementsMatch(t, []string{"Lang1", "Lang2"}, descs) + + found := lang1.HasKey("no-such") + assert.False(t, found) + require.NoError(t, ls.Close()) +} + +func TestLocaleStoreMoreSource(t *testing.T) { + testData1 := []byte(` +a=11 +b=12 +`) + + testData2 := []byte(` +b=21 +c=22 +`) + + ls := NewLocaleStore() + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, testData2)) + lang1, _ := ls.Locale("lang1") + assert.Equal(t, "11", lang1.TrString("a")) + assert.Equal(t, "21", lang1.TrString("b")) + assert.Equal(t, "22", lang1.TrString("c")) +} + +type stringerPointerReceiver struct { + s string +} + +func (s *stringerPointerReceiver) String() string { + return s.s +} + +type stringerStructReceiver struct { + s string +} + +func (s stringerStructReceiver) String() string { + return s.s +} + +type errorStructReceiver struct { + s string +} + +func (e errorStructReceiver) Error() string { + return e.s +} + +type errorPointerReceiver struct { + s string +} + +func (e *errorPointerReceiver) Error() string { + return e.s +} + +func TestLocaleWithTemplate(t *testing.T) { + ls := NewLocaleStore() + require.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", []byte(`key=%s`), nil)) + lang1, _ := ls.Locale("lang1") + + tmpl := template.New("test").Funcs(template.FuncMap{"tr": lang1.TrHTML}) + tmpl = template.Must(tmpl.Parse(`{{tr "key" .var}}`)) + + cases := []struct { + in any + want string + }{ + {"", "<str>"}, + {[]byte(""), "[60 98 121 116 101 115 62]"}, + {template.HTML(""), ""}, + {stringerPointerReceiver{""}, "{<stringerPointerReceiver>}"}, + {&stringerPointerReceiver{""}, "<stringerPointerReceiver ptr>"}, + {stringerStructReceiver{""}, "<stringerStructReceiver>"}, + {&stringerStructReceiver{""}, "<stringerStructReceiver ptr>"}, + {errorStructReceiver{""}, "<errorStructReceiver>"}, + {&errorStructReceiver{""}, "<errorStructReceiver ptr>"}, + {errorPointerReceiver{""}, "{<errorPointerReceiver>}"}, + {&errorPointerReceiver{""}, "<errorPointerReceiver ptr>"}, + } + + buf := &strings.Builder{} + for _, c := range cases { + buf.Reset() + require.NoError(t, tmpl.Execute(buf, map[string]any{"var": c.in})) + assert.Equal(t, c.want, buf.String()) + } +} + +func TestLocaleStoreQuirks(t *testing.T) { + const nl = "\n" + q := func(q1, s string, q2 ...string) string { + return q1 + s + strings.Join(q2, "") + } + testDataList := []struct { + in string + out string + hint string + }{ + {` xx`, `xx`, "simple, no quote"}, + {`" xx"`, ` xx`, "simple, double-quote"}, + {`' xx'`, ` xx`, "simple, single-quote"}, + {"` xx`", ` xx`, "simple, back-quote"}, + + {`x\"y`, `x\"y`, "no unescape, simple"}, + {q(`"`, `x\"y`, `"`), `"x\"y"`, "unescape, double-quote"}, + {q(`'`, `x\"y`, `'`), `x\"y`, "no unescape, single-quote"}, + {q("`", `x\"y`, "`"), `x\"y`, "no unescape, back-quote"}, + + {q(`"`, `x\"y`) + nl + "b=", `"x\"y`, "half open, double-quote"}, + {q(`'`, `x\"y`) + nl + "b=", `'x\"y`, "half open, single-quote"}, + {q("`", `x\"y`) + nl + "b=`", `x\"y` + nl + "b=", "half open, back-quote, multi-line"}, + + {`x ; y`, `x ; y`, "inline comment (;)"}, + {`x # y`, `x # y`, "inline comment (#)"}, + {`x \; y`, `x ; y`, `inline comment (\;)`}, + {`x \# y`, `x # y`, `inline comment (\#)`}, + } + + for _, testData := range testDataList { + ls := NewLocaleStore() + err := ls.AddLocaleByIni("lang1", "Lang1", []byte("a="+testData.in), nil) + lang1, _ := ls.Locale("lang1") + require.NoError(t, err, testData.hint) + assert.Equal(t, testData.out, lang1.TrString("a"), testData.hint) + require.NoError(t, ls.Close()) + } + + // TODO: Crowdin needs the strings to be quoted correctly and doesn't like incomplete quotes + // and Crowdin always outputs quoted strings if there are quotes in the strings. + // So, Gitea's `key="quoted" unquoted` content shouldn't be used on Crowdin directly, + // it should be converted to `key="\"quoted\" unquoted"` first. + // TODO: We can not use UnescapeValueDoubleQuotes=true, because there are a lot of back-quotes in en-US.ini, + // then Crowdin will output: + // > key = "`x \" y`" + // Then Gitea will read a string with back-quotes, which is incorrect. + // TODO: Crowdin might generate multi-line strings, quoted by double-quote, it's not supported by LocaleStore + // LocaleStore uses back-quote for multi-line strings, it's not supported by Crowdin. + // TODO: Crowdin doesn't support back-quote as string quoter, it mainly uses double-quote + // so, the following line will be parsed as: value="`first", comment="second`" on Crowdin + // > a = `first; second` +} -- cgit v1.2.3