mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-26 11:04:27 -04:00
Using comment with i18n-check: to match dynamic translation keys
This commit is contained in:
parent
042156847f
commit
c668142c84
@ -114,6 +114,7 @@ func (r *ActionRunner) StatusName() string {
|
||||
}
|
||||
|
||||
func (r *ActionRunner) StatusLocaleName(lang translation.Locale) string {
|
||||
// i18n-check: actions.runners.status.*
|
||||
return lang.TrString("actions.runners.status." + r.StatusName())
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ func (s Status) String() string {
|
||||
|
||||
// LocaleString returns the locale string name of the Status
|
||||
func (s Status) LocaleString(lang translation.Locale) string {
|
||||
// i18n-check: actions.status.*
|
||||
return lang.TrString("actions.status." + s.String())
|
||||
}
|
||||
|
||||
|
@ -206,6 +206,7 @@ func (status *CommitStatus) APIURL(ctx context.Context) string {
|
||||
|
||||
// LocaleString returns the locale string name of the Status
|
||||
func (status *CommitStatus) LocaleString(lang translation.Locale) string {
|
||||
// i18n-check: repo.commitstatus.*
|
||||
return lang.TrString("repo.commitstatus." + status.State.String())
|
||||
}
|
||||
|
||||
|
@ -225,11 +225,13 @@ const (
|
||||
|
||||
// LocaleString returns the locale string name of the role
|
||||
func (r RoleInRepo) LocaleString(lang translation.Locale) string {
|
||||
// i18n-check: repo.issues.role.*
|
||||
return lang.TrString("repo.issues.role." + string(r))
|
||||
}
|
||||
|
||||
// LocaleHelper returns the locale tooltip of the role
|
||||
func (r RoleInRepo) LocaleHelper(lang translation.Locale) string {
|
||||
// i18n-check: repo.issues.role.*_helper
|
||||
return lang.TrString("repo.issues.role." + string(r) + "_helper")
|
||||
}
|
||||
|
||||
|
@ -128,6 +128,7 @@ func BuildComplexityError(locale translation.Locale) template.HTML {
|
||||
buffer.WriteString("<ul>")
|
||||
for _, c := range requiredList {
|
||||
buffer.WriteString("<li>")
|
||||
// i18n-check: form.password_*
|
||||
buffer.WriteString(locale.TrString(c.TrNameOne))
|
||||
buffer.WriteString("</li>")
|
||||
}
|
||||
|
@ -144,6 +144,7 @@ func (l *locale) TrHTML(trKey string, trArgs ...any) template.HTML {
|
||||
args[i] = template.HTMLEscapeString(fmt.Sprint(v))
|
||||
}
|
||||
}
|
||||
// i18n-check: ignore
|
||||
return template.HTML(l.TrString(trKey, args...))
|
||||
}
|
||||
|
||||
|
@ -238,6 +238,7 @@ func (l *locale) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
|
||||
} else if t, ok := cnt.(int64); ok {
|
||||
c = t
|
||||
} else {
|
||||
// i18n-check: ignore
|
||||
return l.Tr(keyN, args...)
|
||||
}
|
||||
|
||||
|
@ -104,8 +104,10 @@ func Validate(errs binding.Errors, data map[string]any, f Form, l translation.Lo
|
||||
|
||||
trName := field.Tag.Get("locale")
|
||||
if len(trName) == 0 {
|
||||
// i18n-check: form.*
|
||||
trName = l.TrString("form." + field.Name)
|
||||
} else {
|
||||
// i18n-check: ignore
|
||||
trName = l.TrString(trName)
|
||||
}
|
||||
|
||||
|
@ -180,6 +180,7 @@ func DashboardPost(ctx *context.Context) {
|
||||
task := cron.GetTask(form.Op)
|
||||
if task != nil {
|
||||
go task.RunWithUser(ctx.Doer, nil)
|
||||
// i18n-check: admin.dashboard.*
|
||||
ctx.Flash.Success(ctx.Tr("admin.dashboard.task.started", ctx.Tr("admin.dashboard."+form.Op)))
|
||||
} else {
|
||||
ctx.Flash.Error(ctx.Tr("admin.dashboard.task.unknown", form.Op))
|
||||
|
@ -847,6 +847,7 @@ func Run(ctx *context_module.Context) {
|
||||
})
|
||||
if err != nil {
|
||||
if errLocale := util.ErrorAsLocale(err); errLocale != nil {
|
||||
// i18n-check: ignore
|
||||
ctx.Flash.Error(ctx.Tr(errLocale.TrKey, errLocale.TrArgs...))
|
||||
ctx.Redirect(redirectURL)
|
||||
} else {
|
||||
|
@ -51,6 +51,7 @@ func Activity(ctx *context.Context) {
|
||||
}
|
||||
ctx.Data["DateFrom"] = timeFrom
|
||||
ctx.Data["DateUntil"] = timeUntil
|
||||
// i18n-check: repo.activity.period.*
|
||||
ctx.Data["PeriodText"] = ctx.Tr("repo.activity.period." + ctx.Data["Period"].(string))
|
||||
|
||||
canReadCode := ctx.Repo.CanRead(unit.TypeCode)
|
||||
|
@ -39,6 +39,7 @@ func editorHandleFileOperationErrorRender(ctx *context_service.Context, message,
|
||||
|
||||
func editorHandleFileOperationError(ctx *context_service.Context, targetBranchName string, err error) {
|
||||
if errAs := util.ErrorAsLocale(err); errAs != nil {
|
||||
// i18n-check: ignore
|
||||
ctx.JSONError(ctx.Tr(errAs.TrKey, errAs.TrArgs...))
|
||||
} else if errAs, ok := errorAs[git.ErrNotExist](err); ok {
|
||||
ctx.JSONError(ctx.Tr("repo.editor.file_modifying_no_longer_exists", errAs.RelPath))
|
||||
|
@ -318,6 +318,7 @@ func MigrateStatus(ctx *context.Context) {
|
||||
Args: []any{task.Message},
|
||||
}
|
||||
}
|
||||
// i18n-check: repo.migrate.migrating_failed.*
|
||||
message = ctx.Locale.TrString(translatableMessage.Format, translatableMessage.Args...)
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,7 @@ func (b *Base) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
|
||||
}
|
||||
|
||||
func (b *Base) Tr(msg string, args ...any) template.HTML {
|
||||
// i18n-check: ignore
|
||||
return b.Locale.Tr(msg, args...)
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,7 @@ func (b *BaseConfig) DoNoticeOnSuccess() bool {
|
||||
// Please note the `status` string will be concatenated with `admin.dashboard.cron.` and `admin.dashboard.task.` to provide locale messages. Similarly `name` will be composed with `admin.dashboard.` to provide the locale name for the task.
|
||||
func (b *BaseConfig) FormatMessage(locale translation.Locale, name, status, doer string, args ...any) string {
|
||||
realArgs := make([]any, 0, len(args)+2)
|
||||
// i18n-check: admin.dashboard.*
|
||||
realArgs = append(realArgs, locale.TrString("admin.dashboard."+name))
|
||||
if doer == "" {
|
||||
realArgs = append(realArgs, "(Cron)")
|
||||
@ -80,7 +81,9 @@ func (b *BaseConfig) FormatMessage(locale translation.Locale, name, status, doer
|
||||
realArgs = append(realArgs, args...)
|
||||
}
|
||||
if doer == "" {
|
||||
// i18n-check: admin.dashboard.cron.*
|
||||
return locale.TrString("admin.dashboard.cron."+status, realArgs...)
|
||||
}
|
||||
// i18n-check: admin.dashboard.task.*
|
||||
return locale.TrString("admin.dashboard.task."+status, realArgs...)
|
||||
}
|
||||
|
@ -159,6 +159,7 @@ func RegisterTask(name string, config Config, fun func(context.Context, *user_mo
|
||||
log.Debug("Registering task: %s", name)
|
||||
|
||||
i18nKey := "admin.dashboard." + name
|
||||
// i18n-check: admin.dashboard.*
|
||||
if value := translation.NewLocale("en-US").TrString(i18nKey); value == i18nKey {
|
||||
return fmt.Errorf("translation is missing for task %q, please add translation for %q", name, i18nKey)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
@ -87,6 +88,55 @@ func searchUnusedKeyInFile(dir, path string, keys []string, res *[]bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func searchUntranslatedKeyInCall(path string, fset *token.FileSet, astf *ast.File, call *ast.CallExpr, arg ast.Expr, keys []string) string {
|
||||
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
||||
key := strings.Trim(lit.Value, `"`)
|
||||
if !slices.Contains(keys, key) {
|
||||
return key
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var lastCg *ast.CommentGroup
|
||||
for _, cg := range astf.Comments {
|
||||
if cg.End() < call.Pos() {
|
||||
if lastCg == nil || cg.End() > lastCg.End() {
|
||||
lastCg = cg
|
||||
}
|
||||
}
|
||||
}
|
||||
if lastCg == nil {
|
||||
fmt.Printf("no comment found for a dynamic translation key: %s:%d\n", path, fset.Position(call.Pos()).Line)
|
||||
os.Exit(1)
|
||||
return ""
|
||||
}
|
||||
|
||||
transKeyMatch, ok := strings.CutPrefix(lastCg.Text(), "i18n-check:")
|
||||
if !ok {
|
||||
fmt.Printf("no comment found for a dynamic translation key: %s:%d\n", path, fset.Position(call.Pos()).Line)
|
||||
os.Exit(1)
|
||||
return ""
|
||||
}
|
||||
transKeyMatch = strings.TrimSpace(transKeyMatch)
|
||||
switch transKeyMatch {
|
||||
case "ignore": // i18n-check: ignore
|
||||
return ""
|
||||
default: // i18n-check: <transKeyMatch>
|
||||
g := glob.MustCompile(transKeyMatch)
|
||||
found := false
|
||||
for _, key := range keys {
|
||||
if g.Match(key) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return transKeyMatch
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func searchUntranslatedKeyInGoFile(dir, path string, keys []string) ([]string, error) {
|
||||
if filepath.Ext(path) != ".go" || strings.HasSuffix(path, "_test.go") {
|
||||
return nil, nil
|
||||
@ -94,7 +144,7 @@ func searchUntranslatedKeyInGoFile(dir, path string, keys []string) ([]string, e
|
||||
|
||||
var untranslated []string
|
||||
fs := token.NewFileSet()
|
||||
node, err := parser.ParseFile(fs, path, nil, 0)
|
||||
node, err := parser.ParseFile(fs, path, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -108,26 +158,17 @@ func searchUntranslatedKeyInGoFile(dir, path string, keys []string) ([]string, e
|
||||
switch funIdent.Sel.Name {
|
||||
case "Tr", "TrString":
|
||||
if len(call.Args) >= 1 {
|
||||
if lit, ok := call.Args[0].(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
||||
key := strings.Trim(lit.Value, `"`)
|
||||
if !slices.Contains(keys, key) {
|
||||
untranslated = append(untranslated, key)
|
||||
}
|
||||
if key := searchUntranslatedKeyInCall(path, fs, node, call, call.Args[0], keys); key != "" {
|
||||
untranslated = append(untranslated, key)
|
||||
}
|
||||
}
|
||||
case "TrN":
|
||||
if len(call.Args) >= 3 {
|
||||
if lit, ok := call.Args[1].(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
||||
key := strings.Trim(lit.Value, `"`)
|
||||
if !slices.Contains(keys, key) {
|
||||
untranslated = append(untranslated, key)
|
||||
}
|
||||
if key := searchUntranslatedKeyInCall(path, fs, node, call, call.Args[1], keys); key != "" {
|
||||
untranslated = append(untranslated, key)
|
||||
}
|
||||
if lit, ok := call.Args[2].(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
||||
key := strings.Trim(lit.Value, `"`)
|
||||
if !slices.Contains(keys, key) {
|
||||
untranslated = append(untranslated, key)
|
||||
}
|
||||
if key := searchUntranslatedKeyInCall(path, fs, node, call, call.Args[2], keys); key != "" {
|
||||
untranslated = append(untranslated, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,29 +246,6 @@ func searchUnTranslatedKeyInFile(dir, path string, keys []string) ([]string, err
|
||||
return append(untranslatedKeys, untranslatedKeysInTmpl...), nil
|
||||
}
|
||||
|
||||
var whitelist = []string{
|
||||
"repo.signing.wont_sign.*",
|
||||
"repo.issues.role.*",
|
||||
"repo.commitstatus.*",
|
||||
"admin.dashboard.*",
|
||||
"admin.dashboard.cron.*",
|
||||
"admin.dashboard.task.*",
|
||||
"repo.migrate.*.description",
|
||||
"actions.runners.status.*",
|
||||
"projects.*.display_name",
|
||||
"admin.notices.*",
|
||||
"form.NewBranchName", // FIXME: used in integration tests only
|
||||
}
|
||||
|
||||
func isWhitelisted(key string) bool {
|
||||
for _, w := range whitelist {
|
||||
if glob.MustCompile(w).Match(key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 1 {
|
||||
println("usage: clean-locales")
|
||||
@ -248,9 +266,6 @@ func main() {
|
||||
} else {
|
||||
trKey = section.Name() + "." + key.Name()
|
||||
}
|
||||
if isWhitelisted(trKey) {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, trKey)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user