0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo.git synced 2025-02-14 10:08:30 -05:00

chore: teach lint-locale about locale_next (#6800)

- Ref: forgejo/forgejo#6203 & forgejo/forgejo#5703
- Moved code around to be reusable, otherwise an straightforward implementation.
- Added unit test.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6800
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
This commit is contained in:
Gusted 2025-02-06 11:16:19 +00:00 committed by Gusted
parent 6cdc78ed4f
commit 742e0c6a72
2 changed files with 99 additions and 10 deletions

View file

@ -5,6 +5,7 @@
package main
import (
"encoding/json" //nolint:depguard
"fmt"
"html"
"io/fs"
@ -26,6 +27,8 @@ var (
// Matches href="", href="#", href="%s", href="#%s", href="%[1]s" and href="#%[1]s".
placeHolderRegex = regexp.MustCompile(`href="#?(%s|%\[\d\]s)?"`)
dmp = diffmatchpatch.New()
)
func initBlueMondayPolicy() {
@ -79,6 +82,21 @@ func preprocessTranslationValue(value string) string {
return value
}
func checkValue(trKey, value string) []string {
keyValue := preprocessTranslationValue(value)
if html.UnescapeString(policy.Sanitize(keyValue)) == keyValue {
return nil
}
// Create a nice diff of the difference.
diffs := dmp.DiffMain(keyValue, html.UnescapeString(policy.Sanitize(keyValue)), false)
diffs = dmp.DiffCleanupSemantic(diffs)
diffs = dmp.DiffCleanupEfficiency(diffs)
return []string{trKey + ": " + dmp.DiffPrettyText(diffs)}
}
func checkLocaleContent(localeContent []byte) []string {
// Same configuration as Forgejo uses.
cfg := ini.Empty(ini.LoadOptions{
@ -90,9 +108,7 @@ func checkLocaleContent(localeContent []byte) []string {
panic(err)
}
dmp := diffmatchpatch.New()
errors := []string{}
for _, section := range cfg.Sections() {
for _, key := range section.Keys() {
var trKey string
@ -102,16 +118,27 @@ func checkLocaleContent(localeContent []byte) []string {
trKey = section.Name() + "." + key.Name()
}
keyValue := preprocessTranslationValue(key.Value())
errors = append(errors, checkValue(trKey, key.Value())...)
}
}
return errors
}
if html.UnescapeString(policy.Sanitize(keyValue)) != keyValue {
// Create a nice diff of the difference.
diffs := dmp.DiffMain(keyValue, html.UnescapeString(policy.Sanitize(keyValue)), false)
diffs = dmp.DiffCleanupSemantic(diffs)
diffs = dmp.DiffCleanupEfficiency(diffs)
func checkLocaleNextContent(data map[string]any, trKey ...string) []string {
errors := []string{}
for key, value := range data {
currentKey := key
if len(trKey) == 1 {
currentKey = trKey[0] + "." + key
}
errors = append(errors, trKey+": "+dmp.DiffPrettyText(diffs))
}
switch value := value.(type) {
case string:
errors = append(errors, checkValue(currentKey, value)...)
case map[string]any:
errors = append(errors, checkLocaleNextContent(value, currentKey)...)
default:
panic(fmt.Sprintf("Unexpected type during linting locale next: %s - %T", currentKey, value))
}
}
return errors
@ -127,6 +154,7 @@ func main() {
panic(err)
}
// Safety check that we are not reading the wrong directory.
if !slices.ContainsFunc(localeFiles, func(e fs.DirEntry) bool { return strings.HasSuffix(e.Name(), ".ini") }) {
fmt.Println("No locale files found")
os.Exit(1)
@ -151,5 +179,37 @@ func main() {
}
}
// Check the locale next.
localeDir = filepath.Join("options", "locale_next")
localeFiles, err = os.ReadDir(localeDir)
if err != nil {
panic(err)
}
// Safety check that we are not reading the wrong directory.
if !slices.ContainsFunc(localeFiles, func(e fs.DirEntry) bool { return strings.HasSuffix(e.Name(), ".json") }) {
fmt.Println("No locale_next files found")
os.Exit(1)
}
for _, localeFile := range localeFiles {
localeContent, err := os.ReadFile(filepath.Join(localeDir, localeFile.Name()))
if err != nil {
panic(err)
}
var localeData map[string]any
if err := json.Unmarshal(localeContent, &localeData); err != nil {
panic(err)
}
if err := checkLocaleNextContent(localeData); len(err) > 0 {
fmt.Println(localeFile.Name())
fmt.Println(strings.Join(err, "\n"))
fmt.Println()
exitCode = 1
}
}
os.Exit(exitCode)
}

View file

@ -63,3 +63,32 @@ func TestLocalizationPolicy(t *testing.T) {
assert.EqualValues(t, []string{"key: و\x1b[31m&nbsp\x1b[0m\x1b[32m\u00a0\x1b[0m"}, checkLocaleContent([]byte(`key = و&nbsp;`)))
})
}
func TestNextLocalizationPolicy(t *testing.T) {
initBlueMondayPolicy()
initRemoveTags()
t.Run("Nested locales", func(t *testing.T) {
assert.Empty(t, checkLocaleNextContent(map[string]any{
"settings": map[string]any{
"hidden_comment_types_description": `Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "<user> added/removed <label>" comments.`,
},
}))
assert.EqualValues(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent(map[string]any{
"settings": map[string]any{
"hidden_comment_types_description": `"<not-an-allowed-key> <label>"`,
},
}))
})
t.Run("Flat locales", func(t *testing.T) {
assert.Empty(t, checkLocaleNextContent(map[string]any{
"settings.hidden_comment_types_description": `Comment types checked here will not be shown inside issue pages. Checking "Label" for example removes all "<user> added/removed <label>" comments.`,
}))
assert.EqualValues(t, []string{"settings.hidden_comment_types_description: \"\x1b[31m<not-an-allowed-key>\x1b[0m REPLACED-TAG\""}, checkLocaleNextContent(map[string]any{
"settings.hidden_comment_types_description": `"<not-an-allowed-key> <label>"`,
}))
})
}