mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-04-08 02:31:36 +02:00
Merge branch 'forgejo' of /forgejo/forgejo into report_abuse
This commit is contained in:
commit
e483517c7c
125 changed files with 2961 additions and 1573 deletions
|
@ -192,6 +192,9 @@ forgejo.org/modules/translation
|
|||
MockLocale.HasKey
|
||||
MockLocale.PrettyNumber
|
||||
|
||||
forgejo.org/modules/translation/localeiter
|
||||
IterateMessagesContent
|
||||
|
||||
forgejo.org/modules/util
|
||||
OptionalArg
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -462,7 +462,7 @@ lint-locale:
|
|||
|
||||
.PHONY: lint-locale-usage
|
||||
lint-locale-usage:
|
||||
$(GO) run build/lint-locale-usage/lint-locale-usage.go --allow-missing-msgids
|
||||
$(GO) run build/lint-locale-usage/lint-locale-usage.go
|
||||
|
||||
.PHONY: lint-md
|
||||
lint-md: node_modules
|
||||
|
|
|
@ -18,8 +18,8 @@ import (
|
|||
tmplParser "text/template/parse"
|
||||
|
||||
"forgejo.org/modules/container"
|
||||
"forgejo.org/modules/locale"
|
||||
fjTemplates "forgejo.org/modules/templates"
|
||||
"forgejo.org/modules/translation/localeiter"
|
||||
"forgejo.org/modules/util"
|
||||
)
|
||||
|
||||
|
@ -300,10 +300,6 @@ func main() {
|
|||
}
|
||||
|
||||
msgids := make(container.Set[string])
|
||||
onMsgid := func(trKey, trValue string) error {
|
||||
msgids[trKey] = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
localeFile := filepath.Join(filepath.Join("options", "locale"), "locale_en-US.ini")
|
||||
localeContent, err := os.ReadFile(localeFile)
|
||||
|
@ -312,7 +308,10 @@ func main() {
|
|||
os.Exit(2)
|
||||
}
|
||||
|
||||
if err = locale.IterateMessagesContent(localeContent, onMsgid); err != nil {
|
||||
if err = localeiter.IterateMessagesContent(localeContent, func(trKey, trValue string) error {
|
||||
msgids[trKey] = struct{}{}
|
||||
return nil
|
||||
}); err != nil {
|
||||
fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
@ -324,7 +323,11 @@ func main() {
|
|||
os.Exit(2)
|
||||
}
|
||||
|
||||
if err := locale.IterateMessagesNextContent(localeContent, onMsgid); err != nil {
|
||||
if err := localeiter.IterateMessagesNextContent(localeContent, func(trKey, pluralForm, trValue string) error {
|
||||
// ignore plural form
|
||||
msgids[trKey] = struct{}{}
|
||||
return nil
|
||||
}); err != nil {
|
||||
fmt.Printf("%s:\tERROR: %s\n", localeFile, err.Error())
|
||||
os.Exit(2)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
|
||||
"forgejo.org/modules/locale"
|
||||
"forgejo.org/modules/translation/localeiter"
|
||||
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
|
@ -100,7 +100,7 @@ func checkValue(trKey, value string) []string {
|
|||
func checkLocaleContent(localeContent []byte) []string {
|
||||
errors := []string{}
|
||||
|
||||
if err := locale.IterateMessagesContent(localeContent, func(trKey, trValue string) error {
|
||||
if err := localeiter.IterateMessagesContent(localeContent, func(trKey, trValue string) error {
|
||||
errors = append(errors, checkValue(trKey, trValue)...)
|
||||
return nil
|
||||
}); err != nil {
|
||||
|
@ -113,8 +113,12 @@ func checkLocaleContent(localeContent []byte) []string {
|
|||
func checkLocaleNextContent(localeContent []byte) []string {
|
||||
errors := []string{}
|
||||
|
||||
if err := locale.IterateMessagesNextContent(localeContent, func(trKey, trValue string) error {
|
||||
errors = append(errors, checkValue(trKey, trValue)...)
|
||||
if err := localeiter.IterateMessagesNextContent(localeContent, func(trKey, pluralForm, trValue string) error {
|
||||
fullKey := trKey
|
||||
if pluralForm != "" {
|
||||
fullKey = trKey + "." + pluralForm
|
||||
}
|
||||
errors = append(errors, checkValue(fullKey, trValue)...)
|
||||
return nil
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -60,7 +60,7 @@ func TestLocalizationPolicy(t *testing.T) {
|
|||
t.Run("Escaped HTML characters", func(t *testing.T) {
|
||||
assert.Empty(t, checkLocaleContent([]byte("activity.git_stats_push_to_branch = `إلى %s و\"`")))
|
||||
|
||||
assert.Equal(t, []string{"key: و\x1b[31m \x1b[0m\x1b[32m\u00a0\x1b[0m"}, checkLocaleContent([]byte(`key = و `)))
|
||||
assert.Equal(t, []string{"key: و\x1b[31m \x1b[0m\x1b[32m\u00a0\x1b[0m"}, checkLocaleContent([]byte(`key = و `)))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -91,4 +91,16 @@ func TestNextLocalizationPolicy(t *testing.T) {
|
|||
"settings.hidden_comment_types_description": "\"<not-an-allowed-key> <label>\""
|
||||
}`)))
|
||||
})
|
||||
|
||||
t.Run("Plural form", func(t *testing.T) {
|
||||
assert.Equal(t, []string{"repo.pulls.title_desc: key = \x1b[31m<a href=\"https://example.com\">\x1b[0m"}, checkLocaleNextContent([]byte(`{"repo.pulls.title_desc": {
|
||||
"few": "key = <a href=\"%s\">",
|
||||
"other": "key = <a href=\"https://example.com\">"
|
||||
}}`)))
|
||||
|
||||
assert.Equal(t, []string{"repo.pulls.title_desc.few: key = \x1b[31m<a href=\"https://example.com\">\x1b[0m"}, checkLocaleNextContent([]byte(`{"repo.pulls.title_desc": {
|
||||
"few": "key = <a href=\"https://example.com\">",
|
||||
"other": "key = <a href=\"%s\">"
|
||||
}}`)))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -318,8 +318,9 @@ export default tseslint.config(
|
|||
'no-jquery/no-data': [0],
|
||||
'no-jquery/no-deferred': [2],
|
||||
'no-jquery/no-delegate': [2],
|
||||
'no-jquery/no-done-fail': [2],
|
||||
'no-jquery/no-each-collection': [0],
|
||||
'no-jquery/no-each-util': [0],
|
||||
'no-jquery/no-each-util': [2],
|
||||
'no-jquery/no-each': [0],
|
||||
'no-jquery/no-error-shorthand': [2],
|
||||
'no-jquery/no-error': [2],
|
||||
|
@ -331,6 +332,7 @@ export default tseslint.config(
|
|||
'no-jquery/no-find-collection': [0],
|
||||
'no-jquery/no-find-util': [2],
|
||||
'no-jquery/no-find': [0],
|
||||
'no-jquery/no-fx': [2],
|
||||
'no-jquery/no-fx-interval': [2],
|
||||
'no-jquery/no-global-eval': [2],
|
||||
'no-jquery/no-global-selector': [0],
|
||||
|
@ -350,7 +352,7 @@ export default tseslint.config(
|
|||
'no-jquery/no-live': [2],
|
||||
'no-jquery/no-load-shorthand': [2],
|
||||
'no-jquery/no-load': [2],
|
||||
'no-jquery/no-map-collection': [0],
|
||||
'no-jquery/no-map-collection': [2],
|
||||
'no-jquery/no-map-util': [2],
|
||||
'no-jquery/no-map': [2],
|
||||
'no-jquery/no-merge': [2],
|
||||
|
@ -374,12 +376,12 @@ export default tseslint.config(
|
|||
'no-jquery/no-selector-prop': [2],
|
||||
'no-jquery/no-serialize': [2],
|
||||
'no-jquery/no-size': [2],
|
||||
'no-jquery/no-sizzle': [0],
|
||||
'no-jquery/no-sizzle': [2],
|
||||
'no-jquery/no-slide': [2],
|
||||
'no-jquery/no-sub': [2],
|
||||
'no-jquery/no-support': [2],
|
||||
'no-jquery/no-text': [0],
|
||||
'no-jquery/no-trigger': [0],
|
||||
'no-jquery/no-trigger': [2],
|
||||
'no-jquery/no-trim': [2],
|
||||
'no-jquery/no-type': [2],
|
||||
'no-jquery/no-unique': [2],
|
||||
|
|
10
go.mod
10
go.mod
|
@ -2,7 +2,7 @@ module forgejo.org
|
|||
|
||||
go 1.24
|
||||
|
||||
toolchain go1.24.1
|
||||
toolchain go1.24.2
|
||||
|
||||
require (
|
||||
code.forgejo.org/f3/gof3/v3 v3.10.6
|
||||
|
@ -49,7 +49,7 @@ require (
|
|||
github.com/go-openapi/spec v0.20.14
|
||||
github.com/go-sql-driver/mysql v1.9.1
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.14.0
|
||||
github.com/go-webauthn/webauthn v0.12.2
|
||||
github.com/go-webauthn/webauthn v0.12.3
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
|
@ -72,7 +72,7 @@ require (
|
|||
github.com/lib/pq v1.10.9
|
||||
github.com/markbates/goth v1.80.0
|
||||
github.com/mattn/go-isatty v0.0.20
|
||||
github.com/mattn/go-sqlite3 v1.14.24
|
||||
github.com/mattn/go-sqlite3 v1.14.27
|
||||
github.com/meilisearch/meilisearch-go v0.31.0
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
|
@ -177,7 +177,7 @@ require (
|
|||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
|
@ -190,7 +190,7 @@ require (
|
|||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||
github.com/go-openapi/swag v0.22.7 // indirect
|
||||
github.com/go-webauthn/x v0.1.19 // indirect
|
||||
github.com/go-webauthn/x v0.1.20 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
|
||||
|
|
16
go.sum
16
go.sum
|
@ -895,8 +895,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 h1:j2TrkUG/NATGi/EQS+MvEoF79CxiRUmT16ErFroNcKI=
|
||||
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9/go.mod h1:cJ9Ye0ZNSMN7RzZDBRY3E+8M3Bpf/R1JX22Ir9yX6WI=
|
||||
|
@ -967,10 +967,10 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
|||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.14.0 h1:aRt5qyH2XjzFgCC5NizNs6QrzjO7rC4pQZ1oJpPIdo8=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.14.0/go.mod h1:HHb6Yd8spzm6aFZU6jwBj9qFvVUNNkx5nGbjG4UHeOE=
|
||||
github.com/go-webauthn/webauthn v0.12.2 h1:yLaNPgBUEXDQtWnOjhsGhMMCEWbXwjg/aNkC8riJQI8=
|
||||
github.com/go-webauthn/webauthn v0.12.2/go.mod h1:Q8SZPPj4sZ469fNTcQXxRpzJOdb30jQrn/36FX8jilA=
|
||||
github.com/go-webauthn/x v0.1.19 h1:IUfdHiBRoTdujpBA/14qbrMXQ3LGzYe/PRGWdZcmudg=
|
||||
github.com/go-webauthn/x v0.1.19/go.mod h1:C5arLuTQ3pVHKPw89v7CDGnqAZSZJj+4Jnr40dsn7tk=
|
||||
github.com/go-webauthn/webauthn v0.12.3 h1:hHQl1xkUuabUU9uS+ISNCMLs9z50p9mDUZI/FmkayNE=
|
||||
github.com/go-webauthn/webauthn v0.12.3/go.mod h1:4JRe8Z3W7HIw8NGEWn2fnUwecoDzkkeach/NnvhkqGY=
|
||||
github.com/go-webauthn/x v0.1.20 h1:brEBDqfiPtNNCdS/peu8gARtq8fIPsHz0VzpPjGvgiw=
|
||||
github.com/go-webauthn/x v0.1.20/go.mod h1:n/gAc8ssZJGATM0qThE+W+vfgXiMedsWi3wf/C4lld0=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
|
@ -1252,8 +1252,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
|
|||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU=
|
||||
github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY=
|
||||
github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g=
|
||||
github.com/mholt/acmez/v3 v3.1.1 h1:Jh+9uKHkPxUJdxM16q5mOr+G2V0aqkuFtNA28ihCxhQ=
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package forgefed
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -15,12 +16,14 @@ import (
|
|||
// FederationHost data type
|
||||
// swagger:model
|
||||
type FederationHost struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"`
|
||||
NodeInfo NodeInfo `xorm:"extends NOT NULL"`
|
||||
LatestActivity time.Time `xorm:"NOT NULL"`
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"`
|
||||
NodeInfo NodeInfo `xorm:"extends NOT NULL"`
|
||||
LatestActivity time.Time `xorm:"NOT NULL"`
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
KeyID sql.NullString `xorm:"key_id UNIQUE"`
|
||||
PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"`
|
||||
}
|
||||
|
||||
// Factory function for FederationHost. Created struct is asserted to be valid.
|
||||
|
|
|
@ -30,9 +30,9 @@ func GetFederationHost(ctx context.Context, ID int64) (*FederationHost, error) {
|
|||
return host, nil
|
||||
}
|
||||
|
||||
func FindFederationHostByFqdn(ctx context.Context, fqdn string) (*FederationHost, error) {
|
||||
func findFederationHostFromDB(ctx context.Context, searchKey, searchValue string) (*FederationHost, error) {
|
||||
host := new(FederationHost)
|
||||
has, err := db.GetEngine(ctx).Where("host_fqdn=?", strings.ToLower(fqdn)).Get(host)
|
||||
has, err := db.GetEngine(ctx).Where(searchKey, searchValue).Get(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
|
@ -44,6 +44,14 @@ func FindFederationHostByFqdn(ctx context.Context, fqdn string) (*FederationHost
|
|||
return host, nil
|
||||
}
|
||||
|
||||
func FindFederationHostByFqdn(ctx context.Context, fqdn string) (*FederationHost, error) {
|
||||
return findFederationHostFromDB(ctx, "host_fqdn=?", strings.ToLower(fqdn))
|
||||
}
|
||||
|
||||
func FindFederationHostByKeyID(ctx context.Context, keyID string) (*FederationHost, error) {
|
||||
return findFederationHostFromDB(ctx, "key_id=?", keyID)
|
||||
}
|
||||
|
||||
func CreateFederationHost(ctx context.Context, host *FederationHost) error {
|
||||
if res, err := validation.IsValid(host); !res {
|
||||
return err
|
||||
|
|
|
@ -94,6 +94,8 @@ var migrations = []*Migration{
|
|||
NewMigration("Add `created_unix` column to `user_redirect` table", AddCreatedUnixToRedirect),
|
||||
// v27 -> v28
|
||||
NewMigration("Add pronoun privacy settings to user", AddHidePronounsOptionToUser),
|
||||
// v28 -> v29
|
||||
NewMigration("Add public key information to `FederatedUser` and `FederationHost`", AddPublicKeyInformationForFederation),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current Forgejo database version.
|
||||
|
|
29
models/forgejo_migrations/v29.go
Normal file
29
models/forgejo_migrations/v29.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package forgejo_migrations //nolint:revive
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddPublicKeyInformationForFederation(x *xorm.Engine) error {
|
||||
type FederationHost struct {
|
||||
KeyID sql.NullString `xorm:"key_id UNIQUE"`
|
||||
PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"`
|
||||
}
|
||||
|
||||
err := x.Sync(&FederationHost{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type FederatedUser struct {
|
||||
KeyID sql.NullString `xorm:"key_id UNIQUE"`
|
||||
PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"`
|
||||
}
|
||||
|
||||
return x.Sync(&FederatedUser{})
|
||||
}
|
44
models/user/activitypub.go
Normal file
44
models/user/activitypub.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/validation"
|
||||
)
|
||||
|
||||
// APActorID returns the IRI to the api endpoint of the user
|
||||
func (u *User) APActorID() string {
|
||||
if u.IsAPServerActor() {
|
||||
return fmt.Sprintf("%sapi/v1/activitypub/actor", setting.AppURL)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%sapi/v1/activitypub/user-id/%s", setting.AppURL, url.PathEscape(fmt.Sprintf("%d", u.ID)))
|
||||
}
|
||||
|
||||
// APActorKeyID returns the ID of the user's public key
|
||||
func (u *User) APActorKeyID() string {
|
||||
return u.APActorID() + "#main-key"
|
||||
}
|
||||
|
||||
func GetUserByFederatedURI(ctx context.Context, federatedURI string) (*User, error) {
|
||||
user := new(User)
|
||||
has, err := db.GetEngine(ctx).Where("normalized_federated_uri=?", federatedURI).Get(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if res, err := validation.IsValid(*user); !res {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
|
@ -4,14 +4,20 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/modules/validation"
|
||||
)
|
||||
|
||||
type FederatedUser struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UserID int64 `xorm:"NOT NULL"`
|
||||
ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"`
|
||||
FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UserID int64 `xorm:"NOT NULL"`
|
||||
ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"`
|
||||
FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"`
|
||||
KeyID sql.NullString `xorm:"key_id UNIQUE"`
|
||||
PublicKey sql.Null[sql.RawBytes] `xorm:"BLOB"`
|
||||
}
|
||||
|
||||
func NewFederatedUser(userID int64, externalID string, federationHostID int64) (FederatedUser, error) {
|
||||
|
@ -26,6 +32,30 @@ func NewFederatedUser(userID int64, externalID string, federationHostID int64) (
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func getFederatedUserFromDB(ctx context.Context, searchKey, searchValue any) (*FederatedUser, error) {
|
||||
federatedUser := new(FederatedUser)
|
||||
has, err := db.GetEngine(ctx).Where(searchKey, searchValue).Get(federatedUser)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if res, err := validation.IsValid(*federatedUser); !res {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return federatedUser, nil
|
||||
}
|
||||
|
||||
func GetFederatedUserByKeyID(ctx context.Context, keyID string) (*FederatedUser, error) {
|
||||
return getFederatedUserFromDB(ctx, "key_id=?", keyID)
|
||||
}
|
||||
|
||||
func GetFederatedUserByUserID(ctx context.Context, userID int64) (*FederatedUser, error) {
|
||||
return getFederatedUserFromDB(ctx, "user_id=?", userID)
|
||||
}
|
||||
|
||||
func (user FederatedUser) Validate() []string {
|
||||
var result []string
|
||||
result = append(result, validation.ValidateNotEmpty(user.UserID, "UserID")...)
|
||||
|
|
|
@ -314,11 +314,6 @@ func (u *User) HTMLURL() string {
|
|||
return setting.AppURL + url.PathEscape(u.Name)
|
||||
}
|
||||
|
||||
// APActorID returns the IRI to the api endpoint of the user
|
||||
func (u *User) APActorID() string {
|
||||
return fmt.Sprintf("%vapi/v1/activitypub/user-id/%v", setting.AppURL, url.PathEscape(fmt.Sprintf("%v", u.ID)))
|
||||
}
|
||||
|
||||
// OrganisationLink returns the organization sub page link.
|
||||
func (u *User) OrganisationLink() string {
|
||||
return setting.AppSubURL + "/org/" + url.PathEscape(u.Name)
|
||||
|
|
|
@ -73,30 +73,30 @@ func (u *User) IsActions() bool {
|
|||
}
|
||||
|
||||
const (
|
||||
APActorUserID = -3
|
||||
APActorUserName = "actor"
|
||||
APActorEmail = "noreply@forgejo.org"
|
||||
APServerActorUserID = -3
|
||||
APServerActorUserName = "actor"
|
||||
APServerActorEmail = "noreply@forgejo.org"
|
||||
)
|
||||
|
||||
func NewAPActorUser() *User {
|
||||
func NewAPServerActor() *User {
|
||||
return &User{
|
||||
ID: APActorUserID,
|
||||
Name: APActorUserName,
|
||||
LowerName: APActorUserName,
|
||||
ID: APServerActorUserID,
|
||||
Name: APServerActorUserName,
|
||||
LowerName: APServerActorUserName,
|
||||
IsActive: true,
|
||||
Email: APActorEmail,
|
||||
Email: APServerActorEmail,
|
||||
KeepEmailPrivate: true,
|
||||
LoginName: APActorUserName,
|
||||
LoginName: APServerActorUserName,
|
||||
Type: UserTypeIndividual,
|
||||
Visibility: structs.VisibleTypePublic,
|
||||
}
|
||||
}
|
||||
|
||||
func APActorUserAPActorID() string {
|
||||
func APServerActorID() string {
|
||||
path, _ := url.JoinPath(setting.AppURL, "/api/v1/activitypub/actor")
|
||||
return path
|
||||
}
|
||||
|
||||
func (u *User) IsAPActor() bool {
|
||||
return u != nil && u.ID == APActorUserID
|
||||
func (u *User) IsAPServerActor() bool {
|
||||
return u != nil && u.ID == APServerActorUserID
|
||||
}
|
||||
|
|
|
@ -139,9 +139,21 @@ func TestAPActorID(t *testing.T) {
|
|||
user := user_model.User{ID: 1}
|
||||
url := user.APActorID()
|
||||
expected := "https://try.gitea.io/api/v1/activitypub/user-id/1"
|
||||
if url != expected {
|
||||
t.Errorf("unexpected APActorID, expected: %q, actual: %q", expected, url)
|
||||
}
|
||||
assert.Equal(t, expected, url)
|
||||
}
|
||||
|
||||
func TestAPActorID_APActorID(t *testing.T) {
|
||||
user := user_model.User{ID: user_model.APServerActorUserID}
|
||||
url := user.APActorID()
|
||||
expected := "https://try.gitea.io/api/v1/activitypub/actor"
|
||||
assert.Equal(t, expected, url)
|
||||
}
|
||||
|
||||
func TestAPActorKeyID(t *testing.T) {
|
||||
user := user_model.User{ID: 1}
|
||||
url := user.APActorKeyID()
|
||||
expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key"
|
||||
assert.Equal(t, expected, url)
|
||||
}
|
||||
|
||||
func TestSearchUsers(t *testing.T) {
|
||||
|
|
|
@ -191,10 +191,17 @@ func (c *Client) GetBody(uri string) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
body, err := io.ReadAll(response.Body)
|
||||
if response.ContentLength > setting.Federation.MaxSize {
|
||||
return nil, fmt.Errorf("Request returned %d bytes (max allowed incomming size: %d bytes)", response.ContentLength, setting.Federation.MaxSize)
|
||||
} else if response.ContentLength == -1 {
|
||||
log.Warn("Request to %v returned an unknown content length, response may be truncated to %d bytes", uri, setting.Federation.MaxSize)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(io.LimitReader(response.Body, setting.Federation.MaxSize))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Client: got body: %v", charLimiter(string(body), 120))
|
||||
return body, nil
|
||||
}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// extracted from `/build/lint-locale.go`, `/build/lint-locale-usage.go`
|
||||
|
||||
package locale
|
||||
|
||||
import (
|
||||
"encoding/json" //nolint:depguard
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/ini.v1" //nolint:depguard
|
||||
)
|
||||
|
||||
func IterateMessagesContent(localeContent []byte, onMsgid func(string, string) error) error {
|
||||
// Same configuration as Forgejo uses.
|
||||
cfg := ini.Empty(ini.LoadOptions{
|
||||
IgnoreContinuation: true,
|
||||
})
|
||||
cfg.NameMapper = ini.SnackCase
|
||||
|
||||
if err := cfg.Append(localeContent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, section := range cfg.Sections() {
|
||||
for _, key := range section.Keys() {
|
||||
var trKey string
|
||||
if section.Name() == "" || section.Name() == "DEFAULT" || section.Name() == "common" {
|
||||
trKey = key.Name()
|
||||
} else {
|
||||
trKey = section.Name() + "." + key.Name()
|
||||
}
|
||||
if err := onMsgid(trKey, key.Value()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func iterateMessagesNextInner(onMsgid func(string, string) error, data map[string]any, trKey ...string) error {
|
||||
for key, value := range data {
|
||||
currentKey := key
|
||||
if len(trKey) == 1 {
|
||||
currentKey = trKey[0] + "." + key
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if err := onMsgid(currentKey, value); err != nil {
|
||||
return err
|
||||
}
|
||||
case map[string]any:
|
||||
if err := iterateMessagesNextInner(onMsgid, value, currentKey); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected type: %s - %T", currentKey, value)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IterateMessagesNextContent(localeContent []byte, onMsgid func(string, string) error) error {
|
||||
var localeData map[string]any
|
||||
if err := json.Unmarshal(localeContent, &localeData); err != nil {
|
||||
return err
|
||||
}
|
||||
return iterateMessagesNextInner(onMsgid, localeData)
|
||||
}
|
|
@ -341,7 +341,7 @@ func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node {
|
|||
psubtitle := &html.Node{
|
||||
Type: html.ElementNode,
|
||||
Data: atom.Span.String(),
|
||||
Attr: []html.Attribute{{Key: "class", Val: "text small grey"}},
|
||||
Attr: []html.Attribute{{Key: "class", Val: "text grey"}},
|
||||
}
|
||||
psubtitle.AppendChild(&html.Node{
|
||||
Type: html.RawNode,
|
||||
|
|
|
@ -721,7 +721,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -755,7 +755,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<a href="http://localhost:3000/gogits/gogs/" rel="nofollow">gogits/gogs</a> – `+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">gogits/gogs@190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -791,7 +791,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<a href="http://localhost:3000/gogits/gogs/" rel="nofollow">gogits/gogs</a> – `+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/4c1aaf56bcb9f39dcf65f3f250726850aed13cd6/single-line.txt#L1" class="muted" rel="nofollow">single-line.txt</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Line 1 in <a href="http://localhost:3000/gogits/gogs/src/commit/4c1aaf56bcb9f39dcf65f3f250726850aed13cd6" class="text black" rel="nofollow">gogits/gogs@4c1aaf5</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -834,7 +834,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -865,7 +865,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -898,7 +898,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -923,7 +923,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -954,7 +954,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -979,7 +979,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -1004,7 +1004,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -1039,7 +1039,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be/path/to/file.md?display=source#L1-L2" class="muted" rel="nofollow">path/to/file.md</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 1 to 2 in <a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be" class="text black" rel="nofollow">c991312</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -1072,7 +1072,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be/path/to/file.md?display=source#L1-L2" class="muted" rel="nofollow">path/to/file.md</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 1 to 2 in <a href="http://localhost:3000/gogits/gogs/src/commit/c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be" class="text black" rel="nofollow">c991312</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -1107,7 +1107,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go?display=source#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
@ -1142,7 +1142,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d/path/to/file%20%23.txt#L1" class="muted" rel="nofollow">path/to/file #.txt</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`<span class="text grey">`+
|
||||
`Line 1 in <a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d" class="text black" rel="nofollow">eeb243c</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
|
|
|
@ -122,7 +122,7 @@ func createDefaultPolicy() *bluemonday.Policy {
|
|||
policy.AllowAttrs("class").Matching(regexp.MustCompile("^ui table$")).OnElements("div")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile("^header$")).OnElements("div")
|
||||
policy.AllowAttrs("data-line-number").Matching(regexp.MustCompile("^[0-9]+$")).OnElements("span")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile("^text small grey$")).OnElements("span")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile("^text grey$")).OnElements("span")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile("^file-preview$")).OnElements("table")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile("^lines-escape$")).OnElements("td")
|
||||
policy.AllowAttrs("class").Matching(regexp.MustCompile("^toggle-escape-button btn interact-bg$")).OnElements("button")
|
||||
|
|
|
@ -34,6 +34,7 @@ type PullRequest struct {
|
|||
Assignees []string
|
||||
IsLocked bool `yaml:"is_locked"`
|
||||
Reactions []*Reaction
|
||||
Flow int64
|
||||
ForeignIndex int64
|
||||
Context DownloaderContext `yaml:"-"`
|
||||
EnsuredSafe bool `yaml:"ensured_safe"`
|
||||
|
|
|
@ -15,18 +15,20 @@ var (
|
|||
Enabled bool
|
||||
ShareUserStatistics bool
|
||||
MaxSize int64
|
||||
Algorithms []string
|
||||
SignatureAlgorithms []string
|
||||
DigestAlgorithm string
|
||||
GetHeaders []string
|
||||
PostHeaders []string
|
||||
SignatureEnforced bool
|
||||
}{
|
||||
Enabled: false,
|
||||
ShareUserStatistics: true,
|
||||
MaxSize: 4,
|
||||
Algorithms: []string{"rsa-sha256", "rsa-sha512", "ed25519"},
|
||||
SignatureAlgorithms: []string{"rsa-sha256", "rsa-sha512", "ed25519"},
|
||||
DigestAlgorithm: "SHA-256",
|
||||
GetHeaders: []string{"(request-target)", "Date", "Host"},
|
||||
PostHeaders: []string{"(request-target)", "Date", "Host", "Digest"},
|
||||
SignatureEnforced: true,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -44,8 +46,8 @@ func loadFederationFrom(rootCfg ConfigProvider) {
|
|||
// Get MaxSize in bytes instead of MiB
|
||||
Federation.MaxSize = 1 << 20 * Federation.MaxSize
|
||||
|
||||
HttpsigAlgs = make([]httpsig.Algorithm, len(Federation.Algorithms))
|
||||
for i, alg := range Federation.Algorithms {
|
||||
HttpsigAlgs = make([]httpsig.Algorithm, len(Federation.SignatureAlgorithms))
|
||||
for i, alg := range Federation.SignatureAlgorithms {
|
||||
HttpsigAlgs[i] = httpsig.Algorithm(alg)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,8 @@ type PullRequest struct {
|
|||
// swagger:strfmt date-time
|
||||
Closed *time.Time `json:"closed_at"`
|
||||
|
||||
PinOrder int `json:"pin_order"`
|
||||
PinOrder int `json:"pin_order"`
|
||||
Flow int64 `json:"flow"`
|
||||
}
|
||||
|
||||
// PRBranchInfo information about a branch
|
||||
|
|
|
@ -95,7 +95,7 @@ func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server {
|
|||
})
|
||||
}
|
||||
for _, repository := range mock.Repositories {
|
||||
federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox/", repository.ID),
|
||||
federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox", repository.ID),
|
||||
func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != "POST" {
|
||||
t.Errorf("POST expected at: %q", req.URL.EscapedPath())
|
||||
|
|
|
@ -8,9 +8,9 @@ import (
|
|||
"html/template"
|
||||
"slices"
|
||||
|
||||
"forgejo.org/modules/json"
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/translation/localeiter"
|
||||
"forgejo.org/modules/util"
|
||||
)
|
||||
|
||||
|
@ -94,55 +94,20 @@ func (store *localeStore) AddLocaleByIni(langName, langDesc string, pluralRule P
|
|||
return nil
|
||||
}
|
||||
|
||||
func RecursivelyAddTranslationsFromJSON(locale *locale, object map[string]any, prefix string) error {
|
||||
for key, value := range object {
|
||||
var fullkey string
|
||||
if prefix != "" {
|
||||
fullkey = prefix + "." + key
|
||||
} else {
|
||||
fullkey = key
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
// Check whether we are adding a plural form to the parent object, or a new nested JSON object.
|
||||
|
||||
switch key {
|
||||
case "zero", "one", "two", "few", "many":
|
||||
locale.newStyleMessages[prefix+PluralFormSeparator+key] = v
|
||||
case "other":
|
||||
locale.newStyleMessages[prefix] = v
|
||||
default:
|
||||
locale.newStyleMessages[fullkey] = v
|
||||
}
|
||||
|
||||
case map[string]any:
|
||||
err := RecursivelyAddTranslationsFromJSON(locale, v, fullkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case nil:
|
||||
default:
|
||||
return fmt.Errorf("Unrecognized JSON value '%s'", value)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *localeStore) AddToLocaleFromJSON(langName string, source []byte) error {
|
||||
locale, ok := store.localeMap[langName]
|
||||
if !ok {
|
||||
return ErrLocaleDoesNotExist
|
||||
}
|
||||
|
||||
var result map[string]any
|
||||
if err := json.Unmarshal(source, &result); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return RecursivelyAddTranslationsFromJSON(locale, result, "")
|
||||
return localeiter.IterateMessagesNextContent(source, func(key, pluralForm, value string) error {
|
||||
msgKey := key
|
||||
if pluralForm != "" {
|
||||
msgKey = key + PluralFormSeparator + pluralForm
|
||||
}
|
||||
locale.newStyleMessages[msgKey] = value
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (l *locale) LookupNewStyleMessage(trKey string) string {
|
||||
|
|
89
modules/translation/localeiter/utils.go
Normal file
89
modules/translation/localeiter/utils.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// extracted from `/build/lint-locale.go`, `/build/lint-locale-usage.go`
|
||||
|
||||
package localeiter
|
||||
|
||||
import (
|
||||
"encoding/json" //nolint:depguard
|
||||
"fmt"
|
||||
|
||||
"forgejo.org/modules/setting"
|
||||
)
|
||||
|
||||
func IterateMessagesContent(localeContent []byte, onMsgid func(string, string) error) error {
|
||||
cfg, err := setting.NewConfigProviderForLocale(localeContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, section := range cfg.Sections() {
|
||||
for _, key := range section.Keys() {
|
||||
var trKey string
|
||||
// see https://codeberg.org/forgejo/discussions/issues/104
|
||||
// https://github.com/WeblateOrg/weblate/issues/10831
|
||||
// for an explanation of why "common" is an alternative
|
||||
if section.Name() == "" || section.Name() == "DEFAULT" || section.Name() == "common" {
|
||||
trKey = key.Name()
|
||||
} else {
|
||||
trKey = section.Name() + "." + key.Name()
|
||||
}
|
||||
if err := onMsgid(trKey, key.Value()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func iterateMessagesNextInner(onMsgid func(string, string, string) error, data map[string]any, trKey string) error {
|
||||
for key, value := range data {
|
||||
fullKey := key
|
||||
if trKey != "" {
|
||||
fullKey = trKey + "." + key
|
||||
}
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
// Check whether we are adding a plural form to the parent object, or a new nested JSON object.
|
||||
realKey := trKey
|
||||
pluralSuffix := ""
|
||||
|
||||
switch key {
|
||||
case "zero", "one", "two", "few", "many":
|
||||
pluralSuffix = key
|
||||
case "other":
|
||||
// do nothing
|
||||
default:
|
||||
realKey = fullKey
|
||||
}
|
||||
|
||||
if err := onMsgid(realKey, pluralSuffix, value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case map[string]any:
|
||||
if err := iterateMessagesNextInner(onMsgid, value, fullKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case nil:
|
||||
// do nothing
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Unexpected JSON type: %s - %T", fullKey, value)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IterateMessagesNextContent(localeContent []byte, onMsgid func(string, string, string) error) error {
|
||||
var localeData map[string]any
|
||||
if err := json.Unmarshal(localeContent, &localeData); err != nil {
|
||||
return err
|
||||
}
|
||||
return iterateMessagesNextInner(onMsgid, localeData, "")
|
||||
}
|
|
@ -79,6 +79,11 @@ func GetInclude(field reflect.StructField) string {
|
|||
return getRuleBody(field, "Include(")
|
||||
}
|
||||
|
||||
func GetRange(field reflect.StructField) (string, string) {
|
||||
min, max, _ := strings.Cut(getRuleBody(field, "Range("), ",")
|
||||
return min, max
|
||||
}
|
||||
|
||||
// Validate populates the data with validation error (if any).
|
||||
func Validate(errs binding.Errors, data map[string]any, f any, l translation.Locale) binding.Errors {
|
||||
if errs.Len() == 0 {
|
||||
|
@ -131,6 +136,9 @@ func Validate(errs binding.Errors, data map[string]any, f any, l translation.Loc
|
|||
data["ErrorMsg"] = trName + l.TrString("form.url_error", errs[0].Message)
|
||||
case binding.ERR_INCLUDE:
|
||||
data["ErrorMsg"] = trName + l.TrString("form.include_error", GetInclude(field))
|
||||
case binding.ERR_RANGE:
|
||||
min, max := GetRange(field)
|
||||
data["ErrorMsg"] = trName + l.TrString("alert.range_error", l.PrettyNumber(min), l.PrettyNumber(max))
|
||||
case validation.ErrGlobPattern:
|
||||
data["ErrorMsg"] = trName + l.TrString("form.glob_pattern_error", errs[0].Message)
|
||||
case validation.ErrRegexPattern:
|
||||
|
|
|
@ -1652,7 +1652,7 @@ issues.comment_manually_pull_merged_at=ručně sloučena revize %[1]s do %[2]s %
|
|||
issues.close_comment_issue=Zavřít s komentářem
|
||||
issues.reopen_issue=Znovu otevřít
|
||||
issues.reopen_comment_issue=Znovu otevřít s komentářem
|
||||
issues.create_comment=Okomentovat
|
||||
issues.create_comment=Komentovat
|
||||
issues.closed_at=`uzavřel/a tento problém <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.reopened_at=`znovu otevřel/a tento problém <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
issues.commit_ref_at=`odkázal/a na tento problém z revize <a id="%[1]s" href="#%[1]s">%[2]s</a>`
|
||||
|
@ -2607,7 +2607,7 @@ diff.comment.reply=Odpovědět
|
|||
diff.review=Dokončit posouzení
|
||||
diff.review.header=Odeslat posouzení
|
||||
diff.review.placeholder=Posoudit komentář
|
||||
diff.review.comment=Okomentovat
|
||||
diff.review.comment=Komentovat
|
||||
diff.review.approve=Schválit
|
||||
diff.review.self_reject=Autoři požadavků na natažení nemohou požadovat změny na svém vlastním požadavku na natažení
|
||||
diff.review.reject=Požadovat změny
|
||||
|
@ -2909,6 +2909,9 @@ issues.reopen.blocked_by_user = Tento problém nemůžete znovu otevřít, proto
|
|||
pulls.comment.blocked_by_user = Tuto žádost o sloučení nemůžete okomentovat, protože jste byli zablokováni majitelem repozitáře nebo autorem žádosti.
|
||||
issues.filter_no_results_placeholder = Zkuste upravit filtry vyhledávání.
|
||||
issues.filter_no_results = Žádné výsledky
|
||||
migrate.repo_desc_helper = Ponechte prázdné pro importování existujícího popisu
|
||||
archive.nocomment = Komentování není možné, protože je repozitář archivován.
|
||||
comment.blocked_by_user = Komentování není možné, protože jste byli zablokováni majitelem repozitáře nebo autorem.
|
||||
|
||||
[graphs]
|
||||
component_loading_info = Tohle může chvíli trvat…
|
||||
|
|
|
@ -1451,7 +1451,7 @@ issues.filter_sort.leastcomment = Mindst kommenteret
|
|||
issues.filter_sort.nearduedate = Nærmeste forfaldsdato
|
||||
issues.filter_sort.farduedate = Længste forfaldsdato
|
||||
issues.filter_sort.moststars = Flest stjerner
|
||||
editor.file_changed_while_editing = Filens indhold er ændret, siden du begyndte at redigere. <a target="_blank" rel="noopener noreferrer" href="%s">Klik her</a> for at se dem eller <strong>Bekræft ændringer igen</strong> for at overskrive dem.
|
||||
editor.file_changed_while_editing = Filens indhold er ændret, siden du begyndte at åbne den. <a target="_blank" rel="noopener noreferrer" href="%s">Klik her</a> for at se dem eller <strong>Bekræft ændringer igen</strong> for at overskrive dem.
|
||||
editor.file_already_exists = En fil med navnet "%s" findes allerede i dette depot.
|
||||
editor.commit_id_not_matching = Filen blev ændret, mens du redigerede den. Forpligt dig til en ny gren og merge derefter.
|
||||
editor.push_out_of_date = Pushet ser ud til at være forældet.
|
||||
|
@ -2722,6 +2722,9 @@ error.broken_git_hook = Git hooks af dette depot ser ud til at være brudt. Føl
|
|||
branch.create_new_branch = Opret en gren fra gren:
|
||||
branch.confirm_create_branch = Opret gren
|
||||
branch.rename_branch_to = Omdøb "%s" til:
|
||||
migrate.repo_desc_helper = Lad være tom for at importere eksisterende beskrivelse
|
||||
archive.nocomment = Det er ikke muligt at kommentere, fordi depotet er arkiveret.
|
||||
comment.blocked_by_user = Det er ikke muligt at kommentere, fordi du er blokeret af depots ejer eller forfatteren.
|
||||
|
||||
[notification]
|
||||
watching = Overvåger
|
||||
|
@ -2735,9 +2738,40 @@ mark_all_as_read = Markér alle som læste
|
|||
mark_as_unread = Markér som ulæst
|
||||
subscriptions = Abonnementer
|
||||
no_subscriptions = Ingen abonnementer
|
||||
pin = Fastgør notifikation
|
||||
|
||||
[action]
|
||||
watched_repo = begyndte at overvåge <a href="%[1]s">%[2]s</a>
|
||||
commit_repo = pushed til <a href="%[2]s">%[3]s</a> at <a href="%[1]s">%[4]s</a>
|
||||
comment_pull = `kommenterede pull request <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
mirror_sync_push = synkroniserede commits til <a href="%[2]s">%[3]s</a> ved <a href="%[1]s">%[4]s</a> fra spejl
|
||||
create_issue = `åbnet problem <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
mirror_sync_create = synkroniseret ny reference <a href="%[2]s">%[3]s</a> til <a href="%[1]s">%[4]s</a> fra spejl
|
||||
reopen_pull_request = `genåbnet pull request <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
create_repo = oprettede depot <a href="%s">%s</a>
|
||||
compare_branch = Sammenlign
|
||||
rename_repo = omdøbt depot fra <code>%[1]s</code> til <a href="%[2]s">%[3]s</a>
|
||||
reopen_issue = `genåbnet problem <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
create_pull_request = `oprettet pull-anmodning <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
close_pull_request = `lukket pull request <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
starred_repo = stjernemarkerede <a href="%[1]s">%[2]s</a>
|
||||
close_issue = `lukket problem <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
comment_issue = `kommenterede problem <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
delete_branch = slettede gren %[2]s fra <a href="%[1]s">%[3]s</a>
|
||||
compare_commits = Sammenlign %d commits
|
||||
compare_commits_general = Sammenlign commits
|
||||
review_dismissed = `afvist anmeldelse fra <b>%[4]s</b> for <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
review_dismissed_reason = Årsag:
|
||||
create_branch = oprettede gren <a href="%[2]s">%[3]s</a> i <a href="%[1]s">%[4]s</a>
|
||||
merge_pull_request = `sammenflettet pull request <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
auto_merge_pull_request = `automatisk flettet pull-anmodning <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
transfer_repo = overførte lager <code>%s</code> til <a href="%s">%s</a>
|
||||
push_tag = skubbet tag <a href="%[2]s">%[3]s</a> til <a href="%[1]s">%[4]s</a>
|
||||
delete_tag = slettede tag %[2]s fra <a href="%[1]s">%[3]s</a>
|
||||
mirror_sync_delete = synkroniseret og slettet reference <code>%[2]s</code> ved <a href="%[1]s">%[3]s</a> fra spejl
|
||||
approve_pull_request = `godkendt <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
reject_pull_request = `foreslået ændringer for <a href="%[1]s">%[3]s#%[2]s</a>`
|
||||
publish_release = `udgivet <a href="%[2]s">%[4]s</a> på <a href="%[1]s">%[3]s</a>`
|
||||
|
||||
[org]
|
||||
repo_updated = Opdateret %s
|
||||
|
@ -2914,7 +2948,7 @@ dashboard.sync_repo_branches = Synkroniser mistede grene fra Git-data til databa
|
|||
dashboard.sync_repo_tags = Synkroniser tags fra Git-data til database
|
||||
dashboard.archive_cleanup = Slet gamle depotarkiver
|
||||
dashboard.deleted_branches_cleanup = Ryd op i slettede grene
|
||||
dashboard = Dashboard
|
||||
dashboard = Instrumentpanel
|
||||
self_check = Selvtjek
|
||||
assets = Kode aktiver
|
||||
repositories = Depoter
|
||||
|
@ -3095,6 +3129,280 @@ auths.type = Type
|
|||
auths.syncenabled = Slå brugersynkronisering til
|
||||
packages.published = Offentliggjort
|
||||
packages.type = Type
|
||||
packages.cleanup = Ryd op i udløbne data
|
||||
packages.cleanup.success = Der er ryddet op i udløbne data
|
||||
defaulthooks.add_webhook = Tilføj standard webhook
|
||||
defaulthooks.update_webhook = Opdater standardwebhook
|
||||
systemhooks = System webhooks
|
||||
systemhooks.desc = Webhooks foretager automatisk HTTP POST-anmodninger til en server, når visse Forgejo-hændelser udløses. Webhooks, der er defineret her, vil virke på alle repositories på systemet, så overvej venligst eventuelle præstationsimplikationer, dette kan have. Læs mere i <a target="_blank" rel="noopener" href="%s">webhooks-guiden</a>.
|
||||
defaulthooks = Default webhooks
|
||||
defaulthooks.desc = Webhooks foretager automatisk HTTP POST-anmodninger til en server, når visse Forgejo-hændelser udløses. Webhooks defineret her er standarder og vil blive kopieret til alle nye repositories. Læs mere i <a target="_blank" rel="noopener" href="%s">webhooks-guiden</a>.
|
||||
packages.creator = Skaber
|
||||
systemhooks.update_webhook = Opdater System Webhook
|
||||
config.enable_openid_signin = Aktiver OpenID-logon
|
||||
config.cache_test = Test cache
|
||||
config.cache_item_ttl = Cache element TTL
|
||||
self_check.database_inconsistent_collation_columns = Databasen bruger sortering %s, men disse kolonner bruger uoverensstemmende sorteringer. Det kan forårsage nogle uventede problemer.
|
||||
auths.oauth2_group_claim_name = Gør krav på navn med gruppenavne for denne kilde. (Valgfri)
|
||||
auths.oauth2_map_group_to_team_removal = Fjern brugere fra synkroniserede teams, hvis brugeren ikke tilhører den tilsvarende gruppe.
|
||||
auths.tips.oauth2.general.tip = Når du registrerer en ny OAuth2-godkendelse, skal URL-adressen til tilbagekald/omdirigering være:
|
||||
auths.tip.nextcloud = Registrer en ny OAuth-bruger på din instans ved hjælp af følgende menu "Indstillinger -> Sikkerhed -> OAuth 2.0-klient"
|
||||
auths.tip.dropbox = Opret en ny applikation på %s
|
||||
auths.tip.github = Registrer en ny OAuth-applikation på %s
|
||||
auths.tip.gitlab_new = Registrer en ny applikation på %s
|
||||
auths.tip.yandex = Opret en ny applikation på %s. Vælg følgende tilladelser fra afsnittet "Yandex.Passport API": "Adgang til e-mailadresse", "Adgang til brugeravatar" og "Adgang til brugernavn, fornavn og efternavn, køn"
|
||||
config.run_mode = Kør tilstand
|
||||
config.ssh_config = SSH konfiguration
|
||||
config.allow_only_internal_registration = Tillad kun registrering gennem Forgejo selv
|
||||
config.ssh_minimum_key_size_check = Minimum nøglestørrelse kontrol
|
||||
config.enable_captcha = Aktiver CAPTCHA
|
||||
config.ssh_minimum_key_sizes = Minimum nøglestørrelser
|
||||
config.default_keep_email_private = Skjul e-mailadresser som standard
|
||||
config.default_allow_create_organization = Tillad oprettelse af organisationer som standard
|
||||
config.queue_length = Kø længde
|
||||
config.default_enable_dependencies = Aktiver problemafhængigheder som standard
|
||||
config.webhook_config = Webhook-konfiguration
|
||||
config.deliver_timeout = Lever timeout
|
||||
config.skip_tls_verify = Spring over TLS-bekræftelse
|
||||
config.mailer_config = Mailer konfiguration
|
||||
config.https_only = Kun HTTPS
|
||||
monitor.next = Næste gang
|
||||
monitor.processes_count = %d Processer
|
||||
monitor.duration = Varighed (r)
|
||||
config.session_life_time = Session levetid
|
||||
monitor.start = Starttid
|
||||
notices.deselect_all = Fravælg alle
|
||||
notices.view_detail_header = Bemærk detaljer
|
||||
notices.select_all = Vælg alle
|
||||
self_check.database_collation_case_insensitive = Databasen bruger en sortering %s, som er en ufølsom sortering. Selvom Forgejo kunne arbejde med det, kan der være nogle sjældne tilfælde, som ikke fungerer som forventet.
|
||||
config.git_max_diff_line_characters = Maks. diff-tegn pr. linje
|
||||
config.access_log_template = Skabelon til adgangslog
|
||||
monitor.process.children = Børn
|
||||
monitor.queues = Køer
|
||||
monitor.queue.settings.desc = Puljer vokser dynamisk som reaktion på deres blokering af arbejderkø.
|
||||
auths.auth_manage_panel = Administrer godkendelseskilder
|
||||
auths.auth_type = Godkendelsestype
|
||||
auths.map_group_to_team_removal = Fjern brugere fra synkroniserede teams, hvis brugeren ikke tilhører den tilsvarende LDAP-gruppe
|
||||
auths.attribute_name = Fornavn attribut
|
||||
auths.map_group_to_team = Tilknyt LDAP-grupper til organisationsteams (lad feltet stå tomt for at springe over)
|
||||
auths.helo_hostname = HELO værtsnavn
|
||||
auths.helo_hostname_helper = Værtsnavn sendt med HELO. Lad stå tomt for at sende det nuværende værtsnavn.
|
||||
auths.disable_helo = Deaktiver HELO
|
||||
auths.oauth2_authURL = Godkend URL
|
||||
auths.oauth2_profileURL = Profil-URL
|
||||
auths.skip_local_two_fa_helper = Hvis du ikke er indstillet, betyder det, at lokale brugere med 2FA indstillet stadig skal bestå 2FA for at logge på
|
||||
auths.oauth2_required_claim_name_helper = Indstil dette navn for at begrænse login fra denne kilde til brugere med et krav med dette navn
|
||||
auths.tips.gmail_settings = Gmail-indstillinger:
|
||||
auths.update_success = Godkendelseskilden er blevet opdateret.
|
||||
auths.tip.mastodon = Indtast en brugerdefineret instans-URL for den mastodon-instans, du vil godkende med (eller brug standarden)
|
||||
auths.activated = Denne godkendelseskilde er aktiveret
|
||||
auths.new_success = Godkendelsen "%s" er blevet tilføjet.
|
||||
config.enable_openid_signup = Aktiver OpenID-selvregistrering
|
||||
config.show_registration_button = Vis registreringsknap
|
||||
config.require_sign_in_view = Kræv at logge ind for at se indhold
|
||||
config.cache_conn = Cacheforbindelse
|
||||
config.xorm_log_sql = Log SQL
|
||||
monitor.stacktrace = Stakspor
|
||||
monitor.execute_time = Udførelsestid
|
||||
monitor.last_execution_result = Resultat
|
||||
monitor.process.cancel_notices = Annuller: <strong>%s</strong>?
|
||||
monitor.queue.type = Type
|
||||
monitor.queue.exemplar = Eksempler type
|
||||
auths.auth_name = Godkendelsesnavn
|
||||
auths.attribute_username = Brugernavn attribut
|
||||
auths.attribute_username_placeholder = Lad stå tomt for at bruge brugernavnet indtastet i Forgejo.
|
||||
auths.enable_ldap_groups = Aktiver LDAP-grupper
|
||||
auths.ms_ad_sa = MS AD søgeattributter
|
||||
auths.smtpport = SMTP port
|
||||
auths.allowed_domains = Tilladte domæner
|
||||
auths.skip_tls_verify = Spring over TLS-bekræftelse
|
||||
auths.force_smtps = Tving SMTPS
|
||||
auths.force_smtps_helper = SMTPS bruges altid på port 465. Indstil denne til at tvinge SMTPS på andre porte. (Ellers vil STARTTLS blive brugt på andre porte, hvis det understøttes af værten.)
|
||||
auths.oauth2_emailURL = E-mail URL
|
||||
auths.oauth2_scopes = Yderligere omfang
|
||||
auths.oauth2_admin_group = Gruppekravværdi for administratorbrugere. (Valgfrit – kræver kravnavn ovenfor)
|
||||
auths.oauth2_restricted_group = Gruppekravsværdi for begrænsede brugere. (Valgfrit – kræver kravnavn ovenfor)
|
||||
auths.oauth2_map_group_to_team = Kortlæg hævdede grupper til organisationsteams. (Valgfrit – kræver kravnavn ovenfor)
|
||||
auths.tip.google_plus = Hent OAuth2-klientlegitimationsoplysninger fra Google API-konsollen på %s
|
||||
auths.tip.openid_connect = Brug OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) til at angive slutpunkterne
|
||||
auths.tip.gitea = Registrer en ny OAuth2-applikation. Guide kan findes på %s
|
||||
auths.edit = Rediger godkendelseskilden
|
||||
auths.update = Opdater godkendelseskilden
|
||||
config.offline_mode = Lokal tilstand
|
||||
config.disable_router_log = Deaktiver routerlog
|
||||
config.git_version = Git version
|
||||
config.app_data_path = Appens datasti
|
||||
config.repo_root_path = Depotets rodsti
|
||||
config.log_file_root_path = Logsti
|
||||
config.script_type = Script type
|
||||
config.reverse_auth_user = Omvendt proxy-godkendelsesbruger
|
||||
config.ssh_key_test_path = Nøgleteststi
|
||||
config.ssh_keygen_path = Keygen ("ssh-keygen") sti
|
||||
config.active_code_lives = Aktiveringskodens udløbstid
|
||||
config.no_reply_address = Skjult e-mail-domæne
|
||||
config.mailer_enable_helo = Aktiver HELO
|
||||
config.mailer_protocol = Protokol
|
||||
config.mailer_use_sendmail = Brug Sendmail
|
||||
config.mailer_sendmail_path = Sendmail sti
|
||||
config.mailer_sendmail_args = Ekstra argumenter til Sendmail
|
||||
config.cache_test_failed = Kunne ikke undersøge cachen: %v.
|
||||
config.cache_test_slow = Cachetest lykkedes, men svaret er langsomt: %s.
|
||||
config.gc_interval_time = GC interval tid
|
||||
config.git_max_diff_files = Max diff filer vist
|
||||
config.git_gc_args = GC argumenter
|
||||
monitor.queue.settings.maxnumberworkers.error = Max antal arbejdere skal være et tal
|
||||
notices.inverse_selection = Omvendt valg
|
||||
notices.delete_selected = Slet valgte
|
||||
auths.allowed_domains_helper = Lad være tomt for at tillade alle domæner. Adskil flere domæner med et komma (",").
|
||||
auths.skip_local_two_fa = Spring over lokal 2FA
|
||||
auths.oauth2_required_claim_value_helper = Indstil denne værdi for at begrænse login fra denne kilde til brugere med et krav med dette navn og denne værdi
|
||||
monitor.queue.settings.maxnumberworkers = Max antal arbejdere
|
||||
notices.delete_all = Slet alle meddelelser
|
||||
auths.smtp_auth = SMTP-godkendelsestype
|
||||
config.default_visibility_organization = Standardsynlighed for nye organisationer
|
||||
auths.tip.facebook = Registrer en ny applikation på %s og tilføj produktet "Facebook Login"
|
||||
auths.tip.twitter = Gå til %s, opret en applikation og sørg for, at "Tillad, at denne applikation bruges til at logge på med Twitter" er aktiveret
|
||||
self_check.database_fix_mysql = For MySQL/MariaDB-brugere kan du bruge kommandoen "forgejo doctor convert" til at løse sorteringsproblemerne, eller du kan også løse problemet ved at "ALTER ... COLLATE ..." SQLs manuelt.
|
||||
auths.tips.oauth2.general = OAuth2-godkendelse
|
||||
systemhooks.add_webhook = Tilføj System Webhook
|
||||
auths.new = Tilføj godkendelseskilde
|
||||
auths.unable_to_initialize_openid = Kan ikke initialisere OpenID Connect Provider: %s
|
||||
auths.tip.oauth2_provider = OAuth2-udbyder
|
||||
auths.tip.bitbucket = Registrer en ny OAuth-bruger på %s og tilføj tilladelsen "Konto" - "Læs"
|
||||
auths.login_source_exist = Godkendelseskilden "%s" findes allerede.
|
||||
auths.invalid_openIdConnectAutoDiscoveryURL = Ugyldig Auto Discovery URL (dette skal være en gyldig URL, der starter med http:// eller https://)
|
||||
config.lfs_root_path = LFS rodsti
|
||||
auths.login_source_of_type_exist = Der findes allerede en godkendelseskilde af denne type.
|
||||
config.lfs_content_path = LFS indholdssti
|
||||
config.lfs_http_auth_expiry = LFS HTTP-godkendelsesudløbstid
|
||||
config.disable_register = Deaktiver selvregistrering
|
||||
config.register_email_confirm = Kræv e-mail-bekræftelse for at tilmelde dig
|
||||
config.mailer_user = Bruger
|
||||
auths.openIdConnectAutoDiscoveryURL = OpenID Connect Auto Discovery URL
|
||||
auths.oauth2_use_custom_url = Brug tilpassede webadresser i stedet for standardwebadresser
|
||||
auths.pam_service_name = PAM-tjenestenavn
|
||||
auths.pam_email_domain = PAM e-mail domæne (valgfrit)
|
||||
auths.oauth2_provider = OAuth2-udbyder
|
||||
auths.oauth2_clientID = Klient-id (nøgle)
|
||||
auths.oauth2_clientSecret = Klientens hemmelighed
|
||||
auths.oauth2_required_claim_value = Påkrævet kravværdi
|
||||
config.db_config = Database konfiguration
|
||||
config.db_host = Vært
|
||||
config.ssh_start_builtin_server = Brug indbygget server
|
||||
config.lfs_config = LFS-konfiguration
|
||||
config.service_config = Tjenestekonfiguration
|
||||
config.mailer_smtp_port = SMTP-port
|
||||
config.disabled_logger = Deaktiveret
|
||||
config.access_log_mode = Adgang til logtilstand
|
||||
config.allow_only_external_registration = Tillad kun registrering via eksterne tjenester
|
||||
config.mailer_smtp_addr = SMTP vært
|
||||
config.mailer_sendmail_timeout = Sendmail timeout
|
||||
auths.oauth2_tokenURL = Token URL
|
||||
auths.oauth2_tenant = Lejer
|
||||
config.custom_conf = Konfigurationsfilstien
|
||||
config.custom_file_root_path = Brugerdefineret fil rodsti
|
||||
config.app_ver = Forgejo version
|
||||
config.app_url = Base URL
|
||||
config.ssh_domain = SSH server domæne
|
||||
config.ssh_port = Port
|
||||
config.ssh_listen_port = Lyt port
|
||||
config.ssh_root_path = Rodsti
|
||||
config.db_type = Type
|
||||
config.reset_password_code_lives = Gendannelseskodens udløbstid
|
||||
config.mailer_use_dummy = Attrapp
|
||||
config.test_mail_failed = Kunne ikke sende en test-e-mail til "%s": %v
|
||||
config.cache_adapter = Cache adapter
|
||||
config.test_mail_sent = En test-e-mail er blevet sendt til "%s".
|
||||
config.oauth_config = OAuth-konfiguration
|
||||
config.test_email_placeholder = E-mail (f.eks. test@example.com)
|
||||
config.send_test_mail = Send test-e-mail
|
||||
config.send_test_mail_submit = Send
|
||||
config.cache_config = Cache konfiguration
|
||||
config.cookie_name = Cookie navn
|
||||
config.session_config = Sessionskonfiguration
|
||||
config.session_provider = Session udbyder
|
||||
config.provider_config = Udbyder konfig
|
||||
notices.op = Op.
|
||||
notices.delete_success = Systemmeddelelserne er blevet slettet.
|
||||
self_check.no_problem_found = Intet problem fundet endnu.
|
||||
self_check.database_collation_mismatch = Forvent, at databasen bruger sortering: %s
|
||||
auths.user_dn = Bruger DN
|
||||
auths.bind_dn = Bind DN
|
||||
auths.bind_password = Bind adgangskode
|
||||
auths.user_base = Bruger søgebase
|
||||
auths.verify_group_membership = Bekræft gruppemedlemskab i LDAP (lad filteret stå tomt for at springe over)
|
||||
auths.group_search_base = Gruppesøgningsbase DN
|
||||
auths.smtphost = SMTP vært
|
||||
auths.oauth2_required_claim_name = Påkrævet kravnavn
|
||||
auths.tips = Tips
|
||||
auths.delete = Slet godkendelseskilden
|
||||
auths.delete_auth_title = Slet godkendelseskilden
|
||||
auths.delete_auth_desc = Sletning af en godkendelseskilde forhindrer brugere i at bruge den til at logge ind. Vil du fortsætte?
|
||||
auths.still_in_used = Godkendelseskilden er stadig i brug. Konverter eller slet alle brugere, der bruger denne godkendelseskilde først.
|
||||
auths.deletion_success = Godkendelseskilden er blevet slettet.
|
||||
config.run_user = Bruger at køre som
|
||||
config.mail_notify = Aktiver e-mailmeddelelser
|
||||
config.git_migrate_timeout = Migration timeout
|
||||
config.log_config = Log konfiguration
|
||||
auths.default_domain_name = Standard domænenavn, der bruges til e-mailadressen
|
||||
auths.attribute_mail = E-mail attribut
|
||||
auths.allow_deactivate_all = Tillad et tomt søgeresultat for at deaktivere alle brugere
|
||||
auths.use_paged_search = Brug sidesøgning
|
||||
auths.search_page_size = Sidestørrelse
|
||||
auths.attribute_ssh_public_key = Offentlig SSH-nøgleattribut
|
||||
auths.attribute_avatar = Avatar attribut
|
||||
auths.attributes_in_bind = Hent attributter i bind DN-kontekst
|
||||
config.app_name = Instans titel
|
||||
config.app_slogan = instans slogan
|
||||
config.cache_interval = Cache interval
|
||||
monitor.queue.settings.title = Pool indstillinger
|
||||
notices.type = Type
|
||||
notices.type_2 = Opgave
|
||||
config.db_schema = Skematisk
|
||||
config.db_ssl_mode = SSL
|
||||
config.db_path = Sti
|
||||
notices.operations = Operationer
|
||||
auths.restricted_filter_helper = Lad være tom for ikke at angive nogen brugere som begrænset. Brug en stjerne ("*") for at indstille alle brugere, der ikke matcher Admin-filteret, som begrænset.
|
||||
auths.group_attribute_list_users = Gruppeattribut, der indeholder en liste over brugere
|
||||
auths.tip.discord = Registrer en ny applikation på %s
|
||||
config.server_config = Server konfiguration
|
||||
config.domain = Server domæne
|
||||
config.set_setting_failed = Indstilling af %s mislykkedes
|
||||
monitor.previous = Tidligere tid
|
||||
monitor.process = Kørende processer
|
||||
monitor.execute_times = Udførelser
|
||||
monitor.download_diagnosis_report = Hent diagnoserapport
|
||||
monitor.process.cancel = Annuller processen
|
||||
monitor.process.cancel_desc = Annullering af en proces kan medføre tab af data
|
||||
monitor.queue = Kø: %s
|
||||
monitor.queue.numberworkers = Antal arbejdere
|
||||
monitor.queue.activeworkers = Aktive arbejdere
|
||||
monitor.queue.maxnumberworkers = Max antal arbejdere
|
||||
monitor.queue.numberinqueue = Nummer i kø
|
||||
monitor.queue.review_add = Gennemgå / tilføj arbejdere
|
||||
config.git_pull_timeout = Pull Operation timeout
|
||||
config.git_clone_timeout = Klone Operation timeout
|
||||
config.git_gc_timeout = GC Operation timeout
|
||||
config.logger_name_fmt = Logger: %s
|
||||
monitor.stats = Statistik
|
||||
monitor.cron = Cron opgaver
|
||||
monitor.schedule = Tidsplan
|
||||
config.cache_test_succeeded = Cachetest gennemført, fik et svar i %s.
|
||||
config.cookie_life_time = Cookie levetid
|
||||
config.picture_config = Billede og avatar konfiguration
|
||||
config.picture_service = Billedservice
|
||||
config.disable_gravatar = Deaktiver Gravatar
|
||||
config.enable_federated_avatar = Aktiver fødererede avatarer
|
||||
config.open_with_editor_app_help = "Åbn med"-editorerne til klonmenuen. Hvis den efterlades tom, vil standarden blive brugt. Udvid for at se standarden.
|
||||
config.git_config = Git konfiguration
|
||||
config.git_disable_diff_highlight = Deaktiver diff-syntaksfremhævning
|
||||
config.git_max_diff_lines = Max diff-linjer pr. fil
|
||||
config.git_mirror_timeout = Spejlopdateringstimeout
|
||||
auths.attribute_surname = Efternavn attribut
|
||||
auths.filter = Bruger filter
|
||||
auths.admin_filter = Admin filter
|
||||
auths.restricted_filter = Begrænset filter
|
||||
auths.user_attribute_in_group = Brugerattribut angivet i gruppen
|
||||
|
||||
[packages]
|
||||
arch.version.description = Beskrivelse
|
||||
|
@ -3146,6 +3454,134 @@ dependency.version = Version
|
|||
alpine.registry = Konfigurer dette register ved at tilføje url'en i din <code>/etc/apk/repositories</code> fil:
|
||||
alpine.registry.key = Download den offentlige RSA-nøgle til registreringsdatabasen i mappen <code>/etc/apk/keys/</code> for at bekræfte indekssignaturen:
|
||||
alpine.registry.info = Vælg $branch og $repository fra listen nedenfor.
|
||||
empty = Der er ingen pakker endnu.
|
||||
filter.type.all = Alle
|
||||
filter.container.untagged = Umærket
|
||||
about = Om denne pakke
|
||||
filter.no_result = Dit filter gav ingen resultater.
|
||||
dependencies = Afhængigheder
|
||||
empty.documentation = For mere information om pakkeregistret, se <a target="_blank" rel="noopener noreferrer" href="%s">dokumentationen</a>.
|
||||
filter.type = Type
|
||||
registry.documentation = For mere information om %s registreringsdatabasen, se <a target="_blank" rel="noopener noreferrer" href="%s">dokumentationen</a>.
|
||||
title = Pakker
|
||||
desc = Administrer depotpakker.
|
||||
empty.repo = Har du uploadet en pakke, men den vises ikke her? Gå til <a href="%[1]s">pakkeindstillinger</a> og link den til denne repo.
|
||||
filter.container.tagged = Tagget
|
||||
published_by = Udgivet %[1]s af <a href="%[2]s">%[3]s</a>
|
||||
published_by_in = Udgivet %[1]s af <a href="%[2]s">%[3]s</a> i <a href="%[4]s"><strong>%[5]s</strong></a>
|
||||
installation = Installation
|
||||
requirements = Krav
|
||||
cran.registry = Konfigurer dette register i din <code>Rprofile.site</code> fil:
|
||||
rubygems.required.rubygems = Kræver RubyGem version
|
||||
owner.settings.chef.title = Kokkeregister
|
||||
owner.settings.chef.keypair = Generer nøglepar
|
||||
arch.pacman.repo.multi.item = Konfiguration for %s
|
||||
arch.pacman.sync = Synkroniser pakke med pacman:
|
||||
arch.version.properties = Versionsegenskaber
|
||||
arch.version.provides = Forsyner
|
||||
arch.version.checkdepends = Check afhænger
|
||||
arch.version.replaces = Erstatter
|
||||
conan.install = For at installere pakken ved hjælp af Conan skal du køre følgende kommando:
|
||||
conda.registry = Konfigurer dette register som et Conda-depot i din <code>.condarc</code>-fil:
|
||||
conda.install = For at installere pakken ved hjælp af Conda skal du køre følgende kommando:
|
||||
container.images.title = Billeder
|
||||
container.details.type = Billedtype
|
||||
container.details.platform = Platform
|
||||
container.pull = Træk billedet fra kommandolinjen:
|
||||
container.digest = Fordøje
|
||||
alt.setup = Tilføj et depot til listen over tilsluttede arkiver (vælg den nødvendige arkitektur i stedet for "_arch_"):
|
||||
vagrant.install = For at tilføje en Vagrant-boks skal du køre følgende kommando:
|
||||
swift.install2 = og kør følgende kommando:
|
||||
settings.link = Link denne pakke til et depot
|
||||
settings.link.success = Depotlinket blev opdateret.
|
||||
owner.settings.cargo.initialize.description = Et særligt indeks Git-depot er nødvendigt for at bruge Cargo-registret. Brug af denne mulighed vil (gen-)oprette depotet og konfigurere det automatisk.
|
||||
settings.delete.notice = Du er ved at slette %s (%s). Denne operation er uigenkaldeligt, er du sikker?
|
||||
owner.settings.cargo.title = Lastregisterindeks
|
||||
owner.settings.cargo.initialize = Initialiser indeks
|
||||
owner.settings.cleanuprules.preview.none = Oprydningsreglen matcher ikke nogen pakker.
|
||||
owner.settings.cleanuprules.none = Der er endnu ingen oprydningsregler.
|
||||
owner.settings.cleanuprules.keep.count.1 = 1 version pr. pakke
|
||||
owner.settings.cleanuprules.preview.overview = %d pakker er planlagt til at blive fjernet.
|
||||
owner.settings.cleanuprules.keep.pattern.container = Den <code>seneste</code> version bevares altid for containerpakker.
|
||||
settings.delete = Slet pakke
|
||||
settings.delete.description = Sletning af en pakke er permanent og kan ikke fortrydes.
|
||||
owner.settings.cleanuprules.keep.count.n = %d versioner pr. pakke
|
||||
arch.version.makedepends = Gør afhænger
|
||||
alt.install = Installer pakken
|
||||
composer.registry = Konfigurer dette register i din <code>~/.composer/config.json</code> fil:
|
||||
composer.dependencies = Afhængigheder
|
||||
settings.delete.success = Pakken er blevet slettet.
|
||||
settings.delete.error = Kunne ikke slette pakken.
|
||||
owner.settings.cargo.rebuild.error = Kunne ikke genopbygge Cargo-indeks: %v
|
||||
owner.settings.cargo.rebuild = Genopbyg indeks
|
||||
owner.settings.cleanuprules.preview = Forhåndsvisning af oprydningsregel
|
||||
owner.settings.cleanuprules.keep.count = Behold den nyeste
|
||||
owner.settings.cleanuprules.keep.pattern = Hold versionerne matchende
|
||||
owner.settings.chef.keypair.description = Et nøglepar er nødvendigt for at autentificere til Chef-registret. Hvis du har genereret et nøglepar før, vil generering af et nyt nøglepar kassere det gamle nøglepar.
|
||||
maven.install = For at bruge pakken skal du inkludere følgende i blokken <code>afhængigheder</code> i filen <code>pom.xml</code>:
|
||||
details = Detaljer
|
||||
cargo.registry = Konfigurer dette register i Cargo-konfigurationsfilen (for eksempel <code>~/.cargo/config.toml</code>):
|
||||
cargo.install = For at installere pakken ved hjælp af Cargo skal du køre følgende kommando:
|
||||
composer.install = For at installere pakken ved hjælp af Composer skal du køre følgende kommando:
|
||||
container.multi_arch = OS / Arch
|
||||
rubygems.required.ruby = Kræver Ruby version
|
||||
swift.install = Tilføj pakken i din <code>Package.swift</code>-fil:
|
||||
settings.link.select = Vælg Depot
|
||||
settings.link.button = Opdater Depot Link
|
||||
settings.link.error = Kunne ikke opdatere depotlinket.
|
||||
owner.settings.cargo.initialize.success = Cargo-indekset blev oprettet.
|
||||
owner.settings.cargo.rebuild.description = Genopbygning kan være nyttig, hvis indekset ikke er synkroniseret med de lagrede Cargo-pakker.
|
||||
owner.settings.cargo.rebuild.success = Cargo-indekset blev genopbygget med succes.
|
||||
owner.settings.cleanuprules.add = Tilføj oprydningsregel
|
||||
owner.settings.cleanuprules.edit = Rediger oprydningsregel
|
||||
owner.settings.cleanuprules.title = Oprydningsregler
|
||||
maven.registry = Konfigurer denne registreringsdatabasen i din projekt <code>pom.xml</code> fil:
|
||||
npm.install2 = eller føj det til filen package.json:
|
||||
nuget.dependency.framework = Mål Framework
|
||||
npm.registry = Konfigurer denne registreringsdatabase i din projekt-<code>.npmrc</code>-fil:
|
||||
nuget.install = For at installere pakken ved hjælp af NuGet skal du køre følgende kommando:
|
||||
npm.dependencies = Afhængigheder
|
||||
settings.link.description = Hvis du forbinder en pakke med et depot, vises pakken i depotets pakkeliste.
|
||||
owner.settings.cargo.initialize.error = Kunne ikke initialisere Cargo index: %v
|
||||
owner.settings.cleanuprules.keep.title = Versioner, der matcher disse regler, bevares, selvom de matcher en fjernelsesregel nedenfor.
|
||||
generic.download = Download pakken fra kommandolinjen:
|
||||
go.install = Installer pakken fra kommandolinjen:
|
||||
container.layers = Billedlag
|
||||
container.labels.key = Nøgle
|
||||
container.labels.value = Værdi
|
||||
debian.registry.info = Vælg $distribution og $component fra listen nedenfor.
|
||||
maven.download = For at downloade afhængigheden skal du køre via kommandolinjen:
|
||||
rpm.distros.suse = på SUSE-baserede distributioner
|
||||
rpm.distros.redhat = på RedHat-baserede distributioner
|
||||
owner.settings.cleanuprules.pattern_full_match = Anvend mønster på det fulde pakkenavn
|
||||
details.author = Forfatter
|
||||
details.repository_site = Depots hjemmeside
|
||||
details.documentation_site = Dokumentations hjemmeside
|
||||
details.license = Licens
|
||||
assets = Aktiver
|
||||
versions = Versioner
|
||||
details.project_site = Projektets hjemmeside
|
||||
versions.view_all = Se alle
|
||||
dependency.id = ID
|
||||
alpine.repository.branches = Grene
|
||||
arch.version.optdepends = Valgfri afhænger
|
||||
owner.settings.cleanuprules.remove.title = Versioner, der matcher disse regler, fjernes, medmindre en regel ovenfor siger, at de skal beholdes.
|
||||
owner.settings.cleanuprules.remove.days = Fjern versioner ældre end
|
||||
owner.settings.cleanuprules.remove.pattern = Fjern matchende versioner
|
||||
owner.settings.cleanuprules.success.update = Oprydningsreglen er blevet opdateret.
|
||||
owner.settings.cleanuprules.success.delete = Oprydningsregel er blevet slettet.
|
||||
arch.version.backup = Backup
|
||||
chef.registry = Konfigurer dette register i din <code>~/.chef/config.rb</code> fil:
|
||||
npm.install = For at installere pakken ved hjælp af npm skal du køre følgende kommando:
|
||||
owner.settings.cargo.rebuild.no_index = Kan ikke genopbygge, intet indeks er initialiseret.
|
||||
maven.install2 = Kør via kommandolinje:
|
||||
keywords = Keywords
|
||||
arch.pacman.helper.gpg = Tilføj tillidscertifikat til pacman:
|
||||
arch.pacman.repo.multi = %s har den samme version i forskellige distributioner.
|
||||
arch.pacman.conf = Tilføj server med relateret distribution og arkitektur til <code>/etc/pacman.conf</code>:
|
||||
arch.version.groups = Gruppe
|
||||
arch.version.depends = Afhænger
|
||||
arch.version.conflicts = Konflikter
|
||||
|
||||
[actions]
|
||||
runners.description = Beskrivelse
|
||||
|
@ -3155,6 +3591,94 @@ runners.task_list.repository = Depot
|
|||
runners.status.active = Aktiv
|
||||
runners.status.offline = Offline
|
||||
runners.version = Version
|
||||
runners.owner_type = Type
|
||||
runners = Runners
|
||||
unit.desc = Administrer integrerede CI/CD-pipelines med Forgejo Actions.
|
||||
status.unknown = Ukendt
|
||||
runners.runner_title = Runner
|
||||
runners.task_list = Seneste opgaver på denne løber
|
||||
runners.task_list.run = Kør
|
||||
runners.task_list.commit = Commit
|
||||
runners.edit_runner = Rediger Runner
|
||||
runs.commit = Commit
|
||||
runs.scheduled = Planlagt
|
||||
runs.pushed_by = pushed af
|
||||
status.running = Kører
|
||||
status.waiting = Venter
|
||||
runners.new_notice = Hvordan man starter en runner
|
||||
status.success = Succes
|
||||
variables.not_found = Variablen kunne ikke findes.
|
||||
runs.workflow = Arbejdsgang
|
||||
runners.last_online = Sidste online tid
|
||||
runners.task_list.done_at = Udført kl
|
||||
runners.update_runner = Opdater ændringer
|
||||
runners.update_runner_success = Runner blev opdateret
|
||||
runners.update_runner_failed = Løberen kunne ikke opdateres
|
||||
runners.delete_runner_failed = Runner kunne ikke slettes
|
||||
runners.delete_runner_header = Bekræft for at slette denne runner
|
||||
runners.status.idle = Tomgang
|
||||
runs.no_job_without_needs = Arbejdsgangen skal indeholde mindst ét job uden afhængigheder.
|
||||
runs.no_job = Arbejdsgangen skal indeholde mindst ét job
|
||||
runs.no_results = Ingen resultater matchede.
|
||||
runs.no_workflows = Der er endnu ingen arbejdsgange.
|
||||
workflow.enable = Aktiver arbejdsgang
|
||||
workflow.enable_success = Arbejdsgangen "%s" blev aktiveret.
|
||||
variables.none = Der er ingen variabler endnu.
|
||||
variables.edit = Rediger variabel
|
||||
variables.deletion.success = Variablen er blevet fjernet.
|
||||
variables.creation.failed = Kunne ikke tilføje variabel.
|
||||
runners.delete_runner_notice = Hvis en opgave kører på denne runner, vil den blive afsluttet og markeret som mislykket. Det kan bryde bygningens arbejdsgang.
|
||||
runs.no_workflows.help_write_access = Ved du ikke, hvordan du starter med Forgejo Actions? Tjek <a target="_blank" rel="noopener noreferrer" href="%s">hurtigstarten i brugerdokumentationen</a> for at skrive dit første workflow, og <a target="_blank" rel="noopener noreferrer" href="%s">opsæt en Forgejo-løber</a> til at udføre dine opgaver.
|
||||
runners.delete_runner_success = Runner blev slettet
|
||||
variables.update.success = Variablen er blevet redigeret.
|
||||
status.cancelled = Annulleret
|
||||
status.skipped = Oversprunget
|
||||
status.blocked = Blokeret
|
||||
workflow.disable_success = Arbejdsgangen "%s" blev deaktiveret.
|
||||
workflow.disable = Deaktiver arbejdsgang
|
||||
workflow.dispatch.use_from = Brug arbejdsgangen fra
|
||||
workflow.disabled = Arbejdsgangen er deaktiveret.
|
||||
workflow.dispatch.trigger_found = Denne arbejdsgang har en <c>workflow_dispatch</c>-hændelsestrigger.
|
||||
workflow.dispatch.warn_input_limit = Viser kun de første %d input.
|
||||
workflow.dispatch.success = Kørsel af arbejdsgang blev anmodet om.
|
||||
workflow.dispatch.input_required = Kræv værdi for input "%s".
|
||||
workflow.dispatch.invalid_input_type = Ugyldig inputtype "%s".
|
||||
variables.creation = Tilføj variabel
|
||||
need_approval_desc = Har brug for godkendelse for at køre arbejdsgange for fork pull-anmodning.
|
||||
runners.delete_runner = Slet denne runner
|
||||
runners.status.unspecified = Ukendt
|
||||
runners.reset_registration_token_success = Runner registreringstoken blev nulstillet
|
||||
runs.all_workflows = Alle arbejdsgange
|
||||
runners.reset_registration_token = Nulstil registreringstoken
|
||||
runs.empty_commit_message = (tom commit besked)
|
||||
runs.expire_log_message = Logfiler er blevet renset, fordi de var for gamle.
|
||||
variables = Variabler
|
||||
runs.actor = Aktør
|
||||
actions = Handlinger
|
||||
runners.status = Status
|
||||
runners.task_list.status = Status
|
||||
runners.id = ID
|
||||
runners.task_list.no_tasks = Der er ingen opgave endnu.
|
||||
runs.status = Status
|
||||
runs.actors_no_select = Alle aktører
|
||||
runs.status_no_select = Alle status
|
||||
runners.none = Ingen runners tilgængelige
|
||||
variables.management = Administrer variabler
|
||||
variables.creation.success = Variablen "%s" er blevet tilføjet.
|
||||
variables.update.failed = Variablen kunne ikke redigeres.
|
||||
runs.no_workflows.help_no_write_access = For at lære om Forgejo Actions, se <a target="_blank" rel="noopener noreferrer" href="%s">dokumentationen</a>.
|
||||
variables.deletion = Fjern variabel
|
||||
variables.id_not_exist = Variabel med ID %d findes ikke.
|
||||
variables.deletion.description = Fjernelse af en variabel er permanent og kan ikke fortrydes. Vil du fortsætte?
|
||||
variables.description = Variabler vil blive videregivet til visse handlinger og kan ikke læses på anden vis.
|
||||
variables.deletion.failed = Variablen kunne ikke fjernes.
|
||||
runs.no_matching_online_runner_helper = Ingen matchende online-runner med etiket: %s
|
||||
runners.runner_manage_panel = Administrer runners
|
||||
runners.new = Opret ny runner
|
||||
workflow.dispatch.run = Kør arbejdsgang
|
||||
runs.invalid_workflow_helper = Workflow-konfigurationsfilen er ugyldig. Tjek venligst din konfigurationsfil: %s
|
||||
status.failure = Fiasko
|
||||
runs.no_runs = Workflowet har ingen kørsler endnu.
|
||||
|
||||
[tool]
|
||||
1d = 1 dag
|
||||
|
@ -3174,6 +3698,7 @@ weeks = %d uger
|
|||
1s = 1 sekund
|
||||
1m = 1 minut
|
||||
days = %d dage
|
||||
now = nu
|
||||
|
||||
[repo.permissions]
|
||||
actions.read = <b>Læs:</b> Se integrerede CI/CD-pipelines og deres logfiler.
|
||||
|
@ -3207,10 +3732,67 @@ component_loading_failed = Kunne ikke indlæse %s
|
|||
[secrets]
|
||||
creation.name_placeholder = uafhængig af store og små bogstaver, alfanumeriske tegn eller understregninger, kan ikke starte med GITEA_ eller GITHUB_
|
||||
creation.value_placeholder = Indtast ethvert indhold. Mellemrum i starten og slutningen vil blive udeladt.
|
||||
creation = Tilføj hemmelighed
|
||||
description = Hemmeligheder vil blive videregivet til visse handlinger og kan ikke læses på anden vis.
|
||||
secrets = Hemmeligheder
|
||||
creation.success = Hemmeligheden "%s" er blevet tilføjet.
|
||||
deletion = Fjern hemmelighed
|
||||
deletion.success = Hemmeligheden er blevet fjernet.
|
||||
deletion.failed = Hemmeligheden kunne ikke fjernes.
|
||||
management = Håndter hemmeligheder
|
||||
deletion.description = Fjernelse af en hemmelighed er permanent og kan ikke fortrydes. Vil du fortsætte?
|
||||
none = Der er ingen hemmeligheder endnu.
|
||||
creation.failed = Kunne ikke tilføje hemmelighed.
|
||||
|
||||
[dropzone]
|
||||
invalid_input_type = Filer af denne type må ikke uploades.
|
||||
remove_file = Fjern fil
|
||||
default_message = Slip filer eller klik her for at uploade.
|
||||
file_too_big = Filstørrelsen ({{filesize}} MB) overstiger den maksimale størrelse på ({{maxFilesize}} MB).
|
||||
|
||||
[gpg]
|
||||
default_key = Underskrevet med standardnøglen
|
||||
default_key = Underskrevet med standardnøglen
|
||||
error.generate_hash = Kunne ikke generere hash af commit
|
||||
error.no_committer_account = Ingen konto knyttet til committers e-mailadresse
|
||||
error.probable_bad_default_signature = ADVARSEL! Selvom standardnøglen har dette ID, bekræfter den ikke denne commit! Denne commit er MISTÆNLIG.
|
||||
error.no_gpg_keys_found = Ingen kendt nøgle fundet for denne signatur i databasen
|
||||
error.not_signed_commit = Ikke en underskrevet commit
|
||||
error.failed_retrieval_gpg_keys = Kunne ikke hente nogen nøgle knyttet til committerens konto
|
||||
error.probable_bad_signature = ADVARSEL! Selvom der er en nøgle med dette ID i databasen, bekræfter den ikke denne commit! Denne commit er MISTÆNLIG.
|
||||
error.extract_sign = Kunne ikke udtrække signatur
|
||||
|
||||
[munits.data]
|
||||
kib = KiB
|
||||
b = B
|
||||
pib = PiB
|
||||
mib = MiB
|
||||
tib = TiB
|
||||
eib = EiB
|
||||
gib = GiB
|
||||
|
||||
[units]
|
||||
error.no_unit_allowed_repo = Du har ikke tilladelse til at få adgang til nogen sektion af dette depot.
|
||||
unit = Enhed
|
||||
error.unit_not_allowed = Du har ikke tilladelse til at få adgang til denne depotsektion.
|
||||
|
||||
[projects]
|
||||
type-3.display_name = Organisationsprojekt
|
||||
type-2.display_name = Depotprojekt
|
||||
deleted.display_name = Slettet projekt
|
||||
type-1.display_name = Individuelt projekt
|
||||
|
||||
[markup]
|
||||
filepreview.line = Linje %[1]d i %[2]s
|
||||
filepreview.lines = Linjer %[1]d til %[2]d i %[3]s
|
||||
filepreview.truncated = Forhåndsvisningen er blevet afkortet
|
||||
|
||||
[git.filemode]
|
||||
symbolic_link = Symbolsk link
|
||||
submodule = Submodule
|
||||
changed_filemode = %[1]s → %[2]s
|
||||
directory = Directory
|
||||
normal_file = Normal fil
|
||||
executable_file = Eksekverbar fil
|
||||
|
||||
[translation_meta]
|
||||
test = (OK) DA was translated by Tacaly
|
|
@ -110,7 +110,7 @@ preview=Vorschau
|
|||
loading=Laden …
|
||||
|
||||
error=Fehler
|
||||
error404=Die Seite, die du versuchst aufzurufen, <strong>existiert nicht</strong> oder <strong>du bist nicht berechtigt</strong>, diese anzusehen.
|
||||
error404=Die Seite, die du versuchst aufzurufen, <strong>existiert nicht</strong>, <strong>wurde entfernt</strong> oder <strong>du bist nicht berechtigt</strong>, diese anzusehen.
|
||||
go_back=Zurück
|
||||
|
||||
never=Niemals
|
||||
|
@ -1073,7 +1073,7 @@ quota.applies_to_user = Die folgenden Quota-Regeln greifen für deinen Account
|
|||
quota.sizes.assets.attachments.issues = Issue-Anhänge
|
||||
quota.rule.exceeded.helper = Die Gesamtgröße der Objekte für diese Regel hat die Quota überschritten.
|
||||
storage_overview = Speicherübersicht
|
||||
quota = Quota
|
||||
quota = Kontingent
|
||||
quota.sizes.assets.attachments.releases = Release-Anhänge
|
||||
quota.applies_to_org = Die folgenden Quota-Regeln greifen für diese Organisation
|
||||
quota.rule.exceeded = Überschritten
|
||||
|
@ -2911,6 +2911,9 @@ issues.reopen.blocked_by_user = Du kannst dieses Issue nicht wieder eröffnen, w
|
|||
pulls.comment.blocked_by_user = Du kannst diesen Pull-Request nicht kommentieren, da du vom Repository-Besitzer oder Ersteller des Pull-Requests blockiert wurdest.
|
||||
issues.filter_no_results = Keine Ergebnisse
|
||||
issues.filter_no_results_placeholder = Versuche, deine Suchfilter anzupassen.
|
||||
migrate.repo_desc_helper = Leer lassen, um vorhandene Beschreibung zu importieren
|
||||
archive.nocomment = Kommentieren ist nicht möglich, da das Repository archiviert ist.
|
||||
comment.blocked_by_user = Kommentieren ist nicht möglich, da du vom Repository-Besitzer oder vom Autor blockiert wurdest.
|
||||
|
||||
[graphs]
|
||||
component_loading_failed = Konnte %s nicht laden
|
||||
|
|
|
@ -2460,9 +2460,9 @@ settings.protect_branch_name_pattern=Μοτίβο προστατευμένου
|
|||
settings.protect_branch_name_pattern_desc=Μοτίβα ονόματος προστατευμένων κλάδων. Συμβολευτείτε <a href="%s">την τεκμηρίωση</a> για την σύνταξη ενός μοτίβου. Παραδείγματα: main, release/**
|
||||
settings.protect_patterns=Μοτίβα
|
||||
settings.protect_protected_file_patterns=Μοτίβα προστατευμένων αρχείων (διαχωρισμός με semicolon «;» και ΟΧΙ το ελληνικό ερωτηματικό):
|
||||
settings.protect_protected_file_patterns_desc=Τα προστατευόμενα αρχεία δεν επιτρέπεται να αλλάξουν άμεσα, ακόμη και αν ο χρήστης έχει δικαιώματα να προσθέσει, να επεξεργαστεί ή να διαγράψει αρχεία σε αυτόν τον κλάδο. Επιπλέων μοτίβα μπορούν να διαχωριστούν με semicolon («;») (ΟΧΙ ερωτηματικό). Για να συντάξετε μοτίβα, συμβουλευτείται την τεκμηρίωση <a href='%s'>%s</a>. Παράδειγμα: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_protected_file_patterns_desc=Τα προστατευόμενα αρχεία δεν επιτρέπεται να αλλάξουν άμεσα, ακόμη και αν ο χρήστης έχει δικαιώματα να προσθέσει, να επεξεργαστεί ή να διαγράψει αρχεία σε αυτόν τον κλάδο. Επιπλέων μοτίβα μπορούν να διαχωριστούν με semicolon («;») (ΟΧΙ ερωτηματικό). Για να συντάξετε μοτίβα, συμβουλευτείται την τεκμηρίωση <a href="%[1]s">%[2]s</a>. Παράδειγμα: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns=Μοτίβα μη προστατευμένων αρχείων (διαχωρισμένα με semicolon «;» και ΟΧΙ το ελληνικό ερωτηματικό):
|
||||
settings.protect_unprotected_file_patterns_desc=Μη προστατευμένα αρχεία που επιτρέπεται να αλλάξουν απευθείας εάν ο χρήστης έχει πρόσβαση εγγραφής, παρακάμπτοντας τον περιορισμό ώθησης. Επιπλέων μοτίβα μπορούν να διαχωριστούν με ερωτηματικό (';'). Δείτε την τεκμηρίωση <a href='%[1]s'>%[2]s</a> για τη σύνταξη του μοτίβου. Πχ: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns_desc=Μη προστατευμένα αρχεία που επιτρέπεται να αλλάξουν απευθείας εάν ο χρήστης έχει πρόσβαση εγγραφής, παρακάμπτοντας τον περιορισμό ώθησης. Επιπλέων μοτίβα μπορούν να διαχωριστούν με ερωτηματικό (';'). Δείτε την τεκμηρίωση <a href="%[1]s">%[2]s</a> για τη σύνταξη του μοτίβου. Πχ: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.add_protected_branch=Ενεργοποίηση προστασίας
|
||||
settings.delete_protected_branch=Απενεργοποίηση προστασίας
|
||||
settings.update_protect_branch_success=Η προστασία κλάδου για τον κανόνα «%s» ενημερώθηκε.
|
||||
|
|
|
@ -1436,7 +1436,7 @@ editor.file_is_a_symlink = `"%s" is a symbolic link. Symbolic links cannot be ed
|
|||
editor.filename_is_a_directory = Filename "%s" is already used as a directory name in this repository.
|
||||
editor.file_editing_no_longer_exists = The file being edited, "%s", no longer exists in this repository.
|
||||
editor.file_deleting_no_longer_exists = The file being deleted, "%s", no longer exists in this repository.
|
||||
editor.file_changed_while_editing = The file contents have changed since you started editing. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit changes again</strong> to overwrite them.
|
||||
editor.file_changed_while_editing = The file contents have changed since you opened the file. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit changes again</strong> to overwrite them.
|
||||
editor.file_already_exists = A file named "%s" already exists in this repository.
|
||||
editor.commit_id_not_matching = The file was changed while you were editing it. Commit to a new branch and then merge.
|
||||
editor.push_out_of_date = The push appears to be out of date.
|
||||
|
|
|
@ -209,6 +209,7 @@ table_modal.placeholder.content = Contenido
|
|||
link_modal.header = Añadir enlace
|
||||
link_modal.description = Descripción
|
||||
link_modal.paste_reminder = Pista: Con una URL en tu portapapeles, puedes pegar directamente en el editor para crear un enlace.
|
||||
link_modal.url = URL
|
||||
|
||||
[filter]
|
||||
string.asc=A - Z
|
||||
|
@ -1062,6 +1063,18 @@ pronouns_custom_label = Pronombres personalizados
|
|||
user_block_yourself = No puede bloquearse a sí mismo.
|
||||
quota = Cuota
|
||||
quota.sizes.assets.attachments.all = Archivos adjuntos
|
||||
quota.sizes.repos.private = Repositorios privados
|
||||
quota.sizes.assets.packages.all = Paquetes
|
||||
regenerate_token = Regenerar
|
||||
quota.sizes.all = Todo
|
||||
quota.sizes.repos.all = Repositorios
|
||||
quota.sizes.repos.public = Repositorios públicos
|
||||
quota.sizes.git.all = Contenido de Git
|
||||
quota.sizes.wiki = Wiki
|
||||
keep_pronouns_private = Mostrar pronombres solo a personas autenticadas
|
||||
storage_overview = Resumen del almacenamiento
|
||||
quota.sizes.assets.artifacts = Artefactos
|
||||
quota.sizes.assets.attachments.releases = Archivos adjuntos del lanzamiento
|
||||
|
||||
[repo]
|
||||
owner=Propietario
|
||||
|
@ -1141,7 +1154,7 @@ forks=Forks
|
|||
reactions_more=y %d más
|
||||
unit_disabled=El administrador del sitio ha deshabilitado esta sección del repositorio.
|
||||
language_other=Otros
|
||||
adopt_search=Introduzca el nombre de usuario para buscar repositorios no adoptados... (déjelo en blanco para encontrar todos)
|
||||
adopt_search=Introduzca el nombre de usuario para buscar repositorios no adoptados… (déjelo en blanco para encontrar todos)
|
||||
adopt_preexisting_label=Adoptar archivos
|
||||
adopt_preexisting=Adoptar archivos preexistentes
|
||||
adopt_preexisting_content=Crear repositorio desde %s
|
||||
|
@ -2348,7 +2361,7 @@ settings.event_pull_request_comment_desc=Comentario de pull request creado, edit
|
|||
settings.event_pull_request_review=Revisiones
|
||||
settings.event_pull_request_review_desc=Pull request aprobada, rechazada o comentarios de revisión añadidos.
|
||||
settings.event_pull_request_sync=Sincronizado
|
||||
settings.event_pull_request_sync_desc=Pull request sincronizado.
|
||||
settings.event_pull_request_sync_desc=La rama se actualizó automáticamente con la rama de destino.
|
||||
settings.event_pull_request_review_request=Solicitudes de revisión
|
||||
settings.event_pull_request_review_request_desc=La solicitud de Pull Request ha sido eliminada.
|
||||
settings.event_pull_request_approvals=Aprobaciones de pull request
|
||||
|
@ -2450,9 +2463,9 @@ settings.require_signed_commits_desc=Rechazar push en esta rama si los commits n
|
|||
settings.protect_branch_name_pattern=Patrón de nombre de ramas protegidas
|
||||
settings.protect_patterns=Patrones
|
||||
settings.protect_protected_file_patterns=Patrones de archivos protegidos (separados con punto y coma ';')
|
||||
settings.protect_protected_file_patterns_desc=No está permitido cambiar archivos directamente incluso si el usuario tiene permiso para agregar, editar o borrar archivos en esta rama. Múltiples patrones pueden separarse usando punto y coma (';'). Refvisa la documentación de <a href='%s'>%s</a> para la sintaxis de patrones. Ejemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_protected_file_patterns_desc=No está permitido cambiar archivos directamente incluso si el usuario tiene permiso para agregar, editar o borrar archivos en esta rama. Múltiples patrones pueden separarse usando punto y coma (';'). Refvisa la documentación de <a href="%[1]s">%[2]s</a> para la sintaxis de patrones. Ejemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns=Patrones de archivos sin protección (separados con punto y coma ";")
|
||||
settings.protect_unprotected_file_patterns_desc=Los archivos sin protección se pueden cambiar directamente si el usuario tiene acceso de escritura, evitando la restricción push. Múltiples patrones pueden separarse usando punto y coma (';'). Vea la documentación de <a href='%[1]s'>%[2]s</a> para la sintaxis de patrones. Ejemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns_desc=Los archivos sin protección se pueden cambiar directamente si el usuario tiene acceso de escritura, evitando la restricción push. Múltiples patrones pueden separarse usando punto y coma (';'). Vea la documentación de <a href="%[1]s">%[2]s</a> para la sintaxis de patrones. Ejemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.add_protected_branch=Activar protección
|
||||
settings.delete_protected_branch=Desactivar protección
|
||||
settings.update_protect_branch_success=Se ha actualizado la protección de la rama para la regla "%s".
|
||||
|
@ -2542,10 +2555,10 @@ diff.parent=padre
|
|||
diff.commit=commit
|
||||
diff.git-notes=Notas
|
||||
diff.data_not_available=El contenido del diff no está disponible
|
||||
diff.options_button=Opciones de diff
|
||||
diff.options_button=Opciones de diferencias
|
||||
diff.show_diff_stats=Mostrar estadísticas
|
||||
diff.download_patch=Descargar archivo de parche
|
||||
diff.download_diff=Descargar archivo de diff
|
||||
diff.download_diff=Descargar archivo de diferencias
|
||||
diff.show_split_view=Dividir vista
|
||||
diff.show_unified_view=Vista unificada
|
||||
diff.whitespace_button=Espacio blanco
|
||||
|
@ -2848,6 +2861,7 @@ pulls.cmd_instruction_checkout_title = Cambiar rama
|
|||
release.add_external_asset = Añadir un recurso externo
|
||||
settings.enforce_on_admins_desc = Los administradores del repositorio no pueden saltarse esta regla.
|
||||
pulls.editable = Editable
|
||||
issues.filter_no_results = No hay resultados
|
||||
|
||||
[graphs]
|
||||
component_loading = Cargando %s...
|
||||
|
@ -3152,7 +3166,7 @@ emails.primary=Principal
|
|||
emails.activated=Activado
|
||||
emails.filter_sort.email=Correo electrónico
|
||||
emails.filter_sort.email_reverse=Email (invertir)
|
||||
emails.filter_sort.name=Nombre de Usuario
|
||||
emails.filter_sort.name=Nombre de usuario
|
||||
emails.filter_sort.name_reverse=Nombre de usuario (invertir)
|
||||
emails.updated=Email actualizado
|
||||
emails.not_updated=Error al actualizar la dirección de correo electrónico solicitada: %v
|
||||
|
@ -3808,11 +3822,11 @@ unit.desc=Gestione procesos CI/CD integrados con Forgejo Actions.
|
|||
|
||||
status.unknown=Desconocido
|
||||
status.waiting=Esperando
|
||||
status.running=Ejecutando
|
||||
status.running=Corriendo
|
||||
status.success=Éxito
|
||||
status.failure=Fallo
|
||||
status.cancelled=Cancelado
|
||||
status.skipped=Omitido
|
||||
status.skipped=Saltado
|
||||
status.blocked=Bloqueado
|
||||
|
||||
runners=Nodos
|
||||
|
@ -3864,8 +3878,8 @@ runs.status_no_select=Todo el estado
|
|||
runs.no_results=No hay resultados coincidentes.
|
||||
runs.no_runs=El flujo de trabajo no tiene ejecuciones todavía.
|
||||
|
||||
workflow.disable=Desactivar fllujo de trabajo
|
||||
workflow.disable_success=Flujo de trabajo '%s' deshabilitado correctamente.
|
||||
workflow.disable=Desactivar flujo de trabajo
|
||||
workflow.disable_success=Flujo de trabajo "%s" desactivado exitosamente.
|
||||
workflow.enable=Activar flujo de trabajo
|
||||
workflow.enable_success=Flujo de trabajo '%s' habilitado con éxito.
|
||||
workflow.disabled=El flujo de trabajo está deshabilitado.
|
||||
|
@ -3889,10 +3903,11 @@ variables.update.success=La variable ha sido editada.
|
|||
variables.id_not_exist = Variable con id %d no existe.
|
||||
runs.empty_commit_message = (mensaje de commit vacío)
|
||||
runs.expire_log_message = Los registros han sido eliminados porque eran demasiado antiguos.
|
||||
runs.workflow = Flujo de trabajo
|
||||
|
||||
[projects]
|
||||
type-1.display_name=Proyecto individual
|
||||
type-2.display_name=Proyecto repositorio
|
||||
type-2.display_name=Proyecto de repositorio
|
||||
type-3.display_name=Proyecto de organización
|
||||
deleted.display_name = Proyecto borrado
|
||||
|
||||
|
@ -3907,24 +3922,24 @@ submodule=Submódulo
|
|||
|
||||
|
||||
[search]
|
||||
search = Buscar...
|
||||
search = Buscar…
|
||||
type_tooltip = Tipo de búsqueda
|
||||
project_kind = Buscar proyectos...
|
||||
branch_kind = Buscar ramas...
|
||||
commit_kind = Buscar confirmaciones...
|
||||
repo_kind = Buscar repositorios...
|
||||
user_kind = Buscar usuarios...
|
||||
org_kind = Buscar organizaciones...
|
||||
team_kind = Buscar equipos...
|
||||
code_kind = Buscar código...
|
||||
package_kind = Buscar paquetes...
|
||||
project_kind = Buscar proyectos…
|
||||
branch_kind = Buscar ramas…
|
||||
commit_kind = Buscar confirmaciones…
|
||||
repo_kind = Buscar repositorios…
|
||||
user_kind = Buscar usuarios…
|
||||
org_kind = Buscar organizaciones…
|
||||
team_kind = Buscar equipos…
|
||||
code_kind = Buscar código…
|
||||
package_kind = Buscar paquetes…
|
||||
code_search_unavailable = La búsqueda de código no está disponible actualmente. Por favor contacta al administrador del sitio.
|
||||
code_search_by_git_grep = Los resultados actuales de la búsqueda de código son proporcionados por "git grep". Es posible que se obtengan mejores resultados si el administrador del sitio habilita el indexador de código.
|
||||
no_results = No se encontraron resultados coincidentes.
|
||||
keyword_search_unavailable = La búsqueda por palabra clave no está disponible actualmente. Por favor contacta al administrador del sitio.
|
||||
fuzzy_tooltip = Incluir resultados que también coincidan estrechamente con el término de búsqueda
|
||||
milestone_kind = Buscar hitos…
|
||||
pull_kind = Buscar pulls…
|
||||
pull_kind = Buscar extracciones…
|
||||
union = Unión
|
||||
union_tooltip = Incluir resultados correspondientes a cualquiera de las palabras clave separadas por espacios en blanco
|
||||
exact = Exacto
|
||||
|
@ -3938,6 +3953,7 @@ regexp = Expresión Regular
|
|||
[markup]
|
||||
filepreview.lines = Líneas %[1]d a %[2]d en %[3]s
|
||||
filepreview.line = Línea %[1]d en %[2]s
|
||||
filepreview.truncated = La vista previa se ha truncado
|
||||
|
||||
[repo.permissions]
|
||||
pulls.read = <b>Lectura:</b> Leer y crear pull requests.
|
||||
|
@ -3956,3 +3972,13 @@ ext_wiki = Acceder al enlace de la wiki externa. Los permisos se gestionan de fo
|
|||
code.write = <b>Escritura:</b> Push al repositorio, crear ramas y tags.
|
||||
issues.write = <b>Escritura:</b> Cerrar incidencias y gestion de metadatos como etiquetas, hitos, asignaciones, fechas de vencimiento y dependencias.
|
||||
packages.write = <b>Escritura:</b> Publicar y eliminar paquetes asignados al repositorio.
|
||||
|
||||
|
||||
[munits.data]
|
||||
eib = EiB
|
||||
gib = GiB
|
||||
pib = PiB
|
||||
kib = KiB
|
||||
tib = TiB
|
||||
mib = MiB
|
||||
b = B
|
|
@ -2767,6 +2767,9 @@ issues.reopen.blocked_by_user = Hindi mo maaaring buksan muli ang isyung ito dah
|
|||
pulls.comment.blocked_by_user = Hindi ka maaaring magkomento sa hiling sa paghila na ito dahil hinarang ka ng may-ari ng repositoryo o ng may-akda ng hiling sa paghila.
|
||||
issues.filter_no_results = Walang mga resulta
|
||||
issues.filter_no_results_placeholder = Subukang ayusin ang iyong mga filter sa paghahanap.
|
||||
migrate.repo_desc_helper = Iwanang walang laman para i-import ang umiiral na paglalarawan
|
||||
archive.nocomment = Hindi posible ang pagkomento dahil naka-archive ang repositoryo.
|
||||
comment.blocked_by_user = Hindi posible ang pagkomento dahil hinarang ka ng may-ari ng repositoryo o ng may-akda.
|
||||
|
||||
[search]
|
||||
commit_kind = Maghanap ng mga commit…
|
||||
|
|
|
@ -207,6 +207,8 @@ table_modal.label.rows = Lignes
|
|||
table_modal.label.columns = Colonnes
|
||||
link_modal.header = Ajouter un lien
|
||||
link_modal.url = Url
|
||||
link_modal.description = Description
|
||||
link_modal.paste_reminder = Indice : avec une URL dans votre clipboard, vous pouvez copier directement dans l'éditeur pour créer un lien.
|
||||
|
||||
[filter]
|
||||
string.asc=A - Z
|
||||
|
@ -3982,7 +3984,7 @@ search = Rechercher…
|
|||
type_tooltip = Type de recherche
|
||||
fuzzy = Approximatif
|
||||
code_search_by_git_grep = Les résultats de recherche dans le code sont fournis par "git grep". Les résultats pourraient être plus pertinents si l'administrateur du site active les indexeurs de code source.
|
||||
runner_kind = Chercher les runners...
|
||||
runner_kind = Chercher les runners…
|
||||
no_results = Aucun résultat n'a été trouvé.
|
||||
keyword_search_unavailable = La recherche par mot-clé n'est pas disponible actuellement. Veuillez contacter l'administrateur du site.
|
||||
fuzzy_tooltip = Inclure les résultats proches des termes recherchés
|
||||
|
@ -3996,14 +3998,14 @@ code_kind = Chercher le code…
|
|||
code_search_unavailable = La recherche dans le code n'est pas disponible. Veuillez contacter l'administrateur du site.
|
||||
package_kind = Chercher les paquets…
|
||||
project_kind = Chercher les projets…
|
||||
branch_kind = Chercher les branches...
|
||||
commit_kind = Chercher les commits...
|
||||
branch_kind = Chercher les branches…
|
||||
commit_kind = Chercher les commits…
|
||||
exact = Exact
|
||||
exact_tooltip = Inclure uniquement les résultats qui correspondent exactement au terme recherché
|
||||
issue_kind = Rechercher dans les tickets...
|
||||
issue_kind = Rechercher dans les tickets…
|
||||
union = Union
|
||||
union_tooltip = Inclus les résultats contenant au moins un des mots clé séparés par des espaces
|
||||
pull_kind = Rechercher dans les demande d'ajout...
|
||||
pull_kind = Rechercher dans les demande d'ajout
|
||||
milestone_kind = Recherche dans les jalons...
|
||||
regexp_tooltip = Interpréter le terme de recherche comme une expression régulière
|
||||
regexp = RegExp
|
||||
|
|
|
@ -21,4 +21,22 @@ signed_in_as = साइन इन किया गया है
|
|||
link_account = खाता लिंक करें
|
||||
notifications = सूचनाएँ
|
||||
tracked_time_summary = समस्याओं की सूची के फ़िल्टर के आधार पर ट्रैक किए गए समय का सारांश
|
||||
user_profile_and_more = प्रोफ़ाइल और सेटिंग्स…
|
||||
user_profile_and_more = प्रोफ़ाइल और सेटिंग्स…
|
||||
enable_javascript = इस पन्ने के लिये जावा चाहिए
|
||||
toc = विषय सूची
|
||||
licenses = लाइसेंस
|
||||
more_items = और वस्तुएं
|
||||
username = यूजरनाम
|
||||
email = ईमेल पता
|
||||
password = पासवर्ड
|
||||
re_type = पासवर्ड पक्का करें
|
||||
captcha = कैप्चा
|
||||
twofa = टू-फैक्टर ऑथेंटिकेशन
|
||||
twofa_scratch = टू-फैक्टर स्क्रैच कोड
|
||||
passcode = पास कोड
|
||||
repository = भंडार
|
||||
organization = संगठन
|
||||
mirror = छवि
|
||||
settings = सेटिंग्स
|
||||
your_settings = आपकी सेटिंग्स
|
||||
return_to_forgejo = फ़ोर्जेगो पे वापस जाएं
|
|
@ -1418,7 +1418,7 @@ issues.new.assignees=Assegnatari
|
|||
issues.new.clear_assignees=Cancella assegnatari
|
||||
issues.new.no_assignees=Nessun assegnatario
|
||||
issues.new.no_reviewers=Nessun revisore
|
||||
issues.choose.get_started=Cominciare
|
||||
issues.choose.get_started=Comincia
|
||||
issues.choose.open_external_link=Apri
|
||||
issues.choose.blank=Default
|
||||
issues.choose.blank_about=Crea una segnalazione dal modello predefinito.
|
||||
|
|
|
@ -2448,9 +2448,9 @@ settings.protect_branch_name_pattern=保護ブランチ名のパターン
|
|||
settings.protect_branch_name_pattern_desc=保護ブランチ名のパターン。書き方については <a href="%s">ドキュメント</a> を参照してください。例: main, release/**
|
||||
settings.protect_patterns=パターン
|
||||
settings.protect_protected_file_patterns=保護されるファイルのパターン (セミコロン';'で区切る):
|
||||
settings.protect_protected_file_patterns_desc=保護されたファイルは、このブランチにファイルを追加・編集・削除する権限を持つユーザーであっても、直接変更することができなくなります。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については <a href='%s'>%s</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>
|
||||
settings.protect_protected_file_patterns_desc=保護されたファイルは、このブランチにファイルを追加・編集・削除する権限を持つユーザーであっても、直接変更することができなくなります。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については <a href="%[1]s">%[2]s</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>
|
||||
settings.protect_unprotected_file_patterns=保護しないファイルのパターン (セミコロン';'で区切る):
|
||||
settings.protect_unprotected_file_patterns_desc=保護しないファイルは、ユーザーに書き込み権限があればプッシュ制限をバイパスして直接変更できます。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については <a href='%[1]s'>%[2]s</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>
|
||||
settings.protect_unprotected_file_patterns_desc=保護しないファイルは、ユーザーに書き込み権限があればプッシュ制限をバイパスして直接変更できます。 セミコロン(';')で区切って複数のパターンを指定できます。 パターンの文法については <a href="%[1]s">%[2]s</a> を参照してください。 例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>
|
||||
settings.add_protected_branch=保護を有効にする
|
||||
settings.delete_protected_branch=保護を無効にする
|
||||
settings.update_protect_branch_success=ルール "%s" に対するブランチ保護を更新しました。
|
||||
|
|
839
options/locale/locale_ka.ini
Normal file
839
options/locale/locale_ka.ini
Normal file
|
@ -0,0 +1,839 @@
|
|||
|
||||
|
||||
|
||||
[common]
|
||||
register = რეგისტრაცია
|
||||
pin = ამაგრება
|
||||
copy = კოპირება
|
||||
filter = ფილტრი
|
||||
edit = ჩასწორება
|
||||
home = საწყისი
|
||||
dashboard = სამუშაო მაგიდა
|
||||
explore = დათვალიერება
|
||||
help = დახმარება
|
||||
logo = ლოგო
|
||||
sign_in_or = ან
|
||||
sign_up = რეგისტრაცია
|
||||
language = ენა
|
||||
username = მომხმარებლის სახელი
|
||||
version = ვერსია
|
||||
page = გვერდი
|
||||
template = ნიმუში
|
||||
notifications = გაფრთხილებები
|
||||
create_new = შექმნა…
|
||||
licenses = ლიცენზიები
|
||||
password = პაროლი
|
||||
captcha = კაპჩა
|
||||
passcode = საკვანძო კოდი
|
||||
repository = რეპოზიტორია
|
||||
organization = ორგანიზაცია
|
||||
mirror = სარკე
|
||||
settings = მორგება
|
||||
your_profile = პროფილი
|
||||
your_starred = ვარსკვლავიანი
|
||||
your_settings = მორგება
|
||||
all = ყველა
|
||||
sources = წყაროები
|
||||
mirrors = სარკეები
|
||||
collaborative = საერთო
|
||||
forks = ფორკები
|
||||
activities = აქტივობები
|
||||
issues = პრობლემები
|
||||
milestones = მისაღწევი გეგმები
|
||||
ok = დიახ
|
||||
cancel = გაუქმება
|
||||
retry = თავიდან ცდა
|
||||
rerun = თავიდან გაშვება
|
||||
save = შენახვა
|
||||
add = დამატება
|
||||
remove = წაშლა
|
||||
view = ხედი
|
||||
test = შემოწმება
|
||||
enabled = ჩართულია
|
||||
disabled = გამორთულია
|
||||
locked = ჩაკეტილია
|
||||
write = ჩაწერა
|
||||
preview = მინიატურა
|
||||
loading = ჩატვირთვა…
|
||||
error = შეცდომა
|
||||
never = არასდროს
|
||||
unknown = უცნობი
|
||||
unpin = ჩამოხსნა
|
||||
artifacts = არტეფაქტები
|
||||
concept_user_individual = ინდივიდუალური
|
||||
concept_code_repository = რეპოზიტორია
|
||||
concept_user_organization = ორგანიზაცია
|
||||
name = სახელი
|
||||
value = მნიშვნელობა
|
||||
archived = დაარქივებული
|
||||
filter.is_archived = დაარქივებული
|
||||
filter.is_fork = ფორკები
|
||||
filter.is_mirror = სარკეები
|
||||
filter.is_template = ნიმუშები
|
||||
filter.private = პირადი
|
||||
copy_success = დაკოპირებულია!
|
||||
concept_system_global = გლობალური
|
||||
filter.public = საჯარო
|
||||
sign_in = შესვლა
|
||||
sign_out = გასვლა
|
||||
link_account = ანგარიშის მიბმა
|
||||
toggle_menu = მენიუს გადართვა
|
||||
more_items = მეტი ელემენტი
|
||||
email = ელფოსტის მისამართი
|
||||
access_token = წვდომის ტოკენი
|
||||
re_type = დაადასტურეთ პაროლი
|
||||
twofa = 2FA
|
||||
new_mirror = ახალი სარკე
|
||||
new_project = ახალი პროექტი
|
||||
new_project_column = ახალი სვეტი
|
||||
admin_panel = საიტის ადმინისტრირება
|
||||
new_repo.title = ახალი რეპოზიტორია
|
||||
new_migrate.title = ახალი მიგრაცია
|
||||
new_org.title = ახალი ორგანიზაცია
|
||||
new_repo.link = ახალი რეპოზიტორია
|
||||
new_migrate.link = ახალი მიგრაცია
|
||||
new_org.link = ახალი ორგანიზაცია
|
||||
pull_requests = შერწყმის მოთხოვნები
|
||||
add_all = ყველას დამატება
|
||||
remove_all = ყველას წაშლა
|
||||
copy_url = ბმულის კოპირება
|
||||
copy_hash = ჰეშის კოპირება
|
||||
copy_path = ბილიკის კოპირება
|
||||
copy_content = შემცველობის კოპირება
|
||||
copy_error = კოპირება ჩავარდა
|
||||
|
||||
[install]
|
||||
path = ბილიკი
|
||||
install = დაყენება
|
||||
host = ჰოსტი
|
||||
user = მომხმარებლის სახელი
|
||||
password = პაროლი
|
||||
db_schema = სქემა
|
||||
ssl_mode = SSL
|
||||
admin_password = პაროლი
|
||||
|
||||
[search]
|
||||
search = ძებნა…
|
||||
fuzzy = გაურკვეველი
|
||||
union = გაერთიანება
|
||||
exact = ზუსტი
|
||||
regexp = რეგგამოსი
|
||||
|
||||
[aria]
|
||||
footer = ქვედა კოლონტიტული
|
||||
footer.links = ბმულები
|
||||
|
||||
[heatmap]
|
||||
contributions_one = შეწირულობა
|
||||
contributions_few = შეწირულობები
|
||||
less = ნაკლები
|
||||
more = მეტი
|
||||
|
||||
[editor]
|
||||
table_modal.placeholder.header = ზედა კოლონტიტული
|
||||
table_modal.placeholder.content = შემცველობა
|
||||
table_modal.label.rows = მწკრივი
|
||||
table_modal.label.columns = სვეტი
|
||||
link_modal.url = Url
|
||||
link_modal.description = აღწერა
|
||||
|
||||
[home]
|
||||
my_repos = რეპოზიტორიები
|
||||
show_archived = დაარქივებული
|
||||
my_orgs = ორგანიზაციები
|
||||
show_private = პირადი
|
||||
|
||||
[explore]
|
||||
repos = რეპოზიტორიები
|
||||
organizations = ორგანიზაციები
|
||||
code = კოდი
|
||||
users = მომხმარებლები
|
||||
|
||||
[auth]
|
||||
openid_connect_submit = მიერთება
|
||||
verify = გადამოწმება
|
||||
|
||||
[mail]
|
||||
release.downloads = გადმოწერები:
|
||||
repo.transfer.to_you = თქვენ
|
||||
release.note = შენიშვნა:
|
||||
|
||||
[modal]
|
||||
yes = დიახ
|
||||
no = არა
|
||||
confirm = დადასტურება
|
||||
cancel = გაუქმება
|
||||
modify = განახლება
|
||||
|
||||
[form]
|
||||
Description = აღწერა
|
||||
Pronouns = ნაცვალსახელები
|
||||
Biography = ბიოგრაფია
|
||||
Website = ვებგვერდი
|
||||
Location = მდებარეობა
|
||||
Content = შემცველობა
|
||||
UserName = მომხმარებლის სახელი
|
||||
Password = პაროლი
|
||||
|
||||
[user]
|
||||
repositories = რეპოზიტორიები
|
||||
followers.title.one = მომყოლი
|
||||
followers.title.few = მომყოლები
|
||||
following.title.one = მიჰყვებით
|
||||
following.title.few = მიჰყვებით
|
||||
follow = მიყოლა
|
||||
unfollow = მიყოლის გაუქმება
|
||||
code = კოდი
|
||||
projects = პროექტები
|
||||
overview = გადახედვა
|
||||
block = დაბლოკვა
|
||||
unblock = განბლოკვა
|
||||
user_bio = ბიოგრაფია
|
||||
|
||||
[settings]
|
||||
profile = პროფილი
|
||||
account = ანგარიში
|
||||
appearance = გარეგნობა
|
||||
password = პაროლი
|
||||
security = უსაფრთხოება
|
||||
avatar = ავატარი
|
||||
applications = აპები
|
||||
orgs = ორგანიზაციები
|
||||
repos = რეპოზიტორიები
|
||||
organization = ორგანიზაციები
|
||||
uid = UID
|
||||
quota = კვოტა
|
||||
location = მდებარეობა
|
||||
pronouns = ნაცვალსახელები
|
||||
continue = გაგრძელება
|
||||
cancel = გაუქმება
|
||||
language = ენა
|
||||
ui = თემა
|
||||
hints = მინიშნებები
|
||||
comment_type_group_reference = მიმართვა
|
||||
comment_type_group_milestone = მისაღწევი მიზანი
|
||||
comment_type_group_assignee = მიმნიჭებელი
|
||||
comment_type_group_title = სათაური
|
||||
comment_type_group_branch = ბრენჩი
|
||||
comment_type_group_deadline = პროექტის ვადა
|
||||
comment_type_group_dependency = დამოკიდებულება
|
||||
comment_type_group_project = პროექტი
|
||||
privacy = კონფიდენციალობა
|
||||
primary = ძირითადი
|
||||
activated = გააქტიურებულია
|
||||
delete_email = წაშლა
|
||||
gpg_key_verify = გადამოწმება
|
||||
gpg_token = ტოკენი
|
||||
ssh_key_verify = გადამოწმება
|
||||
ssh_token = ტოკენი
|
||||
subkeys = ქვეგასაღებები
|
||||
key_content = შემცველობა
|
||||
principal_content = შემცველობა
|
||||
delete_key = წაშლა
|
||||
can_read_info = წაკითხვა
|
||||
can_write_info = ჩაწერა
|
||||
permission_read = წაკითხვა
|
||||
permissions_list = წვდომები:
|
||||
pronouns_unspecified = მითითებული არაა
|
||||
regenerate_token = რეგენერაცია
|
||||
website = ვებგვერდი
|
||||
comment_type_group_label = ჭდე
|
||||
delete_token = წაშლა
|
||||
save_application = შენახვა
|
||||
oauth2_application_edit = ჩასწორება
|
||||
revoke_key = გაუქმება
|
||||
visibility.public = საჯარო
|
||||
visibility.limited = შეზღუდული
|
||||
quota.rule.exceeded = გადაცილებულია
|
||||
quota.rule.no_limit = შეუზღუდავი
|
||||
quota.sizes.all = ყველა
|
||||
quota.sizes.repos.all = რეპოზიტორიები
|
||||
quota.sizes.assets.artifacts = არტეფაქტები
|
||||
quota.sizes.assets.packages.all = პაკეტები
|
||||
quota.sizes.wiki = ვიკი
|
||||
visibility.private = პირადი
|
||||
quota.sizes.assets.all = ობიექტები
|
||||
webauthn_nickname = მეტსახელი
|
||||
quota.sizes.assets.attachments.all = მიმაგრებული ფაილები
|
||||
|
||||
[startpage]
|
||||
lightweight = მსუბუქი
|
||||
platform = პლატფორმათაშორისი
|
||||
|
||||
[repo]
|
||||
owner = მფლობელი
|
||||
template = ნიმუში
|
||||
visibility = ხილვადობა
|
||||
repo_desc = აღწერა
|
||||
repo_lang = ენა
|
||||
issue_labels = ჭდეები
|
||||
license = ლიცენზია
|
||||
readme = README
|
||||
default_branch_label = ნაგულისხმევი
|
||||
mirror_prune = გასუფთავება
|
||||
mirror_sync = სინქრონიზებულია
|
||||
mirror_password_placeholder = (არ შეცვლილა)
|
||||
mirror_password_blank_placeholder = (დაყენება გაუქმებულია)
|
||||
watchers = მაყურებლები
|
||||
stargazers = ვარსკვლავთმრიცხველები
|
||||
forks = ფორკები
|
||||
stars = ვარსკვლავები
|
||||
language_other = სხვა
|
||||
delete_preexisting_label = წაშლა
|
||||
desc.public = საჯარო
|
||||
desc.template = ნიმუში
|
||||
desc.internal = შიდა
|
||||
desc.sha256 = SHA256
|
||||
template.webhooks = ვებჰუკები
|
||||
template.topics = თემები
|
||||
template.avatar = ავატარი
|
||||
need_auth = ავტორიზაცია
|
||||
migrate_items_wiki = ვიკი
|
||||
migrate_items_labels = ჭდეები
|
||||
migrate_items_issues = პრობლემები
|
||||
migrate_items_releases = რელიზები
|
||||
star = ვარსკვლავი
|
||||
unstar = ვარსკვლავის მოხსნა
|
||||
fork = ფორკი
|
||||
code = კოდი
|
||||
branch = ბრენჩი
|
||||
branches = ბრენჩები
|
||||
tag = ჭდე
|
||||
tags = ჭდეები
|
||||
issues = პრობლემები
|
||||
packages = პაკეტები
|
||||
actions = ქმედებები
|
||||
release = რელიზი
|
||||
releases = რელიზები
|
||||
labels = ჭდეები
|
||||
org_labels_desc_manage = მართვა
|
||||
commits = კომიტები
|
||||
commit = კომიტი
|
||||
file_raw = დაუმუშავებელი
|
||||
file_history = ისტორია
|
||||
escape_control_characters = დაეკრანება
|
||||
unescape_control_characters = დაეკრანების მოხსნა
|
||||
vendored = გარედან მოწოდებული
|
||||
generated = დაგენერირებული
|
||||
commit_graph.monochrome = მონო
|
||||
commit_graph.color = ფერი
|
||||
blame = საავტორო უფლებები
|
||||
line = ხაზი
|
||||
lines = ხაზები
|
||||
from_comment = (კომენტარი)
|
||||
editor.or = ან
|
||||
editor.cancel_lower = გაუქმება
|
||||
editor.add_tmpl.filename = ფაილის სახელი
|
||||
editor.patching = პაჩები:
|
||||
editor.cancel = გაუქმება
|
||||
commits.commits = კომიტები
|
||||
commits.author = ავტორი
|
||||
commits.message = შეტყობინება
|
||||
commits.date = თარიღი
|
||||
commits.older = უფრო ძველი
|
||||
commits.newer = უფრო ახალი
|
||||
commit.revert = დაბრუნება
|
||||
commitstatus.error = შეცდომა
|
||||
commitstatus.failure = ჩავარდნა
|
||||
commitstatus.pending = რიგშია
|
||||
commitstatus.success = წარმატება
|
||||
projects = პროექტები
|
||||
projects.description_placeholder = აღწერა
|
||||
projects.title = სათაური
|
||||
projects.type.none = არცერთი
|
||||
projects.column.edit_title = სახელი
|
||||
projects.column.new_title = სახელი
|
||||
projects.column.color = ფერი
|
||||
projects.open = გახსნა
|
||||
projects.close = დახურვა
|
||||
issues.new.labels = ჭდეები
|
||||
issues.new.projects = პროექტები
|
||||
issues.new.milestone = მისაღწევი მიზანი
|
||||
issues.new.assignees = მიმნიჭებლები
|
||||
issues.choose.open_external_link = გახსნა
|
||||
issues.choose.blank = ნაგულისხმევი
|
||||
issues.new_label_desc_placeholder = აღწერა
|
||||
issues.deleted_project = `(წაშლილია)`
|
||||
issues.filter_label = ჭდე
|
||||
issues.filter_milestone = მისაღწევი მიზანი
|
||||
issues.filter_project = პროექტი
|
||||
issues.filter_poster = ავტორი
|
||||
issues.filter_type = ტიპი
|
||||
issues.filter_sort = დალაგება
|
||||
issues.filter_sort.relevance = შესაბამისობა
|
||||
issues.filter_sort.latest = უახლესი
|
||||
issues.filter_sort.oldest = უძველესი
|
||||
issues.action_open = გახსნა
|
||||
issues.action_close = დახურვა
|
||||
issues.action_label = ჭდე
|
||||
issues.action_milestone = მისაღწევი მიზანი
|
||||
issues.action_assignee = მიმნიჭებელი
|
||||
issues.action_check = ჩართვა/გამორთვა
|
||||
issues.previous = წინა
|
||||
issues.next = შემდეგი
|
||||
issues.open_title = გახსნა
|
||||
issues.closed_title = დახურული
|
||||
issues.all_title = ყველა
|
||||
issues.draft_title = მონახაზი
|
||||
issues.context.delete = წაშლა
|
||||
issues.create_comment = კომენტარი
|
||||
issues.author = ავტორი
|
||||
issues.role.owner = მფლობელი
|
||||
issues.role.member = წევრი
|
||||
issues.role.collaborator = თანამონაწილე
|
||||
issues.role.contributor = მოხალისე
|
||||
issues.edit = ჩასწორება
|
||||
issues.cancel = გაუქმება
|
||||
issues.label_title = სახელი
|
||||
issues.label_description = აღწერა
|
||||
issues.label_color = ფერი
|
||||
issues.label_exclusive = ექსკლუზიური
|
||||
issues.label_delete = წაშლა
|
||||
issues.label.filter_sort.alphabetically = ანბანის მიხედვით
|
||||
issues.unsubscribe = გამოწერის გაუქმება
|
||||
issues.lock_confirm = ჩაკეტვა
|
||||
issues.delete = წაშლა
|
||||
issues.cancel_tracking = მოცილება
|
||||
issues.add_time_cancel = გაუქმება
|
||||
issues.add_time_hours = საათი
|
||||
issues.add_time_minutes = წუთი
|
||||
issues.force_push_compare = შედარება
|
||||
issues.due_date_form = წწწწ-თთ-დდ
|
||||
issues.due_date_form_edit = ჩასწორება
|
||||
issues.due_date_form_remove = წაშლა
|
||||
issues.due_date_overdue = გადაცილებული
|
||||
issues.dependency.title = დამოკიდებულებები
|
||||
issues.dependency.cancel = გაუქმება
|
||||
issues.dependency.remove = წაშლა
|
||||
issues.review.dismissed_label = მოცილებულია
|
||||
issues.review.pending = რიგშია
|
||||
issues.review.reviewers = გადამხედავები
|
||||
issues.reference_issue.body = სხეული
|
||||
issues.content_history.deleted = წაშლილია
|
||||
issues.content_history.edited = ჩასწორებულია
|
||||
issues.content_history.created = შეიქმნა
|
||||
compare.compare_base = ბაზა
|
||||
compare.compare_head = შედარება
|
||||
pulls.has_viewed_file = ნანახია
|
||||
pulls.tab_conversation = საუბარი
|
||||
pulls.tab_commits = კომიტები
|
||||
pulls.merged = შერწყმულია
|
||||
pulls.status_checks_requested = აუცილებელია
|
||||
pulls.status_checks_details = დეტალები
|
||||
pulls.cmd_instruction_merge_title = შერწყმა
|
||||
pulls.made_using_agit = AGit
|
||||
pulls.editable = ჩასწორებადი
|
||||
milestones.open = გახსნა
|
||||
milestones.close = დახურვა
|
||||
milestones.title = სათაური
|
||||
milestones.desc = აღწერა
|
||||
milestones.clear = გასუფთავება
|
||||
milestones.cancel = გაუქმება
|
||||
milestones.filter_sort.name = სახელი
|
||||
wiki = ვიკი
|
||||
wiki.page = გვერდი
|
||||
wiki.new_page = გვერდი
|
||||
wiki.edit_page_button = ჩასწორება
|
||||
wiki.pages = გვერდები
|
||||
activity.navbar.pulse = უახლესი აქტივობები
|
||||
activity.navbar.contributors = მოხალისეები
|
||||
activity.period.filter_label = პერიოდი:
|
||||
activity.overview = გადახედვა
|
||||
activity.opened_prs_label = შეთავაზებულია
|
||||
activity.closed_issue_label = დახურულია
|
||||
activity.new_issue_label = ღიაა
|
||||
activity.unresolved_conv_label = გახსნა
|
||||
activity.published_release_label = რელიზი
|
||||
activity.published_prerelease_label = პრე-რელიზი
|
||||
activity.published_tag_label = ჭდე
|
||||
activity.git_stats_and_deletions = და
|
||||
contributors.contribution_type.commits = კომიტები
|
||||
contributors.contribution_type.additions = დამატებები
|
||||
contributors.contribution_type.deletions = წაშლები
|
||||
settings = მორგება
|
||||
settings.options = რეპოზიტორია
|
||||
settings.collaboration = თანამონაწილეები
|
||||
settings.collaboration.admin = ადმინისტრატორი
|
||||
settings.collaboration.write = ჩაწერა
|
||||
settings.collaboration.read = წაკითხვა
|
||||
settings.collaboration.owner = მფლობელი
|
||||
settings.collaboration.undefined = აღუწერელი
|
||||
settings.hooks = ვებჰუკები
|
||||
settings.mirror_settings.direction.push = გაგზავნა
|
||||
settings.mirror_settings.push_mirror.none_ssh = არცერთი
|
||||
settings.units.units = ერთეულები
|
||||
settings.units.overview = გადახედვა
|
||||
settings.site = ვებგვერდი
|
||||
settings.tracker_issue_style.alphanumeric = ალფარიცხვითი
|
||||
settings.admin_indexer_unindexed = არადაინდექსებული
|
||||
settings.trust_model.collaborator = თანამონაწილე
|
||||
settings.trust_model.committer = გადამცემი
|
||||
settings.teams = გუნდები
|
||||
settings.webhook.headers = თავსართები
|
||||
settings.webhook.body = სხეული
|
||||
settings.secret = საიდუმლო
|
||||
settings.slack_username = მომხმარებლის სახელი
|
||||
settings.slack_color = ფერი
|
||||
settings.event_create = შექმნა
|
||||
settings.event_delete = წაშლა
|
||||
settings.event_fork = ფორკი
|
||||
settings.event_wiki = ვიკი
|
||||
settings.event_release = რელიზი
|
||||
settings.event_push = გაგზავნა
|
||||
settings.event_repository = რეპოზიტორია
|
||||
settings.webhook.response = გამოხმაურება
|
||||
settings.event_issue_assign = მინიჭება
|
||||
settings.event_issue_label = ჭდეები
|
||||
settings.event_issue_comment = კომენტარები
|
||||
settings.event_pull_request = ცვლილება
|
||||
settings.event_pull_request_assign = მინიჭება
|
||||
settings.event_pull_request_label = ჭდეები
|
||||
settings.event_pull_request_milestone = მისაღწევი გეგმები
|
||||
settings.event_pull_request_review = გადახედვები
|
||||
settings.event_pull_request_sync = სინქრონიზებულია
|
||||
settings.event_pull_request_enforcement = ფორსირება
|
||||
settings.event_package = პაკეტი
|
||||
settings.active = აქტიურია
|
||||
settings.slack_token = ტოკენი
|
||||
settings.slack_domain = დომენი
|
||||
settings.slack_channel = არხი
|
||||
settings.web_hook_name_gitea = Gitea
|
||||
settings.web_hook_name_forgejo = Forgejo
|
||||
settings.web_hook_name_gogs = Gogs
|
||||
settings.web_hook_name_slack = Slack
|
||||
settings.web_hook_name_discord = Discord
|
||||
settings.web_hook_name_dingtalk = DingTalk
|
||||
settings.web_hook_name_matrix = Matrix
|
||||
settings.web_hook_name_feishu_only = Feishu
|
||||
settings.web_hook_name_packagist = Packagist
|
||||
settings.sourcehut_builds.secrets = საიდუმლოები
|
||||
settings.title = სათაური
|
||||
settings.branches = ბრენჩები
|
||||
settings.protect_patterns = ნიმუშები
|
||||
settings.tags = ჭდეები
|
||||
settings.lfs_lock = ჩაკეტვა
|
||||
settings.lfs_pointers.oid = OID
|
||||
diff.parent = მშობელი
|
||||
diff.commit = კომიტი
|
||||
diff.git-notes = შენიშვნები
|
||||
diff.bin = BIN
|
||||
diff.file_after = შემდეგ
|
||||
diff.file_image_width = სიგანე
|
||||
diff.file_image_height = სიმაღლე
|
||||
diff.file_byte_size = ზომა
|
||||
diff.generated = გენერირებულია
|
||||
diff.comment.reply = პასუხი
|
||||
diff.review.comment = კომენტარი
|
||||
diff.review.approve = დადასტურება
|
||||
diff.protected = დაცულია
|
||||
diff.image.swipe = გაუსვით
|
||||
diff.image.overlay = ზემოდან დადება
|
||||
release.releases = რელიზები
|
||||
release.draft = მონახაზი
|
||||
release.prerelease = პრე-რელიზი
|
||||
release.stable = სტაბილური
|
||||
release.compare = შედარება
|
||||
release.target = სამიზნე
|
||||
release.cancel = გაუქმება
|
||||
release.downloads = გადმოწერები
|
||||
branch.delete_head = წაშლა
|
||||
branch.included = ჩასმულია
|
||||
commit.cherry-pick = ზუსტი არჩევა
|
||||
issues.deleted_milestone = `(წაშლილია)`
|
||||
issues.content_history.options = მორგება
|
||||
settings.mirror_settings.direction = მიმართულება
|
||||
settings.tracker_issue_style.numeric = რიცხვითი
|
||||
settings.delete_collaborator = წაშლა
|
||||
release.tags = ჭდეები
|
||||
unwatch = თვალყურის დევნების გაუქმება
|
||||
tree = ხე
|
||||
issues.context.edit = ჩასწორება
|
||||
wiki.cancel = გაუქმება
|
||||
settings.event_issue_milestone = მისაღწევი გეგმები
|
||||
diff.file_before = მანამდე
|
||||
commit.operations = ოპერაციები
|
||||
issues.label_edit = ჩასწორება
|
||||
issues.unlock_confirm = ჩაკეტვის გაუქმება
|
||||
issues.dependency.blocks_short = ბლოკები
|
||||
pulls.cmd_instruction_checkout_title = გამოთხოვა
|
||||
pull.deleted_branch = (წაშლილია):%s
|
||||
desc.private = პირადი
|
||||
desc.archived = დაარქივებული
|
||||
watch = თვალყურის დევნება
|
||||
project = პროექტები
|
||||
milestones = მისაღწევი გეგმები
|
||||
file_permalink = მუდმივი ბმული
|
||||
projects.template.desc = ნიმუში
|
||||
issues.filter_assignee = მიმნიჭებელი
|
||||
issues.reopen_issue = თავიდან გახსნა
|
||||
issues.save = შენახვა
|
||||
activity = აქტივობა
|
||||
settings.mirror_settings.direction.pull = მიღება
|
||||
settings.trust_model.collaboratorcommitter = თანამონაწილე+გადამცემი
|
||||
settings.webhook.request = მოთხოვნა
|
||||
settings.discord_username = მომხმარებლის სახელი
|
||||
settings.event_issues = ცვლილება
|
||||
settings.web_hook_name_telegram = Telegram
|
||||
settings.protect_status_check_matched = დამთხვეული
|
||||
settings.edit_protected_branch = ჩასწორება
|
||||
settings.tags.protection.allowed = დაშვებულია
|
||||
diff.whitespace_button = ჰარე
|
||||
diff.vendored = გარედან შემოტანილია
|
||||
release.edit = ჩასწორება
|
||||
release.type_attachment = მიმაგრებული ფაილი
|
||||
topic.done = დასრულება
|
||||
migrate_items_milestones = მისაღწევი გეგმები
|
||||
issues.subscribe = გამოწერა
|
||||
issues.review.outdated = ვადაგადაცილებული
|
||||
activity.merged_prs_label = შერწყმულია
|
||||
settings.webhook.payload = შემცველობა
|
||||
settings.event_pull_request_comment = კომენტარები
|
||||
settings.deploy_key_content = შემცველობა
|
||||
settings.lfs = LFS
|
||||
settings.lfs_locks = დაბლოკვა
|
||||
|
||||
[org]
|
||||
members = წევრები
|
||||
teams = გუნდები
|
||||
code = კოდი
|
||||
lower_members = წევრები
|
||||
lower_repositories = რეპოზიტორიები
|
||||
org_desc = აღწერა
|
||||
team_unit_disabled = (გათიშულია)
|
||||
settings = მორგება
|
||||
settings.options = ორგანიზაცია
|
||||
settings.website = ვებგვერდი
|
||||
settings.permission = წვდომები
|
||||
settings.visibility = ხილვადობა
|
||||
settings.visibility.public = საჯარო
|
||||
settings.visibility.limited_shortname = შეზღუდული
|
||||
settings.visibility.private_shortname = პირადი
|
||||
members.public = ხილული
|
||||
members.owner = მფლობელი
|
||||
members.member = წევრი
|
||||
members.remove = წაშლა
|
||||
members.leave = გასვლა
|
||||
teams.join = შეერთება
|
||||
teams.read_access = წაკითხვა
|
||||
teams.write_access = ჩაწერა
|
||||
teams.settings = მორგება
|
||||
team_desc = აღწერა
|
||||
members.private = დამალული
|
||||
team_permission_desc = წვდომა
|
||||
settings.location = მდებარეობა
|
||||
teams.leave = გასვლა
|
||||
|
||||
[admin]
|
||||
dashboard = სამუშაო მაგიდა
|
||||
organizations = ორგანიზაციები
|
||||
repositories = რეპოზიტორიები
|
||||
hooks = ვებჰუკები
|
||||
integrations = ინტეგრაციები
|
||||
config = მორგება
|
||||
config_summary = შეჯამება
|
||||
config_settings = მორგება
|
||||
monitor = მონიტორინგი
|
||||
first_page = პირველი
|
||||
last_page = ბოლო
|
||||
dashboard.statistic = შეჯამება
|
||||
dashboard.operation_run = გაშვება
|
||||
users.name = მომხმარებლის სახელი
|
||||
users.activated = გააქტიურებულია
|
||||
users.admin = ადმინი
|
||||
users.restricted = შეზღუდული
|
||||
users.reserved = დაცული
|
||||
users.bot = ბოტი
|
||||
users.remote = დაშორებული
|
||||
users.2fa = 2FA
|
||||
users.repos = რეპოები
|
||||
users.created = შეიქმნა
|
||||
users.edit = ჩასწორება
|
||||
users.local = ლოკალური
|
||||
users.list_status_filter.reset = ჩამოყრა
|
||||
users.list_status_filter.is_active = აქტიურია
|
||||
users.list_status_filter.is_admin = ადმინი
|
||||
users.list_status_filter.is_restricted = შეზღუდული
|
||||
emails.primary = ძირითადი
|
||||
emails.activated = გააქტიურებულია
|
||||
emails.filter_sort.email = ელფოსტა
|
||||
emails.filter_sort.name = მომხმარებლის სახელი
|
||||
orgs.name = სახელი
|
||||
orgs.teams = გუნდები
|
||||
repos.owner = მფლობელი
|
||||
repos.name = სახელი
|
||||
repos.private = პირადი
|
||||
repos.issues = პრობლემები
|
||||
repos.size = ზომა
|
||||
packages.owner = მფლობელი
|
||||
packages.name = სახელი
|
||||
packages.version = ვერსია
|
||||
packages.type = ტიპი
|
||||
packages.repository = რეპოზიტორია
|
||||
packages.size = ზომა
|
||||
auths.name = სახელი
|
||||
auths.type = ტიპი
|
||||
auths.enabled = ჩართულია
|
||||
auths.updated = განახლებულია
|
||||
auths.domain = დომენი
|
||||
auths.host = ჰოსტი
|
||||
auths.port = პორტი
|
||||
auths.oauth2_tenant = ტენანტი
|
||||
auths.tips = რჩევები
|
||||
config.ssh_enabled = ჩართულია
|
||||
config.ssh_port = პორტი
|
||||
config.lfs_enabled = ჩართულია
|
||||
config.db_type = ტიპი
|
||||
config.db_host = ჰოსტი
|
||||
config.db_name = სახელი
|
||||
config.db_user = მომხმარებლის სახელი
|
||||
config.db_schema = სქემა
|
||||
config.db_ssl_mode = SSL
|
||||
config.db_path = ბილიკი
|
||||
config.mailer_name = სახელი
|
||||
config.mailer_protocol = პროტოკოლი
|
||||
config.mailer_user = მომხმარებელი
|
||||
config.mailer_use_dummy = სულელი
|
||||
config.oauth_enabled = ჩართულია
|
||||
config.disabled_logger = გამორთულია
|
||||
monitor.stats = სტატისტიკა
|
||||
monitor.name = სახელი
|
||||
monitor.schedule = გეგმა
|
||||
monitor.execute_times = შესრულებები
|
||||
monitor.stacktrace = სტეკის დატრეისება
|
||||
monitor.desc = აღწერა
|
||||
monitor.last_execution_result = შედეგი
|
||||
monitor.process.children = შვილები
|
||||
monitor.queues = რიგები
|
||||
monitor.queue.name = სახელი
|
||||
monitor.queue.type = ტიპი
|
||||
notices.operations = ოპერაციები
|
||||
notices.type = ტიპი
|
||||
notices.type_1 = რეპოზიტორია
|
||||
notices.type_2 = ამოცანა
|
||||
notices.desc = აღწერა
|
||||
notices.op = ოპ.
|
||||
users.list_status_filter.menu_text = ფილტრი
|
||||
users.list_status_filter.not_active = არააქტიურია
|
||||
config.send_test_mail_submit = გაგზავნა
|
||||
packages.creator = შემქმნელი
|
||||
dashboard.operation_switch = გადართვა
|
||||
orgs.members = წევრები
|
||||
packages.published = გამოქვეყნებულია
|
||||
config.mailer_enabled = ჩართულია
|
||||
|
||||
[action]
|
||||
review_dismissed_reason = მიზეზი:
|
||||
compare_branch = შედარება
|
||||
|
||||
[tool]
|
||||
now = ახლა
|
||||
raw_seconds = წამი
|
||||
raw_minutes = წუთი
|
||||
future = მომავალში
|
||||
|
||||
[munits.data]
|
||||
b = ბ
|
||||
kib = კიბ
|
||||
mib = მიბ
|
||||
gib = გიბ
|
||||
tib = ტიბ
|
||||
pib = პიბ
|
||||
eib = ეიბ
|
||||
|
||||
[notification]
|
||||
notifications = გაფრთხილებები
|
||||
read = წაკითხვა
|
||||
subscriptions = გამოწერები
|
||||
watching = უყურებთ
|
||||
unread = წაკითხულობის გაუქმება
|
||||
|
||||
[units]
|
||||
unit = ერთეული
|
||||
|
||||
[packages]
|
||||
title = პაკეტები
|
||||
filter.type = ტიპი
|
||||
filter.type.all = ყველა
|
||||
filter.container.tagged = ჭდით
|
||||
filter.container.untagged = ჭდემოხსნილი
|
||||
installation = დაყენება
|
||||
requirements = მოთხოვნები
|
||||
dependencies = დამოკიდებულებები
|
||||
keywords = საკვანძო სიტყვები
|
||||
details = დეტალები
|
||||
details.author = ავტორი
|
||||
details.license = ლიცენზია
|
||||
assets = ობიექტები
|
||||
versions = ვერსიები
|
||||
dependency.id = ID
|
||||
dependency.version = ვერსია
|
||||
alpine.repository.branches = ბრენჩები
|
||||
alpine.repository.repositories = რეპოზიტორიები
|
||||
alpine.repository.architectures = არქიტექტურები
|
||||
arch.version.provides = მოგაწვდით
|
||||
arch.version.depends = დამოკიდებულია
|
||||
arch.version.conflicts = კონფლიქტშია
|
||||
arch.version.replaces = ანაცვლებს
|
||||
arch.version.backup = მარქაფი
|
||||
conan.details.repository = რეპოზიტორია
|
||||
container.images.title = ასლის ფაილები
|
||||
container.details.platform = პლატფორმა
|
||||
container.digest = დაიჯესტი
|
||||
container.labels = ჭდეები
|
||||
container.labels.key = გასაღები
|
||||
debian.repository.distributions = დისტრიბუტივები
|
||||
debian.repository.components = კომპონენტები
|
||||
debian.repository.architectures = არქიტექტურები
|
||||
npm.dependencies = დამოკიდებულებები
|
||||
npm.details.tag = ჭდე
|
||||
rpm.repository.architectures = არქიტექტურები
|
||||
alt.repository.architectures = არქიტექტურები
|
||||
owner.settings.cleanuprules.enabled = ჩართულია
|
||||
arch.version.description = აღწერა
|
||||
arch.version.groups = ჯგუფი
|
||||
composer.dependencies = დამოკიდებულებები
|
||||
container.labels.value = მნიშვნელობა
|
||||
|
||||
[secrets]
|
||||
secrets = საიდუმლოები
|
||||
|
||||
[actions]
|
||||
actions = ქმედებები
|
||||
status.unknown = უცნობი
|
||||
status.waiting = ველოდები
|
||||
status.running = მიმდინარეობს შესრულება
|
||||
status.success = წარმატება
|
||||
status.failure = ჩავარდნა
|
||||
status.cancelled = გაუქმებულია
|
||||
status.skipped = გამოტოვებულია
|
||||
status.blocked = დაბლოკილია
|
||||
runners = გამსვებები
|
||||
runners.status = სტატუსი
|
||||
runners.id = ID
|
||||
runners.owner_type = ტიპი
|
||||
runners.description = აღწერა
|
||||
runners.labels = ჭდეები
|
||||
runners.runner_title = გამშვები
|
||||
runners.task_list.run = გაშვება
|
||||
runners.task_list.status = სტატუსი
|
||||
runners.task_list.repository = რეპოზიტორია
|
||||
runners.task_list.commit = კომიტი
|
||||
runners.status.unspecified = უცნობი
|
||||
runners.status.idle = უქმე
|
||||
runners.status.active = აქტიურია
|
||||
runners.status.offline = ქსელგარეშე
|
||||
runners.version = ვერსია
|
||||
runs.commit = კომიტი
|
||||
runs.scheduled = დაგეგმილია
|
||||
runs.workflow = შრომის პროცესი
|
||||
runs.actor = ავტორი
|
||||
runs.status = სტატუსი
|
||||
variables = ცვლადები
|
||||
runners.name = სახელი
|
||||
|
||||
[git.filemode]
|
||||
directory = საქაღალდე
|
||||
submodule = ქვემოდული
|
||||
|
||||
[graphs]
|
||||
contributors.what = მოხალისეები
|
|
@ -2488,9 +2488,9 @@ settings.protect_branch_name_pattern=Aizsargātā zara nosaukuma paraugs
|
|||
settings.protect_branch_name_pattern_desc=Aizsargāto zaru nosaukumu paraugi. Paraugu pierakstu skatīt <a href="%s">dokumentācijā</a>. Piemēri: main, release/**
|
||||
settings.protect_patterns=Paraugi
|
||||
settings.protect_protected_file_patterns=Aizsargāto datņu paraugs (vairākus atdala ar semikolu ";")
|
||||
settings.protect_protected_file_patterns_desc=Aizsargātās datnes nav ļauts tiešā veidā mainīt, pat ja lietotājam šajā zarā ir tiesības pievienot, labot vai izdzēst datnes. Vairākus paraugus var atdalīt ar semikolu (";"). Paraugu pieraksts ir skatāms <a href='%[1]s'>%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_protected_file_patterns_desc=Aizsargātās datnes nav ļauts tiešā veidā mainīt, pat ja lietotājam šajā zarā ir tiesības pievienot, labot vai izdzēst datnes. Vairākus paraugus var atdalīt ar semikolu (";"). Paraugu pieraksts ir skatāms <a href="%[1]s">%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns=Neaizsargāto datņu paraugs (vairākus atdala ar semikolu ";")
|
||||
settings.protect_unprotected_file_patterns_desc=Neaizsargātās datnes, kuras ir ļauts izmainīt tiešā veidā, apejot aizgādāšanas ierobežojumu, ja lietotājam ir rakstīšanas piekļuve. Vairāki paraugi ir atdalāmi ar semikolu (";"). Paraugu pierakstu skatīt <a href='%[1]s'>%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns_desc=Neaizsargātās datnes, kuras ir ļauts izmainīt tiešā veidā, apejot aizgādāšanas ierobežojumu, ja lietotājam ir rakstīšanas piekļuve. Vairāki paraugi ir atdalāmi ar semikolu (";"). Paraugu pierakstu skatīt <a href="%[1]s">%[2]s</a> dokumentācijā. Piemēri: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.add_protected_branch=Iespējot aizsargāšanu
|
||||
settings.delete_protected_branch=Atspējot aizsargāšanu
|
||||
settings.update_protect_branch_success=Zara aizsargāšanas kārtula "%s" tika atjaunināta.
|
||||
|
@ -2907,6 +2907,9 @@ issues.reopen.blocked_by_user = Tu nevari atkārtoti atvērt šo pieteikumu, jo
|
|||
pulls.comment.blocked_by_user = Tu šim izmaiņu pieprasījumam nevari pievienot piebildi, jo tā izveidotājs vai glabātavas īpašnieks ir liedzis Tevi.
|
||||
issues.filter_no_results = Nav vienumu
|
||||
issues.filter_no_results_placeholder = Jāmēģina pielāgot meklēšanas atlasītāji.
|
||||
migrate.repo_desc_helper = Atstāt tukšu, lai ievietotu esošo aprakstu
|
||||
archive.nocomment = Piebilžu pievienošana nav iespējama, jo glabātava ir arhivēta.
|
||||
comment.blocked_by_user = Piebilžu pievienošana nav iespējama, jo glabātavas īpašnieks vai autors ir nolieguši Tevi.
|
||||
|
||||
[graphs]
|
||||
component_loading=Ielādē %s…
|
||||
|
@ -3896,7 +3899,7 @@ status.unknown=Nezināms
|
|||
status.waiting=Gaida
|
||||
status.running=Izpildās
|
||||
status.success=Sekmīgi
|
||||
status.failure=Atteice
|
||||
status.failure=Nesekmīgi
|
||||
status.cancelled=Atcelts
|
||||
status.skipped=Izlaists
|
||||
status.blocked=Aizturēts
|
||||
|
|
|
@ -1305,7 +1305,7 @@ issues.new.clear_assignees = Towiesens leegmaken
|
|||
editor.file_editing_no_longer_exists = De Datei, wat bewarkt word, »%s«, gifft dat in deesem Repositorium nich mehr.
|
||||
editor.user_no_push_to_branch = Bruker kann nich to Twieg schuven
|
||||
editor.directory_is_a_file = Verteeknisnaam »%s« word in deesem Repositorium al as Dateinaam bruukt.
|
||||
editor.file_changed_while_editing = De Datei-Inhollens hebben sik ännert, siet du mit de Bewarken begunnen hest. <a target="_blank" rel="noopener noreferrer" href="%s">Klick hier</a>, um se to sehn, of <strong>kommitteer de Änners weer</strong>, um se to överschrieven.
|
||||
editor.file_changed_while_editing = De Datei-Inhollens hebben sik ännert, siet du de Datei opmaakt hest. <a target="_blank" rel="noopener noreferrer" href="%s">Klick hier</a>, um se to sehn, of <strong>kommitteer de Änners weer</strong>, um se to överschrieven.
|
||||
editor.push_rejected_no_message = De Ännern is vun de Server sünner Naricht oflehnt worden. Bidde överprüüf de Git-Hakens.
|
||||
commits.signed_by = Unnerschrieven vun
|
||||
commit.revert-content = Twieg utkören, up wat du dat torüggnehmen willst:
|
||||
|
@ -2608,6 +2608,9 @@ issues.reopen.blocked_by_user = Du kannst deeses Gefall nich weer opmaken, denn
|
|||
pulls.comment.blocked_by_user = Du kannst up deesem Haalvörslag nich kommenteren, denn de Repositoriums-Eegner of de Autor vun de Haalvörslag hett di blockeert.
|
||||
issues.filter_no_results = Keene Resultaten
|
||||
issues.filter_no_results_placeholder = Versöök, diene Söök-Filters antopassen.
|
||||
migrate.repo_desc_helper = Leeg laten, um de bestahn Beschrieven to importeren
|
||||
comment.blocked_by_user = Kommenteren gaht hier nich, denn du büst vun de Repositoriums-Eegner of de Autor blockeert worden.
|
||||
archive.nocomment = Kommenteren gaht hier nich, denn dat Repositorium is archiveert.
|
||||
|
||||
[repo.permissions]
|
||||
code.read = <b>Lesen:</b> De Quelltext vun deesem Repositorium ankieken un klonen.
|
||||
|
|
|
@ -1361,7 +1361,7 @@ editor.propose_file_change=Stel bestandswijziging voor
|
|||
editor.new_branch_name_desc=Nieuwe branch naam…
|
||||
editor.cancel=Annuleer
|
||||
editor.filename_cannot_be_empty=Bestandsnaam mag niet leeg zijn.
|
||||
editor.file_changed_while_editing=De bestandsinhoud is veranderd sinds je bent begonnen met bewerken. <a target="_blank" rel="noopener noreferrer" href="%s">Klik hier</a> om ze te zien, of <strong>commit de veranderingen opnieuw</strong> om ze te overschrijven.
|
||||
editor.file_changed_while_editing=De inhoud van het bestand is gewijzigd sinds u het bestand hebt geopend. <a target="_blank" rel="noopener noreferrer" href="%s">Klik hier</a> om ze te zien, of <strong>commit de veranderingen opnieuw</strong> om ze te overschrijven.
|
||||
editor.commit_empty_file_header=Commit een leeg bestand
|
||||
editor.commit_empty_file_text=Het bestand dat u wilt committen is leeg. Doorgaan?
|
||||
editor.no_changes_to_show=Er zijn geen wijzigingen om weer te geven.
|
||||
|
@ -2906,6 +2906,9 @@ issues.reopen.blocked_by_user = U kunt deze issue niet heropenen omdat u geblokk
|
|||
pulls.comment.blocked_by_user = U kunt niet reageren op deze pull request omdat u geblokkeerd bent door de eigenaar van de repository of de poster van de issue.
|
||||
issues.filter_no_results_placeholder = Probeer uw zoekfilters aan te passen.
|
||||
issues.filter_no_results = Geen resultaten
|
||||
migrate.repo_desc_helper = Leeg laten om bestaande beschrijving te importeren
|
||||
archive.nocomment = Commentaar geven is niet mogelijk omdat de repository gearchiveerd is.
|
||||
comment.blocked_by_user = Commentaar geven is niet mogelijk omdat u geblokkeerd bent door de eigenaar van de repository of door de auteur.
|
||||
|
||||
|
||||
|
||||
|
@ -2924,7 +2927,7 @@ org_name_holder=Organisatienaam
|
|||
org_full_name_holder=Volledige naam organisatie
|
||||
org_name_helper=Organisatienamen horen kort en memorabel zijn.
|
||||
create_org=Nieuwe organisatie aanmaken
|
||||
repo_updated=Geupdate %s
|
||||
repo_updated=Geüpdatet %s
|
||||
members=Leden
|
||||
teams=Teams
|
||||
lower_members=leden
|
||||
|
|
|
@ -2432,9 +2432,9 @@ settings.require_signed_commits_desc=Rejeitar pushes para este branch se não es
|
|||
settings.protect_branch_name_pattern=Padrão de nome de branch protegido
|
||||
settings.protect_patterns=Padrões
|
||||
settings.protect_protected_file_patterns=Padrões de arquivo protegidos (separados usando ponto e vírgula ";")
|
||||
settings.protect_protected_file_patterns_desc=Arquivos protegidos não podem ser alterados diretamente, mesmo que o usuário tenha direitos para adicionar, editar ou excluir arquivos neste branch. Vários padrões podem ser separados usando ponto e vírgula (';'). Consulte a documentação <a href='%s'>%s</a> para a sintaxe padrão. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_protected_file_patterns_desc=Arquivos protegidos não podem ser alterados diretamente, mesmo que o usuário tenha direitos para adicionar, editar ou excluir arquivos neste branch. Vários padrões podem ser separados usando ponto e vírgula (';'). Consulte a documentação <a href="%[1]s">%[2]s</a> para a sintaxe padrão. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns=Padrões de arquivo desprotegidos (separados usando ponto e vírgula ";")
|
||||
settings.protect_unprotected_file_patterns_desc=Arquivos não protegidos que podem ser alterados diretamente se o usuário tiver acesso de gravação, ignorando as restrições de push. Vários padrões podem ser separados usando ponto e vírgula (\;'). Veja <a href='%[1]s'>%[2]s</a> documentação para sintaxe de padrões. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns_desc=Arquivos não protegidos que podem ser alterados diretamente se o usuário tiver acesso de gravação, ignorando as restrições de push. Vários padrões podem ser separados usando ponto e vírgula (\;'). Veja <a href="%[1]s">%[2]s</a> documentação para sintaxe de padrões. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.add_protected_branch=Habilitar proteção
|
||||
settings.delete_protected_branch=Desabilitar proteção
|
||||
settings.update_protect_branch_success=Proteção do branch "%s" foi atualizada.
|
||||
|
@ -2907,6 +2907,9 @@ issues.reopen.blocked_by_user = Você não pode reabrir este issue porque você
|
|||
pulls.comment.blocked_by_user = Você não pode comentar neste pull request porque você foi bloqueado pelo dono do repositório ou pelo autor do pull request.
|
||||
issues.filter_no_results = Nenhum resultado
|
||||
issues.filter_no_results_placeholder = Tente ajustar seus filtros de pesquisa.
|
||||
archive.nocomment = Não é possível comentar pois o repositório foi arquivado.
|
||||
migrate.repo_desc_helper = Deixe em branco para importar a descrição existente
|
||||
comment.blocked_by_user = Não é possível comentar pois você foi bloqueado pelo dono ou autor do repositório.
|
||||
|
||||
[graphs]
|
||||
component_loading = Carregando %s…
|
||||
|
|
|
@ -2909,6 +2909,9 @@ pulls.editable = Editável
|
|||
pulls.editable_explanation = Este pedido de integração permite edições dos responsáveis. Pode contribuir diretamente para ele.
|
||||
issues.filter_no_results = Nenhum resultado
|
||||
issues.filter_no_results_placeholder = Tente ajustar os seus filtros de pesquisa.
|
||||
migrate.repo_desc_helper = Deixe em branco para importar a descrição existente
|
||||
archive.nocomment = Não é possível fazer comentários porque o repositório está arquivado.
|
||||
comment.blocked_by_user = Não é possível comentar porque está bloqueado pelo proprietário do repositório ou pelo autor.
|
||||
|
||||
[graphs]
|
||||
component_loading=A carregar %s…
|
||||
|
|
|
@ -1042,7 +1042,7 @@ user_block_success = Пользователь заблокирован.
|
|||
oauth2_application_locked = Forgejo предварительно регистрирует некоторые приложения OAuth2 при запуске, если это включено в конфигурации. Для избежания неожиданного поведения их нельзя удалять или редактировать. Ознакомиться с подробностями можно в документации OAuth2.
|
||||
hooks.desc = Добавьте веб-хуки, которые будут срабатывать <strong>во всех</strong> ваших репозиториях.
|
||||
webauthn_alternative_tip = Возможно, стоит настроить дополнительный метод аутентификации.
|
||||
blocked_since = Заблокирован с %s
|
||||
blocked_since = Заблокирован %s
|
||||
user_unblock_success = Пользователь разблокирован.
|
||||
twofa_scratch_token_regenerated = Ваш одноразовый ключ восстановления: %s. Сохраните его в надёжном месте. Больше он показан не будет.
|
||||
blocked_users = Заблокированные пользователи
|
||||
|
@ -2910,6 +2910,9 @@ issues.reopen.blocked_by_user = Повторное открытие задачи
|
|||
pulls.comment.blocked_by_user = Вы не можете комментировать под этим запросом слияния, т.к. вы заблокированы владельцем репозитория или автором задачи.
|
||||
issues.filter_no_results = Ничего не нашлось
|
||||
issues.filter_no_results_placeholder = Попробуйте поискать по-другому.
|
||||
migrate.repo_desc_helper = Оставьте пустым, чтобы скопировать описание из источника
|
||||
archive.nocomment = Комментирование невозможно, потому что этот репозиторий архивирован.
|
||||
comment.blocked_by_user = Комментирование невозможно, потому что вы заблокированы его владельцем или автором обсуждения.
|
||||
|
||||
[graphs]
|
||||
component_loading_failed = Не удалось загрузить %s
|
||||
|
|
|
@ -2429,9 +2429,9 @@ settings.protect_branch_name_pattern=Korunmuş Dal Adı Deseni
|
|||
settings.protect_branch_name_pattern_desc=Korunmuş dal isim desenleri. Desen sözdizimi için <a href="%s">belgelere</a> bakabilirsiniz. Örnekler: main, release/**
|
||||
settings.protect_patterns=Desenler
|
||||
settings.protect_protected_file_patterns=Korumalı dosya kalıpları (noktalı virgülle ayrılmış ';'):
|
||||
settings.protect_protected_file_patterns_desc=Kullanıcının bu dalda dosya ekleme, düzenleme veya silme hakları olsa bile doğrudan değiştirilmesine izin verilmeyen korumalı dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen sözdizimi için <a href='%s'>%s</a> belgelerine bakın. Örnekler: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_protected_file_patterns_desc=Kullanıcının bu dalda dosya ekleme, düzenleme veya silme hakları olsa bile doğrudan değiştirilmesine izin verilmeyen korumalı dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen sözdizimi için <a href="%[1]s">%[2]s</a> belgelerine bakın. Örnekler: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns=Korunmasız dosya desenleri (noktalı virgülle ayrılmış ';'):
|
||||
settings.protect_unprotected_file_patterns_desc=Kullanıcının yazma erişimi, itme kısıtlamasını atlama hakkı olduğunda doğrudan değiştirmesine izin verilen korunmasız dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen söz dizimi için <a href='%[1]s'>%[2]s</a> belgelerine bakın. Örnekler: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.protect_unprotected_file_patterns_desc=Kullanıcının yazma erişimi, itme kısıtlamasını atlama hakkı olduğunda doğrudan değiştirmesine izin verilen korunmasız dosyalar. Birden çok desen noktalı virgül (';') kullanılarak ayrılabilir. Desen söz dizimi için <a href="%[1]s">%[2]s</a> belgelerine bakın. Örnekler: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
|
||||
settings.add_protected_branch=Korumayı etkinleştir
|
||||
settings.delete_protected_branch=Korumayı devre dışı bırak
|
||||
settings.update_protect_branch_success=Dal koruma kuralı "%s" güncellendi.
|
||||
|
|
|
@ -1075,6 +1075,7 @@ quota.applies_to_org = До цієї організації застосован
|
|||
quota.rule.exceeded = Перевищено
|
||||
regenerate_token = Згенерувати знову
|
||||
access_token_regeneration = Згенерувати новий токен доступу
|
||||
quota.sizes.assets.all = Ресурси
|
||||
|
||||
[repo]
|
||||
owner=Власник
|
||||
|
@ -1323,7 +1324,7 @@ editor.propose_file_change=Запропонувати зміну файлу
|
|||
editor.new_branch_name_desc=Ім'я нової гілки…
|
||||
editor.cancel=Відмінити
|
||||
editor.filename_cannot_be_empty=Ім'я файлу не може бути порожнім.
|
||||
editor.file_changed_while_editing=Зміст файлу змінився з моменту початку редагування. <a target="_blank" rel="noopener" href="%s"> Натисніть тут </a>, щоб переглянути що було змінено, або <strong>закомітьте зміни ще раз</strong>, щоб переписати їх.
|
||||
editor.file_changed_while_editing=Вміст файлу змінився відтоді, як ви його відкрили. <a target="_blank" rel="noopener noreferrer" href="%s">Натисніть тут</a>, щоб переглянути, що було змінено, або <strong>закомітьте зміни ще раз</strong>, щоб переписати їх.
|
||||
editor.commit_empty_file_header=Закомітити порожній файл
|
||||
editor.commit_empty_file_text=Файл, в комміті порожній. Продовжити?
|
||||
editor.no_changes_to_show=Нема змін для показу.
|
||||
|
@ -1585,8 +1586,8 @@ issues.due_date=Дата завершення
|
|||
issues.invalid_due_date_format=Дата закінчення має бути в форматі 'ррр-мм-дд'.
|
||||
issues.error_modifying_due_date=Не вдалося змінити дату завершення.
|
||||
issues.error_removing_due_date=Не вдалося видалити дату завершення.
|
||||
issues.push_commit_1=додав %d коміт %s
|
||||
issues.push_commits_n=додав %d коміти(-ів) %s
|
||||
issues.push_commit_1=додає %d коміт %s
|
||||
issues.push_commits_n=додає %d коміти(-ів) %s
|
||||
issues.force_push_codes=`примусово залито %[1]s з <a class="%[7]s" href="%[3]s"><code>%[2]s</code></a> до <a class="%[7]s" href="%[5]s"><code>%[4]s</code></a> %[6]s`
|
||||
issues.force_push_compare=Порівняти
|
||||
issues.due_date_form=рррр-мм-дд
|
||||
|
@ -2650,6 +2651,14 @@ release.asset_external_url = Зовнішня URL-адреса
|
|||
branch.download = Завантажити гілку «%s»
|
||||
pulls.delete_after_merge.head_branch.insufficient_branch = У вас немає дозволу видаляти головну гілку.
|
||||
activity.published_tag_label = Тег
|
||||
migrate.repo_desc_helper = Залиште порожнім, щоб імпортувати існуючий опис
|
||||
comment.blocked_by_user = Ви не можете коментувати, оскільки вас заблокував власник репозиторію або автор.
|
||||
archive.nocomment = Ви не можете коментувати, оскільки цей репозиторій архівовано.
|
||||
release.type_external_asset = Зовнішній ресурс
|
||||
release.asset_name = Назва ресурсу
|
||||
release.add_external_asset = Додати зовнішній ресурс
|
||||
find_file.no_matching = Не знайдено відповідного файлу
|
||||
commits.search.tooltip = До ключових слів можна додавати префікси «author:», «committer:», «after:» або «before:», наприклад, «revert author:Alice before:2019-01-13».
|
||||
|
||||
[graphs]
|
||||
contributors.what = внески
|
||||
|
@ -2777,6 +2786,8 @@ settings.change_orgname_redirect_prompt.with_cooldown.few = Стара назв
|
|||
teams.none_access = Немає доступу
|
||||
teams.invite_team_member = Запросити до %s
|
||||
teams.write_access = Запис
|
||||
teams.invite.by = Вас запрошує %s
|
||||
teams.invite_team_member.list = Запрошення в очікуванні
|
||||
|
||||
[admin]
|
||||
dashboard=Панель управління
|
||||
|
@ -3282,6 +3293,7 @@ dashboard.task.cancelled = Завдання: %[1]s скасовано: %[3]s
|
|||
config.mailer_protocol = Протокол
|
||||
dashboard.cron.cancelled = Cron: %[1]s скасовано: %[3]s
|
||||
defaulthooks.desc = Вебхуки автоматично сповіщають HTTP-сервер POST-запитами, коли в Forgejo відбуваються певні події. Вказані тут вебхуки є типовими і будуть скопійовані до всіх нових репозиторіїв. Докладніше — в <a target="_blank" rel="noopener" href="%s">посібнику з вебхуків</a>.
|
||||
assets = Ресурси коду
|
||||
|
||||
|
||||
[action]
|
||||
|
@ -3364,7 +3376,7 @@ error.generate_hash=Не вдалося згенерувати хеш коміт
|
|||
error.no_committer_account=Аккаунт користувача з таким Email не знайдено
|
||||
error.no_gpg_keys_found=Не вдалося знайти GPG-ключ, що відповідає даному підпису
|
||||
error.not_signed_commit=Непідписаний коміт
|
||||
error.failed_retrieval_gpg_keys=Не вдалося отримати відповідний GPG-ключ користувача
|
||||
error.failed_retrieval_gpg_keys=Не вдалося отримати ключ, пов'язаний з обліковим записом комітера
|
||||
error.probable_bad_signature=УВАГА! Хоча ключ із таким ID і є в базі, коміт неможливо ним перевірити! Цей коміт ПІДОЗРІЛИЙ.
|
||||
error.probable_bad_default_signature=УВАГА! Хоча типовий ключ має цей ID, коміт неможливо ним перевірити! Цей коміт ПІДОЗРІЛИЙ.
|
||||
|
||||
|
@ -3491,6 +3503,16 @@ published_by = %[1]s опубліковано <a href="%[2]s">%[3]s</a>
|
|||
swift.install = Додайте пакунок у свій файл <code>Package.swift</code>:
|
||||
settings.link.select = Виберіть репозиторій
|
||||
alpine.registry.info = Виберіть $branch і $repository зі списку нижче.
|
||||
conda.registry = Встановіть цей реєстр як репозиторій Conda у своєму файлі <code>.condarc</code>:
|
||||
owner.settings.chef.title = Реєстр Chef
|
||||
arch.pacman.repo.multi.item = Конфігурація %s
|
||||
maven.registry = Налаштуйте цей реєстр у файлі <code>pom.xml</code> свого проєкту:
|
||||
cargo.registry = Налаштуйте цей реєстр у файлі конфігурації Cargo (наприклад, <code>~/.cargo/config.toml</code>):
|
||||
composer.registry = Налаштуйте цей реєстр у файлі <code>~/.composer/config.json</code>:
|
||||
alpine.registry = Налаштуйте цей реєстр, додавши URL у файл <code>/etc/apk/repositories</code>:
|
||||
cran.registry = Налаштуйте цей реєстр у файлі <code>Rprofile.site</code>:
|
||||
npm.registry = Налаштуйте цей реєстр у файлі <code>.npmrc</code> свого проєкту:
|
||||
chef.registry = Налаштуйте цей реєстр у файлі <code>~/.chef/config.rb</code>:
|
||||
|
||||
[secrets]
|
||||
deletion = Видалити секрет
|
||||
|
@ -3505,6 +3527,7 @@ none = Секретів ще немає.
|
|||
creation.name_placeholder = без урахування регістру, тільки літерно-цифрові символи або підкреслення, не може починатися з GITEA_ або GITHUB_
|
||||
secrets = Секрети
|
||||
creation.value_placeholder = Уведіть довільний вміст. Пробіли на початку та в кінці будуть пропущені.
|
||||
description = Секрети передаються певним діям і не можуть бути прочитані інакше.
|
||||
|
||||
[actions]
|
||||
runners.name=Назва
|
||||
|
@ -3649,8 +3672,16 @@ filepreview.lines = Рядки з %[1]d по %[2]d в %[3]s
|
|||
test = Це тестовий текст. Він не відображається в інтерфейсі користувача Forgejo, а використовується з метою тестування
|
||||
|
||||
[repo.permissions]
|
||||
packages.read = <b>Читати:</b> Дивитись та завантажити пакунки призначені до репозиторію.
|
||||
packages.write = <b>Писати:</b> Публікувати та видаляти пакунки призначені до репозиторію.
|
||||
packages.read = <b>Читати:</b> дивитись та завантажити пакунки, призначені до репозиторію.
|
||||
packages.write = <b>Писати:</b> публікувати та видаляти пакунки, призначені до репозиторію.
|
||||
releases.write = <b>Писати:</b> публікувати, змінювати і видаляти випуски та їхні ресурси.
|
||||
pulls.read = <b>Читати:</b> дивитись і створювати запити на злиття.
|
||||
releases.read = <b>Читати:</b> дивитись і завантажувати випуски.
|
||||
actions.read = <b>Читати:</b> дивитися вбудовані конвеєри CI/CD та їхні журнали.
|
||||
wiki.write = <b>Писати:</b> створювати, оновлювати та видаляти сторінки вбудованої вікі.
|
||||
issues.read = <b>Читати:</b> дивитись і створювати задачі та коментарі.
|
||||
wiki.read = <b>Читати:</b> переглядати вбудовану вікі та її історію.
|
||||
actions.write = <b>Писати:</b> вручну запускати, перезапускати, скасовувати або схвалювати конвеєри CI/CD в очікуванні.
|
||||
|
||||
[munits.data]
|
||||
pib = ПіБ
|
||||
|
|
|
@ -1076,7 +1076,7 @@ quota.sizes.repos.all = 仓库
|
|||
quota.sizes.repos.private = 私有仓库
|
||||
quota.sizes.git.all = Git 内容
|
||||
quota.sizes.git.lfs = Git LFS
|
||||
quota.sizes.assets.all = 资产
|
||||
quota.sizes.assets.all = 资源
|
||||
quota.sizes.assets.attachments.all = 附件
|
||||
quota.sizes.assets.attachments.issues = 工单附件
|
||||
quota.sizes.assets.attachments.releases = 版本发布附件
|
||||
|
@ -1416,7 +1416,7 @@ editor.file_is_a_symlink=`"%s" 是一个符号链接,无法在 web 编辑器
|
|||
editor.filename_is_a_directory=此仓库中已存在名为“%s” 的目录。
|
||||
editor.file_editing_no_longer_exists=正在编辑的文件 %s 已不存在。
|
||||
editor.file_deleting_no_longer_exists=正在删除的文件 %s 已不存在。
|
||||
editor.file_changed_while_editing=文件内容在您进行编辑时被更改。<a target="_blank" rel="noopener noreferrer" href="%s">单击此处</a> 查看具体被更改的内容,或者 <strong>再次提交</strong> 覆盖其它在编辑时产生的更改。
|
||||
editor.file_changed_while_editing=文件内容在您打开文件后被更改。<a target="_blank" rel="noopener noreferrer" href="%s">单击此处</a> 查看具体被更改的内容,或者 <strong>再次提交</strong> 覆盖其它在编辑时产生的更改。
|
||||
editor.file_already_exists=此仓库已经存在名为 %s 的文件。
|
||||
editor.commit_empty_file_header=提交一个空文件
|
||||
editor.commit_empty_file_text=您要提交的文件是空的,继续吗?
|
||||
|
@ -2500,7 +2500,7 @@ settings.protect_branch_name_pattern=受保护的分支名称正则
|
|||
settings.protect_branch_name_pattern_desc=受保护的分支名称正则。语法请参阅<a href="%s">文档</a> 。如:main, release/**
|
||||
settings.protect_patterns=规则
|
||||
settings.protect_protected_file_patterns=受保护的文件模式(使用半角分号“;”分隔)
|
||||
settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用半角分号(“;”)分隔多个模式。 见<a href='%s'>%s</a>文档了解模式语法。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
|
||||
settings.protect_protected_file_patterns_desc=即使用户有权添加、编辑或删除此分支中的文件,也不允许直接更改受保护的文件。 可以使用半角分号(“;”)分隔多个模式。 见<a href="%[1]s">%[2]s</a>文档了解模式语法。例如: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
|
||||
settings.protect_unprotected_file_patterns=不受保护的文件模式(使用半角分号“;”分隔)
|
||||
settings.protect_unprotected_file_patterns_desc=在用户有写权限的情况下允许绕过限制,直接修改设为不保护的文件。如有多个匹配模式,则可用半角分号(“;”)分隔开。见 <a href="%[1]s">%[2]s</a> 的文档以了解匹配模式的格式。例子: <code>.drone.yml</code>、<code>/docs/**/*.txt</code>。
|
||||
settings.add_protected_branch=启用保护
|
||||
|
@ -2846,10 +2846,10 @@ settings.federation_not_enabled = 当前实例未启用邦联功能。
|
|||
issues.author.tooltip.issue = 此用户是本工单的作者。
|
||||
issues.author.tooltip.pr = 此用户是此合并请求的作者。
|
||||
release.type_attachment = 附件
|
||||
release.type_external_asset = 外部资产
|
||||
release.asset_name = 资产名称
|
||||
release.type_external_asset = 外部资源
|
||||
release.asset_name = 资源名称
|
||||
release.asset_external_url = 外部 URL
|
||||
release.add_external_asset = 添加外部资产
|
||||
release.add_external_asset = 添加外部资源
|
||||
release.invalid_external_url = 无效的外部 URL:“%s”
|
||||
milestones.filter_sort.name = 名称
|
||||
settings.pull_mirror_sync_quota_exceeded = 超出配额,未拉取更改。
|
||||
|
@ -2909,6 +2909,9 @@ issues.reopen.blocked_by_user = 由于你已被仓库所有者或工单作者屏
|
|||
pulls.comment.blocked_by_user = 由于你已被仓库所有者或合并请求作者屏蔽,你不能在此合并请求发表评论。
|
||||
issues.filter_no_results = 无结果
|
||||
issues.filter_no_results_placeholder = 尝试调整搜索筛选条件。
|
||||
migrate.repo_desc_helper = 留空以导入现有描述
|
||||
archive.nocomment = 您无法评论,因为此仓库已存档。
|
||||
comment.blocked_by_user = 您无法评论,因为您已被仓库所有者或作者屏蔽。
|
||||
|
||||
[graphs]
|
||||
component_loading=正在加载 %s…
|
||||
|
@ -3053,7 +3056,7 @@ self_check = 自检
|
|||
identity_access=身份及认证
|
||||
users=用户帐户
|
||||
organizations=组织管理
|
||||
assets=代码资产
|
||||
assets=代码资源
|
||||
repositories=仓库管理
|
||||
hooks=Web 钩子
|
||||
integrations=集成
|
||||
|
@ -3660,7 +3663,7 @@ raw_minutes=分钟
|
|||
|
||||
[dropzone]
|
||||
default_message=拖放文件或点击此处上传。
|
||||
invalid_input_type=您不能上传该类型的文件
|
||||
invalid_input_type=您不能上传该类型的文件。
|
||||
file_too_big=文件体积({{filesize}} MB)超过了最大允许体积({{maxFilesize}} MB)
|
||||
remove_file=移除文件
|
||||
|
||||
|
@ -4064,7 +4067,7 @@ code.write = <b>写入:</b>推送到仓库,创建分支和标签。
|
|||
code.read = <b>读取:</b>访问并克隆仓库的代码。
|
||||
actions.read = <b>读取:</b>查看集成的 CI/CD 管道及其日志。
|
||||
issues.write = <b>写入:</b>关闭工单并管理元数据,如标签、里程碑、指派成员、截止日期和依赖。
|
||||
releases.write = <b>写入:</b>发布、编辑和删除版本发布及其资产。
|
||||
releases.write = <b>写入:</b>发布、编辑和删除版本发布及其资源。
|
||||
issues.read = <b>读取:</b>阅读并创建工单和评论。
|
||||
pulls.read = <b>读取:</b>阅读并创建合并请求。
|
||||
releases.read = <b>读取:</b>查看并下载版本发布。
|
||||
|
|
|
@ -2317,9 +2317,9 @@ settings.require_signed_commits=僅接受經簽署的提交
|
|||
settings.require_signed_commits_desc=拒絕未經簽署或未經驗證的提交推送到此分支。
|
||||
settings.protect_branch_name_pattern=受保護的分支名稱式樣
|
||||
settings.protect_protected_file_patterns=受保護檔案的式樣(以半形分號區隔「;」)
|
||||
settings.protect_protected_file_patterns_desc=即便使用者有權限新增、修改、刪除此分支的檔案,仍不允許直接修改受保護的檔案。可以用半形分號「;」分隔多個式樣。請於 <a href='%s'>%s</a> 文件查看模式格式。範例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
|
||||
settings.protect_protected_file_patterns_desc=即便使用者有權限新增、修改、刪除此分支的檔案,仍不允許直接修改受保護的檔案。可以用半形分號「;」分隔多個式樣。請於 <a href="%[1]s">%[2]s</a> 文件查看模式格式。範例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
|
||||
settings.protect_unprotected_file_patterns=未受保護檔案的式樣(以半形分號區隔「;」)
|
||||
settings.protect_unprotected_file_patterns_desc=當使用者有寫入權限時,可繞過推送限制,直接修改未受保護的檔案。可以用半形分號「;」分隔多個模式。請於 <a href='%[1]s'>%[2]s</a> 文件查看模式格式。範例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
|
||||
settings.protect_unprotected_file_patterns_desc=當使用者有寫入權限時,可繞過推送限制,直接修改未受保護的檔案。可以用半形分號「;」分隔多個模式。請於 <a href="%[1]s">%[2]s</a> 文件查看模式格式。範例: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>。
|
||||
settings.add_protected_branch=啟用保護
|
||||
settings.delete_protected_branch=停用保護
|
||||
settings.protected_branch_deletion=刪除分支保護
|
||||
|
|
|
@ -18,5 +18,7 @@
|
|||
"incorrect_root_url": "Tato instance Forgejo je nastavena tak, aby běžela na adrese „%s“. Vy si momentálně prohlížíte Forgejo na jiné adrese, což může způsobit rozbití některých částí aplikace. Správná adresa je ovládána správci Forgejo pomocí nastavení ROOT_URL v souboru app.ini.",
|
||||
"themes.names.forgejo-auto": "Forgejo (podle motivu systému)",
|
||||
"themes.names.forgejo-light": "Forgejo – světlé",
|
||||
"themes.names.forgejo-dark": "Forgejo – tmavé"
|
||||
"themes.names.forgejo-dark": "Forgejo – tmavé",
|
||||
"error.not_found.title": "Stránka nenalezena",
|
||||
"alert.asset_load_failed": "Nepodařilo se načíst soubory příloh z {path}. Ujistěte se, že jsou dané soubory přístupné."
|
||||
}
|
||||
|
|
|
@ -16,5 +16,11 @@
|
|||
"home.explore_orgs": "Udforsk organisationer",
|
||||
"themes.names.forgejo-light": "Forgejo lys",
|
||||
"themes.names.forgejo-dark": "Forgejo mørk",
|
||||
"themes.names.forgejo-auto": "Forgejo (følg systemtema)"
|
||||
"themes.names.forgejo-auto": "Forgejo (følg systemtema)",
|
||||
"error.not_found.title": "Siden blev ikke fundet",
|
||||
"alert.asset_load_failed": "Kunne ikke indlæse aktivfiler fra {path}. Sørg for, at aktivfilerne er tilgængelige.",
|
||||
"install.invalid_lfs_path": "Kan ikke oprette LFS-roden på den angivne sti: %[1]s",
|
||||
"install.lfs_jwt_secret_failed": "Kan ikke generere en LFS JWT-hemmelighed: %[1]s",
|
||||
"settings.adopt": "Adoptere",
|
||||
"alert.range_error": " skal være et tal mellem %[1]s og %[2]s."
|
||||
}
|
||||
|
|
|
@ -16,5 +16,7 @@
|
|||
"incorrect_root_url": "Diese Forgejo-Instanz ist konfiguriert, auf „%s“ bereitgestellt zu werden. Du rufst Forgejo über einen anderen URL auf, was dazu führen könnte, dass einige Bereiche nicht funktionieren. Der anerkannte URL wird durch die Forgejo-Admins mittels der Einstellung ROOT_URL in der app.ini kontrolliert.",
|
||||
"themes.names.forgejo-auto": "Forgejo (folge Systemthema)",
|
||||
"themes.names.forgejo-light": "Forgejo hell",
|
||||
"themes.names.forgejo-dark": "Forgejo dunkel"
|
||||
"themes.names.forgejo-dark": "Forgejo dunkel",
|
||||
"error.not_found.title": "Seite nicht gefunden",
|
||||
"alert.asset_load_failed": "Konnte Asset-Dateien nicht von {path} laden. Bitte stelle sicher, dass auf die Asset-Dateien zugegriffen werden kann."
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
"themes.names.forgejo-light": "Forgejo light",
|
||||
"themes.names.forgejo-dark": "Forgejo dark",
|
||||
"error.not_found.title": "Page not found",
|
||||
"alert.asset_load_failed": "Failed to load asset files from {path}. Please make sure the asset files can be accessed.",
|
||||
"alert.range_error": " must be a number between %[1]s and %[2]s.",
|
||||
"install.invalid_lfs_path": "Unable to create the LFS root at the specified path: %[1]s",
|
||||
"install.lfs_jwt_secret_failed": "Unable to generate a LFS JWT secret: %[1]s",
|
||||
"admin.config.moderation_config": "Moderation configuration",
|
||||
"moderation.report_abuse": "Report abuse",
|
||||
"moderation.report_content": "Report content",
|
||||
|
@ -36,4 +40,4 @@
|
|||
"moderation.submit_report": "Submit report",
|
||||
"moderation.reporting_failed": "Something went wrong while trying to submit the new abuse report: %v",
|
||||
"moderation.reported_thank_you": "Thank you for your report. An administrator will look into it shortly."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,5 +9,10 @@
|
|||
"many": "quiere fusionar %[1]d commits de <code>%[2]s</code> en <code id=\"%[4]s\">%[3]s</code>",
|
||||
"other": ""
|
||||
},
|
||||
"search.milestone_kind": "Buscar hitos…"
|
||||
"search.milestone_kind": "Buscar hitos…",
|
||||
"error.not_found.title": "Página no encontrada",
|
||||
"themes.names.forgejo-auto": "Forgejo (seguir el tema del sistema)",
|
||||
"themes.names.forgejo-dark": "Forgejo oscuro",
|
||||
"themes.names.forgejo-light": "Forgejo claro",
|
||||
"home.welcome.no_activity": "Sin actividad"
|
||||
}
|
||||
|
|
|
@ -16,5 +16,6 @@
|
|||
"home.welcome.activity_hint": "Wala pang laman ang iyong feed. Makikita dito ang iyong mga aksyon at aktibidad mula sa mga repositoryo na pinapanood mo.",
|
||||
"home.explore_repos": "Tuklasin ang mga repositoryo",
|
||||
"home.explore_users": "Tuklasin ang mga user",
|
||||
"home.explore_orgs": "Tuklasin ang mga organisasyon"
|
||||
"home.explore_orgs": "Tuklasin ang mga organisasyon",
|
||||
"error.not_found.title": "Hindi nahanap ang pahina"
|
||||
}
|
||||
|
|
|
@ -9,5 +9,11 @@
|
|||
"many": "vuole unire %[1]d commit da <code>%[2]s</code> a <code id=\"%[4]s\">%[3]s</code>",
|
||||
"other": ""
|
||||
},
|
||||
"search.milestone_kind": "Ricerca tappe..."
|
||||
"search.milestone_kind": "Ricerca traguardi…",
|
||||
"themes.names.forgejo-light": "Forgejo chiaro",
|
||||
"themes.names.forgejo-dark": "Forgejo scuro",
|
||||
"home.welcome.no_activity": "Nessun'attività",
|
||||
"home.explore_repos": "Esplora i repositori",
|
||||
"home.explore_users": "Esplora l'utenza",
|
||||
"home.explore_orgs": "Esplora le organizzazioni"
|
||||
}
|
||||
|
|
|
@ -18,5 +18,7 @@
|
|||
"incorrect_root_url": "Šis Forgejo serveris ir konfigurēts darboties \"%s\". Pašlaik Forgejo tiek apmeklēta ar atšķirīgu URL, kas var radīt atsevišķu lietotnes daļu salūšanu. Kanonisko URL pārrauga Forgejo pārvaldītāji ar app.ini iestatījumu ROOT_URL.",
|
||||
"themes.names.forgejo-auto": "Forgejo (ievērot sistēmas izskatu)",
|
||||
"themes.names.forgejo-light": "Forgejo gaišais",
|
||||
"themes.names.forgejo-dark": "Forgejo tumšais"
|
||||
"themes.names.forgejo-dark": "Forgejo tumšais",
|
||||
"error.not_found.title": "Lapa nav atrasta",
|
||||
"alert.asset_load_failed": "Neizdevās ielādēt līdzekļu datnes no {path}. Lūgums pārliecināties, ka līdzekļu datnēm var piekļūt."
|
||||
}
|
||||
|
|
|
@ -16,5 +16,11 @@
|
|||
"incorrect_root_url": "Deese Forgejo-Instanz is inricht, unner »%s« besöcht to worden. Du bekiekst Forgejo jüüst dör een anner URL, wat daarto föhren kann, dat ’t deelwies nich richtig warkt. De kanonisk URL word vun de Forgejo-Chefs över de ROOT_URL-Instellen in de app.ini kuntrolleert.",
|
||||
"themes.names.forgejo-light": "Forgejo Hell",
|
||||
"themes.names.forgejo-dark": "Forgejo Dunker",
|
||||
"themes.names.forgejo-auto": "Forgejo (Systeem-Thema nagahn)"
|
||||
"themes.names.forgejo-auto": "Forgejo (Systeem-Thema nagahn)",
|
||||
"error.not_found.title": "Sied nich funnen",
|
||||
"alert.asset_load_failed": "Kunn Objekt-Dateien ut {path} nich laden. Bidde wees wiss, dat up de Objekt-Dateien togriepen worden kann.",
|
||||
"install.lfs_jwt_secret_failed": "Kunn dat LFS-JWT-Geheemst nich maken: %[1]s",
|
||||
"settings.adopt": "Övernehmen",
|
||||
"install.invalid_lfs_path": "Kunn de LFS-Ruut an de angeven Padd nich maken: %[1]s",
|
||||
"alert.range_error": " mutt eene Tahl tüsken %[1]s un %[2]s wesen."
|
||||
}
|
||||
|
|
|
@ -4,17 +4,22 @@
|
|||
"other": "heeft %[1]d commits samengevoegd van <code>%[2]s</code> naar <code>%[3]s</code> %[4]s"
|
||||
},
|
||||
"repo.pulls.title_desc": {
|
||||
"one": "wilt %[1]d commit van <code>%[2]s</code> samenvoegen in <code id=\"%[4]s\">%[3]s</code>",
|
||||
"other": "wilt %[1]d commits van <code>%[2]s</code> samenvoegen met <code id=\"%[4]s\">%[3]s</code>"
|
||||
"one": "wil %[1]d commit van <code>%[2]s</code> samenvoegen in <code id=\"%[4]s\">%[3]s</code>",
|
||||
"other": "wil %[1]d commits van <code>%[2]s</code> samenvoegen met <code id=\"%[4]s\">%[3]s</code>"
|
||||
},
|
||||
"search.milestone_kind": "Zoek mijlpalen…",
|
||||
"home.welcome.no_activity": "Geen activiteit",
|
||||
"home.welcome.activity_hint": "Er staat nog niets in uw feed. Uw acties en activiteiten van repositories die u bekijkt zullen hier verschijnen.",
|
||||
"home.explore_repos": "Ontdek repositories",
|
||||
"home.explore_users": "Ontdek gebruikers",
|
||||
"home.explore_orgs": "Ontdek organisaties",
|
||||
"home.welcome.activity_hint": "Er staat nog niets in uw feed. Uw acties en activiteiten van repositories die u monitort zullen hier verschijnen.",
|
||||
"home.explore_repos": "Verken repositories",
|
||||
"home.explore_users": "Verken gebruikers",
|
||||
"home.explore_orgs": "Verken organisaties",
|
||||
"incorrect_root_url": "Deze Forgejo-instantie is geconfigureerd om geserveerd te worden op \"%s\". U bekijkt Forgejo momenteel via een andere URL, waardoor onderdelen van de applicatie kunnen breken. De canonieke URL kan worden gewijzigd door Forgejo admins via de ROOT_URL instelling in de app.ini.",
|
||||
"themes.names.forgejo-auto": "Forgejo (volg het systeemthema)",
|
||||
"themes.names.forgejo-light": "Forgejo licht",
|
||||
"themes.names.forgejo-dark": "Forgejo donker"
|
||||
"themes.names.forgejo-dark": "Forgejo donker",
|
||||
"error.not_found.title": "Pagina niet gevonden",
|
||||
"alert.asset_load_failed": "Het laden van asset-bestanden van {path} is mislukt. Controleer of de asset-bestanden toegankelijk zijn.",
|
||||
"settings.adopt": "Adopteer",
|
||||
"install.invalid_lfs_path": "Kan de LFS-root niet aanmaken op het opgegeven pad: %[1]s",
|
||||
"install.lfs_jwt_secret_failed": "Kan geen LFS JWT-geheim genereren: %[1]s"
|
||||
}
|
||||
|
|
|
@ -18,5 +18,7 @@
|
|||
"incorrect_root_url": "Esta instância do Forgejo está configurada para o endereço \"%s\". Você está atualmente vendo o Forgejo através de uma URL diferente, o que pode causar erros em algumas partes da aplicação. A URL oficial é controlada pela administração do Forgejo através da configuração ROOT_URL no arquivo app.ini.",
|
||||
"themes.names.forgejo-auto": "Forgejo (usar o tema do sistema)",
|
||||
"themes.names.forgejo-light": "Forgejo claro",
|
||||
"themes.names.forgejo-dark": "Forgejo escuro"
|
||||
"themes.names.forgejo-dark": "Forgejo escuro",
|
||||
"error.not_found.title": "Página não encontrada",
|
||||
"alert.asset_load_failed": "Não foi possível carregar arquivos de assets de {path}. Por favor, certifique-se que os arquivos podem ser acessados."
|
||||
}
|
||||
|
|
|
@ -18,5 +18,6 @@
|
|||
"incorrect_root_url": "Esta instância do Forgejo está configurada para ser servida em “%s”. Atualmente, está a visualizar o Forgejo através de um URL diferente, o que pode causar a quebra de partes da aplicação. O URL official é controlado pelos administradores do Forgejo através da configuração ROOT_URL no ficheiro app.ini.",
|
||||
"themes.names.forgejo-auto": "Forgejo (segue o tema do sistema)",
|
||||
"themes.names.forgejo-light": "Forgejo claro",
|
||||
"themes.names.forgejo-dark": "Forgejo escuro"
|
||||
"themes.names.forgejo-dark": "Forgejo escuro",
|
||||
"error.not_found.title": "Página não encontrada"
|
||||
}
|
||||
|
|
|
@ -18,5 +18,6 @@
|
|||
"incorrect_root_url": "Этот сервер Forgejo расположен по адресу «%s», но вы просматриваете страницу с другого адреса. Это может приводить к поломкам частей приложения. Канонический адрес указывается администратором сервера в файле конфигурации app.ini - ROOT_URL.",
|
||||
"themes.names.forgejo-light": "Forgejo – светлая",
|
||||
"themes.names.forgejo-auto": "Forgejo – как в системе",
|
||||
"themes.names.forgejo-dark": "Forgejo – тёмная"
|
||||
"themes.names.forgejo-dark": "Forgejo – тёмная",
|
||||
"error.not_found.title": "Страница не найдена"
|
||||
}
|
||||
|
|
|
@ -18,5 +18,11 @@
|
|||
"incorrect_root_url": "Цей екземпляр Forgejo налаштовано на відвідування з «%s». Зараз ви переглядаєте Forgejo за іншою URL-адресою, що може призвести до збоїв деяких частин програми. Канонічна URL-адреса встановлюється адміністраторами Forgejo за допомогою параметра ROOT_URL у файлі app.ini.",
|
||||
"themes.names.forgejo-light": "Forgejo світла",
|
||||
"themes.names.forgejo-dark": "Forgejo темна",
|
||||
"themes.names.forgejo-auto": "Forgejo (як у системі)"
|
||||
"themes.names.forgejo-auto": "Forgejo (як у системі)",
|
||||
"error.not_found.title": "Сторінку не знайдено",
|
||||
"alert.asset_load_failed": "Не вдалося завантажити файли ресурсів з {path}. Переконайтеся, що до файлів ресурсів є доступ.",
|
||||
"install.invalid_lfs_path": "Не вдалося створити корінь LFS за вказаним шляхом: %[1]s",
|
||||
"settings.adopt": "Прийняти",
|
||||
"install.lfs_jwt_secret_failed": "Не вдалося створити секрет LFS JWT: %[1]s",
|
||||
"alert.range_error": " має бути числом від %[1]s до %[2]s."
|
||||
}
|
||||
|
|
|
@ -10,5 +10,10 @@
|
|||
"incorrect_root_url": "此 Forgejo 实例配置为在“%s”上提供服务。您当前正在通过不同的网址查看 Forgejo,这可能会导致应用程序的某些部分损坏。Forgejo 管理员可以通过 app.ini 中的 ROOT_URL 设置控制规范网址。",
|
||||
"themes.names.forgejo-auto": "Forgejo(遵循系统主题)",
|
||||
"themes.names.forgejo-light": "Forgejo 浅色",
|
||||
"themes.names.forgejo-dark": "Forgejo 深色"
|
||||
"themes.names.forgejo-dark": "Forgejo 深色",
|
||||
"error.not_found.title": "页面不存在",
|
||||
"alert.asset_load_failed": "无法从 {path} 加载资源文件。请确保可以访问资源文件。",
|
||||
"install.lfs_jwt_secret_failed": "无法生成 LFS JWT 密钥:%[1]s",
|
||||
"install.invalid_lfs_path": "无法在指定路径创建 LFS 根目录:%[1]s",
|
||||
"alert.range_error": " 必须是一个介于 %[1]s 和 %[2]s 之间的数字。"
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ func Actor(ctx *context.APIContext) {
|
|||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := user_model.APActorUserAPActorID()
|
||||
link := user_model.APServerActorID()
|
||||
actor := ap.ActorNew(ap.IRI(link), ap.ApplicationType)
|
||||
|
||||
actor.PreferredUsername = ap.NaturalLanguageValuesNew()
|
||||
|
@ -46,7 +46,7 @@ func Actor(ctx *context.APIContext) {
|
|||
actor.PublicKey.ID = ap.IRI(link + "#main-key")
|
||||
actor.PublicKey.Owner = ap.IRI(link)
|
||||
|
||||
publicKeyPem, err := activitypub.GetPublicKey(ctx, user_model.NewAPActorUser())
|
||||
publicKeyPem, err := activitypub.GetPublicKey(ctx, user_model.NewAPServerActor())
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPublicKey", err)
|
||||
return
|
||||
|
|
|
@ -6,59 +6,141 @@ package activitypub
|
|||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"database/sql"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/forgefed"
|
||||
"forgejo.org/models/user"
|
||||
"forgejo.org/modules/activitypub"
|
||||
"forgejo.org/modules/httplib"
|
||||
fm "forgejo.org/modules/forgefed"
|
||||
"forgejo.org/modules/log"
|
||||
"forgejo.org/modules/setting"
|
||||
gitea_context "forgejo.org/services/context"
|
||||
"forgejo.org/services/federation"
|
||||
|
||||
"github.com/42wim/httpsig"
|
||||
ap "github.com/go-ap/activitypub"
|
||||
)
|
||||
|
||||
func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err error) {
|
||||
person := ap.PersonNew(ap.IRI(keyID.String()))
|
||||
err = person.UnmarshalJSON(b)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err)
|
||||
}
|
||||
pubKey := person.PublicKey
|
||||
if pubKey.ID.String() != keyID.String() {
|
||||
return nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b))
|
||||
}
|
||||
pubKeyPem := pubKey.PublicKeyPem
|
||||
func decodePublicKeyPem(pubKeyPem string) ([]byte, error) {
|
||||
block, _ := pem.Decode([]byte(pubKeyPem))
|
||||
if block == nil || block.Type != "PUBLIC KEY" {
|
||||
return nil, fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type")
|
||||
}
|
||||
p, err = x509.ParsePKIXPublicKey(block.Bytes)
|
||||
return p, err
|
||||
|
||||
return block.Bytes, nil
|
||||
}
|
||||
|
||||
func fetch(iri *url.URL) (b []byte, err error) {
|
||||
req := httplib.NewRequest(iri.String(), http.MethodGet)
|
||||
req.Header("Accept", activitypub.ActivityStreamsContentType)
|
||||
req.Header("User-Agent", "Gitea/"+setting.AppVer)
|
||||
resp, err := req.Response()
|
||||
func getFederatedUser(ctx *gitea_context.APIContext, person *ap.Person, federationHost *forgefed.FederationHost) (*user.FederatedUser, error) {
|
||||
dbUser, err := user.GetUserByFederatedURI(ctx, person.ID.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status)
|
||||
if dbUser != nil {
|
||||
federatedUser, err := user.GetFederatedUserByUserID(ctx, dbUser.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if federatedUser != nil {
|
||||
return federatedUser, nil
|
||||
}
|
||||
}
|
||||
b, err = io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize))
|
||||
return b, err
|
||||
|
||||
personID, err := fm.NewPersonID(person.ID.String(), string(federationHost.NodeInfo.SoftwareName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, federatedUser, err := federation.CreateUserFromAP(ctx, personID, federationHost.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return federatedUser, nil
|
||||
}
|
||||
|
||||
func storePublicKey(ctx *gitea_context.APIContext, person *ap.Person, pubKeyBytes []byte) error {
|
||||
federationHost, err := federation.GetFederationHostForURI(ctx, person.ID.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if person.Type == ap.ActivityVocabularyType("Application") {
|
||||
federationHost.KeyID = sql.NullString{
|
||||
String: person.PublicKey.ID.String(),
|
||||
Valid: true,
|
||||
}
|
||||
|
||||
federationHost.PublicKey = sql.Null[sql.RawBytes]{
|
||||
V: pubKeyBytes,
|
||||
Valid: true,
|
||||
}
|
||||
|
||||
_, err = db.GetEngine(ctx).ID(federationHost.ID).Update(federationHost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if person.Type == ap.ActivityVocabularyType("Person") {
|
||||
federatedUser, err := getFederatedUser(ctx, person, federationHost)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
federatedUser.KeyID = sql.NullString{
|
||||
String: person.PublicKey.ID.String(),
|
||||
Valid: true,
|
||||
}
|
||||
|
||||
federatedUser.PublicKey = sql.Null[sql.RawBytes]{
|
||||
V: pubKeyBytes,
|
||||
Valid: true,
|
||||
}
|
||||
|
||||
_, err = db.GetEngine(ctx).ID(federatedUser.ID).Update(federatedUser)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPublicKeyFromResponse(b []byte, keyID *url.URL) (person *ap.Person, pubKeyBytes []byte, p crypto.PublicKey, err error) {
|
||||
person = ap.PersonNew(ap.IRI(keyID.String()))
|
||||
err = person.UnmarshalJSON(b)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err)
|
||||
}
|
||||
|
||||
pubKey := person.PublicKey
|
||||
if pubKey.ID.String() != keyID.String() {
|
||||
return nil, nil, nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b))
|
||||
}
|
||||
|
||||
pubKeyBytes, err = decodePublicKeyPem(pubKey.PublicKeyPem)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
p, err = x509.ParsePKIXPublicKey(pubKeyBytes)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return person, pubKeyBytes, p, err
|
||||
}
|
||||
|
||||
func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, err error) {
|
||||
if !setting.Federation.SignatureEnforced {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
r := ctx.Req
|
||||
|
||||
// 1. Figure out what key we need to verify
|
||||
|
@ -66,23 +148,78 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
ID := v.KeyId()
|
||||
idIRI, err := url.Parse(ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
signatureAlgorithm := httpsig.Algorithm(setting.Federation.SignatureAlgorithms[0])
|
||||
|
||||
// 2. Fetch the public key of the other actor
|
||||
b, err := fetch(idIRI)
|
||||
// Try if the signing actor is an already known federated user
|
||||
federationUser, err := user.GetFederatedUserByKeyID(ctx, idIRI.String())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
pubKey, err := getPublicKeyFromResponse(b, idIRI)
|
||||
|
||||
if federationUser != nil && federationUser.PublicKey.Valid {
|
||||
pubKey, err := x509.ParsePKIXPublicKey(federationUser.PublicKey.V)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
authenticated = v.Verify(pubKey, signatureAlgorithm) == nil
|
||||
return authenticated, err
|
||||
}
|
||||
|
||||
// Try if the signing actor is an already known federation host
|
||||
federationHost, err := forgefed.FindFederationHostByKeyID(ctx, idIRI.String())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// 3. Verify the other actor's key
|
||||
algo := httpsig.Algorithm(setting.Federation.Algorithms[0])
|
||||
authenticated = v.Verify(pubKey, algo) == nil
|
||||
|
||||
if federationHost != nil && federationHost.PublicKey.Valid {
|
||||
pubKey, err := x509.ParsePKIXPublicKey(federationHost.PublicKey.V)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
authenticated = v.Verify(pubKey, signatureAlgorithm) == nil
|
||||
return authenticated, err
|
||||
}
|
||||
|
||||
// Fetch missing public key
|
||||
actionsUser := user.NewAPServerActor()
|
||||
clientFactory, err := activitypub.GetClientFactory(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
b, err := apClient.GetBody(idIRI.String())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
person, pubKeyBytes, pubKey, err := getPublicKeyFromResponse(b, idIRI)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
authenticated = v.Verify(pubKey, signatureAlgorithm) == nil
|
||||
if authenticated {
|
||||
err = storePublicKey(ctx, person, pubKeyBytes)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return authenticated, err
|
||||
}
|
||||
|
||||
|
|
|
@ -840,22 +840,22 @@ func Routes() *web.Route {
|
|||
m.Group("/activitypub", func() {
|
||||
// deprecated, remove in 1.20, use /user-id/{user-id} instead
|
||||
m.Group("/user/{username}", func() {
|
||||
m.Get("", activitypub.Person)
|
||||
m.Get("", activitypub.ReqHTTPSignature(), activitypub.Person)
|
||||
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
|
||||
}, context.UserAssignmentAPI(), checkTokenPublicOnly())
|
||||
m.Group("/user-id/{user-id}", func() {
|
||||
m.Get("", activitypub.Person)
|
||||
m.Get("", activitypub.ReqHTTPSignature(), activitypub.Person)
|
||||
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
|
||||
}, context.UserIDAssignmentAPI(), checkTokenPublicOnly())
|
||||
m.Group("/actor", func() {
|
||||
m.Get("", activitypub.Actor)
|
||||
m.Post("/inbox", activitypub.ActorInbox)
|
||||
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.ActorInbox)
|
||||
})
|
||||
m.Group("/repository-id/{repository-id}", func() {
|
||||
m.Get("", activitypub.Repository)
|
||||
m.Get("", activitypub.ReqHTTPSignature(), activitypub.Repository)
|
||||
m.Post("/inbox",
|
||||
bind(forgefed.ForgeLike{}),
|
||||
// TODO: activitypub.ReqHTTPSignature(),
|
||||
activitypub.ReqHTTPSignature(),
|
||||
activitypub.RepositoryInbox)
|
||||
}, context.RepositoryIDAssignmentAPI())
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub))
|
||||
|
|
|
@ -585,7 +585,7 @@ func DeleteFilePost(ctx *context.Context) {
|
|||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
} else if models.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
|
||||
} else if git.IsErrPushRejected(err) {
|
||||
errPushRej := err.(*git.ErrPushRejected)
|
||||
if len(errPushRej.Message) == 0 {
|
||||
|
|
|
@ -1313,7 +1313,7 @@ func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *use
|
|||
}
|
||||
|
||||
// Special user that can't have associated contributions and permissions in the repo.
|
||||
if poster.IsGhost() || poster.IsActions() || poster.IsAPActor() {
|
||||
if poster.IsGhost() || poster.IsActions() || poster.IsAPServerActor() {
|
||||
return roleDescriptor, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1044,7 +1044,15 @@ func renderHomeCode(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if entry.IsDir() {
|
||||
if entry.IsSubModule() {
|
||||
subModuleURL, err := ctx.Repo.Commit.GetSubModule(entry.Name())
|
||||
if err != nil {
|
||||
HandleGitError(ctx, "Repo.Commit.GetSubModule", err)
|
||||
return
|
||||
}
|
||||
subModuleFile := git.NewSubModuleFile(ctx.Repo.Commit, subModuleURL, entry.ID.String())
|
||||
ctx.Redirect(subModuleFile.RefURL(setting.AppURL, ctx.Repo.Repository.FullName(), setting.SSH.Domain))
|
||||
} else if entry.IsDir() {
|
||||
renderDirectory(ctx)
|
||||
} else {
|
||||
renderFile(ctx, entry)
|
||||
|
|
|
@ -16,12 +16,8 @@ import (
|
|||
|
||||
// AdoptOrDeleteRepository adopts or deletes a repository
|
||||
func AdoptOrDeleteRepository(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("settings.adopt")
|
||||
ctx.Data["PageIsSettingsRepos"] = true
|
||||
allowAdopt := ctx.IsUserSiteAdmin() || setting.Repository.AllowAdoptionOfUnadoptedRepositories
|
||||
ctx.Data["allowAdopt"] = allowAdopt
|
||||
allowDelete := ctx.IsUserSiteAdmin() || setting.Repository.AllowDeleteOfUnadoptedRepositories
|
||||
ctx.Data["allowDelete"] = allowDelete
|
||||
|
||||
dir := ctx.FormString("id")
|
||||
action := ctx.FormString("action")
|
||||
|
|
|
@ -95,6 +95,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u
|
|||
RequestedReviewersTeams: []*api.Team{},
|
||||
|
||||
AllowMaintainerEdit: pr.AllowMaintainerEdit,
|
||||
Flow: int64(pr.Flow),
|
||||
|
||||
Base: &api.PRBranchInfo{
|
||||
Name: pr.BaseBranch,
|
||||
|
|
|
@ -98,39 +98,47 @@ func ProcessLikeActivity(ctx context.Context, form any, repositoryID int64) (int
|
|||
}
|
||||
|
||||
func CreateFederationHostFromAP(ctx context.Context, actorID fm.ActorID) (*forgefed.FederationHost, error) {
|
||||
actionsUser := user.NewActionsUser()
|
||||
actionsUser := user.NewAPServerActor()
|
||||
clientFactory, err := activitypub.GetClientFactory(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := clientFactory.WithKeys(ctx, actionsUser, "no idea where to get key material.")
|
||||
|
||||
client, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := client.GetBody(actorID.AsWellKnownNodeInfoURI())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeInfoWellKnown, err := forgefed.NewNodeInfoWellKnown(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err = client.GetBody(nodeInfoWellKnown.Href)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeInfo, err := forgefed.NewNodeInfo(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := forgefed.NewFederationHost(nodeInfo, actorID.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = forgefed.CreateFederationHost(ctx, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
|
@ -155,18 +163,18 @@ func GetFederationHostForURI(ctx context.Context, actorURI string) (*forgefed.Fe
|
|||
}
|
||||
|
||||
func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostID int64) (*user.User, *user.FederatedUser, error) {
|
||||
// ToDo: Do we get a publicKeyId from server, repo or owner or repo?
|
||||
actionsUser := user.NewActionsUser()
|
||||
actionsUser := user.NewAPServerActor()
|
||||
clientFactory, err := activitypub.GetClientFactory(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
client, err := clientFactory.WithKeys(ctx, actionsUser, "no idea where to get key material.")
|
||||
|
||||
apClient, err := clientFactory.WithKeys(ctx, actionsUser, actionsUser.APActorKeyID())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
body, err := client.GetBody(personID.AsURI())
|
||||
body, err := apClient.GetBody(personID.AsURI())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -176,26 +184,32 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if res, err := validation.IsValid(person); !res {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
log.Info("Fetched valid person:%q", person)
|
||||
|
||||
localFqdn, err := url.ParseRequestURI(setting.AppURL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
email := fmt.Sprintf("f%v@%v", uuid.New().String(), localFqdn.Hostname())
|
||||
loginName := personID.AsLoginName()
|
||||
name := fmt.Sprintf("%v%v", person.PreferredUsername.String(), personID.HostSuffix())
|
||||
fullName := person.Name.String()
|
||||
|
||||
if len(person.Name) == 0 {
|
||||
fullName = name
|
||||
}
|
||||
|
||||
password, err := password.Generate(32)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
newUser := user.User{
|
||||
LowerName: strings.ToLower(name),
|
||||
Name: name,
|
||||
|
@ -209,16 +223,18 @@ func CreateUserFromAP(ctx context.Context, personID fm.PersonID, federationHostI
|
|||
IsAdmin: false,
|
||||
NormalizedFederatedURI: personID.AsURI(),
|
||||
}
|
||||
|
||||
federatedUser := user.FederatedUser{
|
||||
ExternalID: personID.ID,
|
||||
FederationHostID: federationHostID,
|
||||
}
|
||||
|
||||
err = user.CreateFederatedUser(ctx, &newUser, &federatedUser)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
log.Info("Created federatedUser:%q", federatedUser)
|
||||
|
||||
log.Info("Created federatedUser:%q", federatedUser)
|
||||
return &newUser, &federatedUser, nil
|
||||
}
|
||||
|
||||
|
@ -274,7 +290,8 @@ func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorID())
|
||||
|
||||
apclient, err := apclientFactory.WithKeys(ctx, &doer, doer.APActorKeyID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -285,7 +302,7 @@ func SendLikeActivities(ctx context.Context, doer user.User, repoID int64) error
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = apclient.Post(json, fmt.Sprintf("%v/inbox/", activity.Object))
|
||||
_, err = apclient.Post(json, fmt.Sprintf("%s/inbox", activity.Object))
|
||||
if err != nil {
|
||||
log.Error("error %v while sending activity: %q", err, activity)
|
||||
}
|
||||
|
|
|
@ -725,8 +725,8 @@ func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) bi
|
|||
|
||||
// AddTimeManuallyForm form that adds spent time manually.
|
||||
type AddTimeManuallyForm struct {
|
||||
Hours int `binding:"Range(0,1000)"`
|
||||
Minutes int `binding:"Range(0,1000)"`
|
||||
Hours int `binding:"Range(0,1000)" locale:"repo.issues.add_time_hours"`
|
||||
Minutes int `binding:"Range(0,1000)" locale:"repo.issues.add_time_minutes"`
|
||||
}
|
||||
|
||||
// Validate validates the fields
|
||||
|
|
|
@ -504,6 +504,28 @@ func (g *GiteaDownloader) GetComments(commentable base.Commentable) ([]*base.Com
|
|||
return allComments, true, nil
|
||||
}
|
||||
|
||||
type ForgejoPullRequest struct {
|
||||
gitea_sdk.PullRequest
|
||||
Flow int64 `json:"flow"`
|
||||
}
|
||||
|
||||
// Extracted from https://gitea.com/gitea/go-sdk/src/commit/164e3358bc02213954fb4380b821bed80a14824d/gitea/pull.go#L347-L364
|
||||
func (g *GiteaDownloader) fixPullHeadSha(pr *ForgejoPullRequest) error {
|
||||
if pr.Base != nil && pr.Base.Repository != nil && pr.Base.Repository.Owner != nil && pr.Head != nil && pr.Head.Ref != "" && pr.Head.Sha == "" {
|
||||
owner := pr.Base.Repository.Owner.UserName
|
||||
repo := pr.Base.Repository.Name
|
||||
refs, _, err := g.client.GetRepoRefs(owner, repo, pr.Head.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(refs) == 0 {
|
||||
return fmt.Errorf("unable to resolve PR ref %q", pr.Head.Ref)
|
||||
}
|
||||
pr.Head.Sha = refs[0].Object.SHA
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPullRequests returns pull requests according page and perPage
|
||||
func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullRequest, bool, error) {
|
||||
if perPage > g.maxPerPage {
|
||||
|
@ -511,16 +533,30 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques
|
|||
}
|
||||
allPRs := make([]*base.PullRequest, 0, perPage)
|
||||
|
||||
prs, _, err := g.client.ListRepoPullRequests(g.repoOwner, g.repoName, gitea_sdk.ListPullRequestsOptions{
|
||||
prs := make([]*ForgejoPullRequest, 0, perPage)
|
||||
opt := gitea_sdk.ListPullRequestsOptions{
|
||||
ListOptions: gitea_sdk.ListOptions{
|
||||
Page: page,
|
||||
PageSize: perPage,
|
||||
},
|
||||
State: gitea_sdk.StateAll,
|
||||
})
|
||||
}
|
||||
|
||||
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", url.PathEscape(g.repoOwner), url.PathEscape(g.repoName)))
|
||||
link.RawQuery = opt.QueryEncode()
|
||||
_, err := getParsedResponse(g.client, "GET", link.String(), http.Header{"content-type": []string{"application/json"}}, nil, &prs)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("error while listing pull requests (page: %d, pagesize: %d). Error: %w", page, perPage, err)
|
||||
}
|
||||
|
||||
if g.client.CheckServerVersionConstraint(">= 1.14.0") != nil {
|
||||
for i := range prs {
|
||||
if err := g.fixPullHeadSha(prs[i]); err != nil {
|
||||
return nil, false, fmt.Errorf("error while listing pull requests (page: %d, pagesize: %d). Error: %w", page, perPage, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pr := range prs {
|
||||
var milestone string
|
||||
if pr.Milestone != nil {
|
||||
|
@ -598,6 +634,7 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques
|
|||
MergeCommitSHA: mergeCommitSHA,
|
||||
IsLocked: pr.IsLocked,
|
||||
PatchURL: pr.PatchURL,
|
||||
Flow: pr.Flow,
|
||||
Head: base.PullRequestBranch{
|
||||
Ref: headRef,
|
||||
SHA: headSHA,
|
||||
|
|
|
@ -307,3 +307,46 @@ func TestGiteaDownloadRepo(t *testing.T) {
|
|||
},
|
||||
}, reviews)
|
||||
}
|
||||
|
||||
func TestForgejoDownloadRepo(t *testing.T) {
|
||||
token := os.Getenv("CODE_FORGEJO_TOKEN")
|
||||
|
||||
fixturePath := "./testdata/code-forgejo-org/full_download"
|
||||
server := unittest.NewMockWebServer(t, "https://code.forgejo.org", fixturePath, token != "")
|
||||
defer server.Close()
|
||||
|
||||
downloader, err := NewGiteaDownloader(t.Context(), server.URL, "Gusted/agit-test", "", "", token)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, downloader)
|
||||
|
||||
prs, _, err := downloader.GetPullRequests(1, 50)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, prs, 1)
|
||||
|
||||
assertPullRequestEqual(t, &base.PullRequest{
|
||||
Number: 1,
|
||||
PosterID: 63,
|
||||
PosterName: "Gusted",
|
||||
PosterEmail: "postmaster@gusted.xyz",
|
||||
Title: "Add extra information",
|
||||
State: "open",
|
||||
Created: time.Date(2025, time.April, 1, 20, 28, 45, 0, time.UTC),
|
||||
Updated: time.Date(2025, time.April, 1, 20, 28, 45, 0, time.UTC),
|
||||
Base: base.PullRequestBranch{
|
||||
CloneURL: "",
|
||||
Ref: "main",
|
||||
SHA: "79ebb873a6497c8847141ba9706b3f757196a1e6",
|
||||
RepoName: "agit-test",
|
||||
OwnerName: "Gusted",
|
||||
},
|
||||
Head: base.PullRequestBranch{
|
||||
CloneURL: server.URL + "/Gusted/agit-test.git",
|
||||
Ref: "refs/pull/1/head",
|
||||
SHA: "667e9317ec37b977e6d3d7d43e3440636970563c",
|
||||
RepoName: "agit-test",
|
||||
OwnerName: "Gusted",
|
||||
},
|
||||
PatchURL: server.URL + "/Gusted/agit-test/pulls/1.patch",
|
||||
Flow: 1,
|
||||
}, prs[0])
|
||||
}
|
||||
|
|
16
services/migrations/gitea_sdk_hack.go
Normal file
16
services/migrations/gitea_sdk_hack.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
_ "unsafe" // Needed for go:linkname support
|
||||
|
||||
gitea_sdk "code.gitea.io/sdk/gitea"
|
||||
)
|
||||
|
||||
//go:linkname getParsedResponse code.gitea.io/sdk/gitea.(*Client).getParsedResponse
|
||||
func getParsedResponse(client *gitea_sdk.Client, method, path string, header http.Header, body io.Reader, obj any) (*gitea_sdk.Response, error)
|
|
@ -802,6 +802,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model
|
|||
MergeBase: pr.Base.SHA,
|
||||
Index: pr.Number,
|
||||
HasMerged: pr.Merged,
|
||||
Flow: issues_model.PullRequestFlow(pr.Flow),
|
||||
|
||||
Issue: &issue,
|
||||
}
|
||||
|
|
|
@ -136,6 +136,7 @@ func assertPullRequestEqual(t *testing.T, expected, actual *base.PullRequest) {
|
|||
assert.ElementsMatch(t, expected.Assignees, actual.Assignees)
|
||||
assert.Equal(t, expected.IsLocked, actual.IsLocked)
|
||||
assertReactionsEqual(t, expected.Reactions, actual.Reactions)
|
||||
assert.Equal(t, expected.Flow, actual.Flow)
|
||||
}
|
||||
|
||||
func assertPullRequestsEqual(t *testing.T, expected, actual []*base.PullRequest) {
|
||||
|
|
File diff suppressed because one or more lines are too long
7
services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fsettings%2Fapi
vendored
Normal file
7
services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fsettings%2Fapi
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
Content-Length: 117
|
||||
Cache-Control: max-age=0, private, must-revalidate, no-transform
|
||||
Content-Type: application/json;charset=utf-8
|
||||
X-Content-Type-Options: nosniff
|
||||
X-Frame-Options: SAMEORIGIN
|
||||
|
||||
{"max_response_items":50,"default_paging_num":30,"default_git_trees_per_page":1000,"default_max_blob_size":10485760}
|
7
services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fversion
vendored
Normal file
7
services/migrations/testdata/code-forgejo-org/full_download/GET_%2Fapi%2Fv1%2Fversion
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
Cache-Control: max-age=0, private, must-revalidate, no-transform
|
||||
Content-Type: application/json;charset=utf-8
|
||||
X-Content-Type-Options: nosniff
|
||||
X-Frame-Options: SAMEORIGIN
|
||||
Content-Length: 53
|
||||
|
||||
{"version":"11.0.0-dev-617-1d1e0ced3e+gitea-1.22.0"}
|
|
@ -31,7 +31,7 @@
|
|||
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
|
||||
<tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
|
||||
<tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<td class="view-detail"><a href="#">{{svg "octicon-note" 16}}</a></td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
|
||||
<tr><td class="tw-text-center" colspan="6">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
{{if .Notices}}
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
<td><a href="{{.OrganisationLink}}/settings" data-tooltip-content="{{ctx.Locale.Tr "edit"}}">{{svg "octicon-pencil"}}</a></td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
|
||||
<tr><td class="tw-text-center" colspan="7">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -75,7 +75,7 @@
|
|||
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td class="tw-text-center" colspan="10">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
|
||||
<tr><td class="tw-text-center" colspan="10">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr><td class="tw-text-center" colspan="12">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
|
||||
<tr><td class="tw-text-center" colspan="12">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -110,7 +110,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr class="no-results-row"><td class="tw-text-center" colspan="9">{{ctx.Locale.Tr "no_results_found"}}</td></tr>
|
||||
<tr class="no-results-row"><td class="tw-text-center" colspan="9">{{ctx.Locale.Tr "repo.pulls.no_results"}}</td></tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
{{template "base/footer_content" .}}
|
||||
|
||||
<script src="{{AssetUrlPrefix}}/js/index.js?v={{AssetVersion}}" onerror="alert('Failed to load asset files from ' + this.src + '. Please make sure the asset files can be accessed.')"></script>
|
||||
<script src="{{AssetUrlPrefix}}/js/index.js?v={{AssetVersion}}" onerror="alert('{{ctx.Locale.Tr "alert.asset_load_failed"}}'.replace('{path}', this.src))"></script>
|
||||
|
||||
{{template "custom/footer" .}}
|
||||
</body>
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<strong>{{ctx.Locale.Tr "repo.audio_not_supported_in_browser"}}</strong>
|
||||
</audio>
|
||||
{{else if .IsPDFFile}}
|
||||
<div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "diff.view_file"}}"></div>
|
||||
<div class="pdf-content is-loading" data-src="{{$.RawFileLink}}" data-fallback-button-text="{{ctx.Locale.Tr "repo.diff.view_file"}}"></div>
|
||||
{{else}}
|
||||
<a href="{{$.RawFileLink}}" rel="nofollow">{{ctx.Locale.Tr "repo.file_view_raw"}}</a>
|
||||
{{end}}
|
||||
|
|
5
templates/swagger/v1_json.tmpl
generated
5
templates/swagger/v1_json.tmpl
generated
|
@ -26133,6 +26133,11 @@
|
|||
"format": "date-time",
|
||||
"x-go-name": "Deadline"
|
||||
},
|
||||
"flow": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"x-go-name": "Flow"
|
||||
},
|
||||
"head": {
|
||||
"$ref": "#/definitions/PRBranchInfo"
|
||||
},
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<div class="inline field">
|
||||
<label></label>
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "sign_in"}}</button>
|
||||
<a href="{{AppSubUrl}}/user/forget_password">{{ctx.Locale.Tr "auth.forget_password"}}</a>
|
||||
<a href="{{AppSubUrl}}/user/forget_password">{{ctx.Locale.Tr "auth.forgot_password"}}</a>
|
||||
</div>
|
||||
{{if .ShowRegistrationButton}}
|
||||
<div class="inline field">
|
||||
|
|
39
tests/e2e/dimmer.test.e2e.ts
Normal file
39
tests/e2e/dimmer.test.e2e.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
// @watch start
|
||||
// templates/shared/user/**
|
||||
// web_src/css/modules/dimmer.ts
|
||||
// web_src/css/modules/dimmer.css
|
||||
// @watch end
|
||||
|
||||
import {expect} from '@playwright/test';
|
||||
import {save_visual, test} from './utils_e2e.ts';
|
||||
|
||||
test.use({user: 'user2'});
|
||||
|
||||
test('Dimmed modal', async ({page}) => {
|
||||
await page.goto('/user1');
|
||||
|
||||
await expect(page.locator('.block')).toContainText('Block');
|
||||
|
||||
// Ensure the modal is hidden
|
||||
await expect(page.locator('#block-user')).toBeHidden();
|
||||
|
||||
await page.locator('.block').click();
|
||||
|
||||
// Modal and dimmer should be visible.
|
||||
await expect(page.locator('#block-user')).toBeVisible();
|
||||
await expect(page.locator('.ui.dimmer')).toBeVisible();
|
||||
await save_visual(page);
|
||||
|
||||
// After canceling, modal and dimmer should be hidden.
|
||||
await page.locator('#block-user .cancel').click();
|
||||
await expect(page.locator('.ui.dimmer')).toBeHidden();
|
||||
await expect(page.locator('#block-user')).toBeHidden();
|
||||
await save_visual(page);
|
||||
|
||||
// Open the block modal and make the dimmer visible again.
|
||||
await page.locator('.block').click();
|
||||
await expect(page.locator('#block-user')).toBeVisible();
|
||||
await expect(page.locator('.ui.dimmer')).toBeVisible();
|
||||
await expect(page.locator('.ui.dimmer')).toHaveCount(1);
|
||||
await save_visual(page);
|
||||
});
|
54
tests/integration/activitypub_client_test.go
Normal file
54
tests/integration/activitypub_client_test.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/unittest"
|
||||
user_model "forgejo.org/models/user"
|
||||
"forgejo.org/modules/activitypub"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/test"
|
||||
"forgejo.org/routers"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestActivityPubClientBodySize(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.Federation.Enabled, true)()
|
||||
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
|
||||
clientFactory, err := activitypub.GetClientFactory(db.DefaultContext)
|
||||
require.NoError(t, err)
|
||||
|
||||
apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.APActorKeyID())
|
||||
require.NoError(t, err)
|
||||
|
||||
url := u.JoinPath("/api/v1/nodeinfo").String()
|
||||
|
||||
// Request with normal MaxSize
|
||||
t.Run("NormalMaxSize", func(t *testing.T) {
|
||||
resp, err := apClient.GetBody(url)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "forgejo")
|
||||
})
|
||||
|
||||
// Set MaxSize to something very low to always fail
|
||||
// Request with low MaxSize
|
||||
t.Run("LowMaxSize", func(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.Federation.MaxSize, 100)()
|
||||
|
||||
_, err = apClient.GetBody(url)
|
||||
require.Error(t, err)
|
||||
assert.ErrorContains(t, err, "Request returned")
|
||||
})
|
||||
})
|
||||
}
|
|
@ -26,33 +26,47 @@ import (
|
|||
func TestActivityPubPerson(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.Federation.Enabled, true)()
|
||||
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
userID := 2
|
||||
username := "user2"
|
||||
userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID)
|
||||
|
||||
userID := 2
|
||||
username := "user2"
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/user-id/%v", userID))
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
assert.Contains(t, resp.Body.String(), "@context")
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
|
||||
var person ap.Person
|
||||
err := person.UnmarshalJSON(resp.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
clientFactory, err := activitypub.GetClientFactory(db.DefaultContext)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, ap.PersonType, person.Type)
|
||||
assert.Equal(t, username, person.PreferredUsername.String())
|
||||
keyID := person.GetID().String()
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%v$", userID), keyID)
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%v/outbox$", userID), person.Outbox.GetID().String())
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%v/inbox$", userID), person.Inbox.GetID().String())
|
||||
apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.APActorKeyID())
|
||||
require.NoError(t, err)
|
||||
|
||||
pubKey := person.PublicKey
|
||||
assert.NotNil(t, pubKey)
|
||||
publicKeyID := keyID + "#main-key"
|
||||
assert.Equal(t, pubKey.ID.String(), publicKeyID)
|
||||
// Unsigned request
|
||||
t.Run("UnsignedRequest", func(t *testing.T) {
|
||||
req := NewRequest(t, "GET", userURL)
|
||||
MakeRequest(t, req, http.StatusBadRequest)
|
||||
})
|
||||
|
||||
pubKeyPem := pubKey.PublicKeyPem
|
||||
assert.NotNil(t, pubKeyPem)
|
||||
assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", pubKeyPem)
|
||||
t.Run("SignedRequestValidation", func(t *testing.T) {
|
||||
// Signed requset
|
||||
resp, err := apClient.GetBody(userURL)
|
||||
require.NoError(t, err)
|
||||
|
||||
var person ap.Person
|
||||
err = person.UnmarshalJSON(resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, ap.PersonType, person.Type)
|
||||
assert.Equal(t, username, person.PreferredUsername.String())
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d$", userID), person.GetID())
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d/outbox$", userID), person.Outbox.GetID().String())
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d/inbox$", userID), person.Inbox.GetID().String())
|
||||
|
||||
assert.NotNil(t, person.PublicKey)
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/user-id/%d#main-key$", userID), person.PublicKey.ID)
|
||||
|
||||
assert.NotNil(t, person.PublicKey.PublicKeyPem)
|
||||
assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----", person.PublicKey.PublicKeyPem)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestActivityPubMissingPerson(t *testing.T) {
|
||||
|
|
|
@ -28,18 +28,28 @@ import (
|
|||
func TestActivityPubRepository(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.Federation.Enabled, true)()
|
||||
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
repositoryID := 2
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%v", repositoryID))
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
assert.Contains(t, resp.Body.String(), "@context")
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
repositoryID := 2
|
||||
|
||||
var repository forgefed_modules.Repository
|
||||
err := repository.UnmarshalJSON(resp.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
apServerActor := user.NewAPServerActor()
|
||||
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/repository-id/%v$", repositoryID), repository.GetID().String())
|
||||
cf, err := activitypub.GetClientFactory(db.DefaultContext)
|
||||
require.NoError(t, err)
|
||||
|
||||
c, err := cf.WithKeys(db.DefaultContext, apServerActor, apServerActor.APActorKeyID())
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := c.GetBody(fmt.Sprintf("%sapi/v1/activitypub/repository-id/%d", u, repositoryID))
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, string(resp), "@context")
|
||||
|
||||
var repository forgefed_modules.Repository
|
||||
err = repository.UnmarshalJSON(resp)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Regexp(t, fmt.Sprintf("activitypub/repository-id/%d$", repositoryID), repository.GetID().String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestActivityPubMissingRepository(t *testing.T) {
|
||||
|
@ -48,7 +58,7 @@ func TestActivityPubMissingRepository(t *testing.T) {
|
|||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
repositoryID := 9999999
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%v", repositoryID))
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/activitypub/repository-id/%d", repositoryID))
|
||||
resp := MakeRequest(t, req, http.StatusNotFound)
|
||||
assert.Contains(t, resp.Body.String(), "repository does not exist")
|
||||
}
|
||||
|
@ -62,14 +72,16 @@ func TestActivityPubRepositoryInboxValid(t *testing.T) {
|
|||
defer federatedSrv.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
actionsUser := user.NewActionsUser()
|
||||
apServerActor := user.NewAPServerActor()
|
||||
repositoryID := 2
|
||||
timeNow := time.Now().UTC()
|
||||
|
||||
cf, err := activitypub.GetClientFactory(db.DefaultContext)
|
||||
require.NoError(t, err)
|
||||
c, err := cf.WithKeys(db.DefaultContext, actionsUser, "not used")
|
||||
|
||||
c, err := cf.WithKeys(db.DefaultContext, apServerActor, apServerActor.APActorKeyID())
|
||||
require.NoError(t, err)
|
||||
|
||||
repoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String()
|
||||
|
||||
activity1 := []byte(fmt.Sprintf(
|
||||
|
@ -139,14 +151,16 @@ func TestActivityPubRepositoryInboxInvalid(t *testing.T) {
|
|||
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
actionsUser := user.NewActionsUser()
|
||||
apServerActor := user.NewAPServerActor()
|
||||
repositoryID := 2
|
||||
|
||||
cf, err := activitypub.GetClientFactory(db.DefaultContext)
|
||||
require.NoError(t, err)
|
||||
c, err := cf.WithKeys(db.DefaultContext, actionsUser, "not used")
|
||||
|
||||
c, err := cf.WithKeys(db.DefaultContext, apServerActor, apServerActor.APActorKeyID())
|
||||
require.NoError(t, err)
|
||||
|
||||
repoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox", repositoryID)).String()
|
||||
repoInboxURL := u.JoinPath(fmt.Sprintf("/api/v1/activitypub/repository-id/%d/inbox", repositoryID)).String()
|
||||
activity := []byte(`{"type":"Wrong"}`)
|
||||
resp, err := c.Post(activity, repoInboxURL)
|
||||
require.NoError(t, err)
|
||||
|
|
82
tests/integration/api_federation_httpsig_test.go
Normal file
82
tests/integration/api_federation_httpsig_test.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"forgejo.org/models/db"
|
||||
"forgejo.org/models/forgefed"
|
||||
"forgejo.org/models/unittest"
|
||||
"forgejo.org/models/user"
|
||||
"forgejo.org/modules/activitypub"
|
||||
"forgejo.org/modules/setting"
|
||||
"forgejo.org/modules/test"
|
||||
"forgejo.org/routers"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFederationHttpSigValidation(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.Federation.Enabled, true)()
|
||||
defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
userID := 2
|
||||
userURL := fmt.Sprintf("%sapi/v1/activitypub/user-id/%d", u, userID)
|
||||
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user.User{ID: 1})
|
||||
|
||||
clientFactory, err := activitypub.GetClientFactory(db.DefaultContext)
|
||||
require.NoError(t, err)
|
||||
|
||||
apClient, err := clientFactory.WithKeys(db.DefaultContext, user1, user1.APActorKeyID())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Unsigned request
|
||||
t.Run("UnsignedRequest", func(t *testing.T) {
|
||||
req := NewRequest(t, "GET", userURL)
|
||||
MakeRequest(t, req, http.StatusBadRequest)
|
||||
})
|
||||
|
||||
// Signed request
|
||||
t.Run("SignedRequest", func(t *testing.T) {
|
||||
resp, err := apClient.Get(userURL)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
})
|
||||
|
||||
// HACK HACK HACK: the host part of the URL gets set to which IP forgejo is
|
||||
// listening on, NOT localhost, which is the Domain given to forgejo which
|
||||
// is then used for eg. the keyID all requests
|
||||
applicationKeyID := fmt.Sprintf("%sapi/v1/activitypub/actor#main-key", setting.AppURL)
|
||||
actorKeyID := fmt.Sprintf("%sapi/v1/activitypub/user-id/1#main-key", setting.AppURL)
|
||||
|
||||
// Check for cached public keys
|
||||
t.Run("ValidateCaches", func(t *testing.T) {
|
||||
host, err := forgefed.FindFederationHostByKeyID(db.DefaultContext, applicationKeyID)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, host)
|
||||
assert.True(t, host.PublicKey.Valid)
|
||||
|
||||
user, err := user.GetFederatedUserByKeyID(db.DefaultContext, actorKeyID)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, user)
|
||||
assert.True(t, user.PublicKey.Valid)
|
||||
})
|
||||
|
||||
// Disable signature validation
|
||||
defer test.MockVariableValue(&setting.Federation.SignatureEnforced, false)()
|
||||
|
||||
// Unsigned request
|
||||
t.Run("SignatureValidationDisabled", func(t *testing.T) {
|
||||
req := NewRequest(t, "GET", userURL)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
})
|
||||
})
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue