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:
parent
6cdc78ed4f
commit
742e0c6a72
2 changed files with 99 additions and 10 deletions
|
@ -5,6 +5,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json" //nolint:depguard
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
@ -26,6 +27,8 @@ var (
|
||||||
|
|
||||||
// Matches href="", href="#", href="%s", href="#%s", href="%[1]s" and href="#%[1]s".
|
// Matches href="", href="#", href="%s", href="#%s", href="%[1]s" and href="#%[1]s".
|
||||||
placeHolderRegex = regexp.MustCompile(`href="#?(%s|%\[\d\]s)?"`)
|
placeHolderRegex = regexp.MustCompile(`href="#?(%s|%\[\d\]s)?"`)
|
||||||
|
|
||||||
|
dmp = diffmatchpatch.New()
|
||||||
)
|
)
|
||||||
|
|
||||||
func initBlueMondayPolicy() {
|
func initBlueMondayPolicy() {
|
||||||
|
@ -79,6 +82,21 @@ func preprocessTranslationValue(value string) string {
|
||||||
return value
|
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 {
|
func checkLocaleContent(localeContent []byte) []string {
|
||||||
// Same configuration as Forgejo uses.
|
// Same configuration as Forgejo uses.
|
||||||
cfg := ini.Empty(ini.LoadOptions{
|
cfg := ini.Empty(ini.LoadOptions{
|
||||||
|
@ -90,9 +108,7 @@ func checkLocaleContent(localeContent []byte) []string {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dmp := diffmatchpatch.New()
|
|
||||||
errors := []string{}
|
errors := []string{}
|
||||||
|
|
||||||
for _, section := range cfg.Sections() {
|
for _, section := range cfg.Sections() {
|
||||||
for _, key := range section.Keys() {
|
for _, key := range section.Keys() {
|
||||||
var trKey string
|
var trKey string
|
||||||
|
@ -102,16 +118,27 @@ func checkLocaleContent(localeContent []byte) []string {
|
||||||
trKey = section.Name() + "." + key.Name()
|
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 {
|
func checkLocaleNextContent(data map[string]any, trKey ...string) []string {
|
||||||
// Create a nice diff of the difference.
|
errors := []string{}
|
||||||
diffs := dmp.DiffMain(keyValue, html.UnescapeString(policy.Sanitize(keyValue)), false)
|
for key, value := range data {
|
||||||
diffs = dmp.DiffCleanupSemantic(diffs)
|
currentKey := key
|
||||||
diffs = dmp.DiffCleanupEfficiency(diffs)
|
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
|
return errors
|
||||||
|
@ -127,6 +154,7 @@ func main() {
|
||||||
panic(err)
|
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") }) {
|
if !slices.ContainsFunc(localeFiles, func(e fs.DirEntry) bool { return strings.HasSuffix(e.Name(), ".ini") }) {
|
||||||
fmt.Println("No locale files found")
|
fmt.Println("No locale files found")
|
||||||
os.Exit(1)
|
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)
|
os.Exit(exitCode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,3 +63,32 @@ func TestLocalizationPolicy(t *testing.T) {
|
||||||
assert.EqualValues(t, []string{"key: و\x1b[31m \x1b[0m\x1b[32m\u00a0\x1b[0m"}, checkLocaleContent([]byte(`key = و `)))
|
assert.EqualValues(t, []string{"key: و\x1b[31m \x1b[0m\x1b[32m\u00a0\x1b[0m"}, checkLocaleContent([]byte(`key = و `)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>"`,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue