From c4c4b8867b7b69972d69aaab9d3049f77b7fcf4b Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 16 May 2025 21:14:41 +0000 Subject: [PATCH 01/94] fix: parse `change-id` in the git commit header (#7884) - `change-id` is a commit header set by [jj-vcs](https://jj-vcs.github.io/jj/). Modify the commit parser to consider this header to be a valid header, this in turn fixes the signature validation on git commits that contain this header. - Resolves forgejo/forgejo#7836 - Added unit test. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7884 Reviewed-by: Otto Co-authored-by: Gusted Co-committed-by: Gusted --- modules/git/commit_reader.go | 2 ++ modules/git/commit_test.go | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/modules/git/commit_reader.go b/modules/git/commit_reader.go index 8e2523d7fb..ec8989f5a7 100644 --- a/modules/git/commit_reader.go +++ b/modules/git/commit_reader.go @@ -85,6 +85,8 @@ readLoop: _, _ = payloadSB.Write(line) case "encoding": _, _ = payloadSB.Write(line) + case "change-id": // jj-vcs specific header. + _, _ = payloadSB.Write(line) case "gpgsig": fallthrough case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present. diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index bc7d99658e..484827149c 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -190,6 +190,55 @@ ISO-8859-1`, commitFromReader.Signature.Payload) assert.Equal(t, commitFromReader, commitFromReader2) } +func TestCommitWithChangeIDFromReader(t *testing.T) { + commitString := `e66911914414b0daa85d4a428c8d607b9b249a2c commit 611 +tree efd3cbedfc360ce9f60e5f92d51221be5afb4bf0 +author Nicole Patricia Mazzuca 1746965490 +0200 +committer Nicole Patricia Mazzuca 1746965630 +0200 +change-id psyxzzozmuvvwrwnpqpvmtwntqsnwzpu +gpgsig -----BEGIN PGP SIGNATURE----- +` + " " + ` + iHUEABYKAB0WIQT/T2ISZ7rMF2EbKVdDm0tNAL/2MgUCaCCUfgAKCRBDm0tNAL/2 + Mmu/AQC0OWWHsSlfDKIArdALjDLgd00OQVbP+6iYVE9e+rorFwEA5qYVAXD60EHB + +7UVcfwZ2jKajkk3q01VyT/CDo3LLQE= + =yq2Y + -----END PGP SIGNATURE----- + +views: first commit! + +includes a basic month view, and prints a nice view of an imaginary +January where the year starts on a Monday :)` + + sha := &Sha1Hash{0xe6, 0x69, 0x11, 0x91, 0x44, 0x14, 0xb0, 0xda, 0xa8, 0x5d, 0x4a, 0x42, 0x8c, 0x8d, 0x60, 0x7b, 0x9b, 0x24, 0x9a, 0x2c} + gitRepo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare")) + require.NoError(t, err) + assert.NotNil(t, gitRepo) + defer gitRepo.Close() + + commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString)) + require.NoError(t, err) + require.NotNil(t, commitFromReader) + assert.EqualValues(t, sha, commitFromReader.ID) + assert.Equal(t, `-----BEGIN PGP SIGNATURE----- + +iHUEABYKAB0WIQT/T2ISZ7rMF2EbKVdDm0tNAL/2MgUCaCCUfgAKCRBDm0tNAL/2 +Mmu/AQC0OWWHsSlfDKIArdALjDLgd00OQVbP+6iYVE9e+rorFwEA5qYVAXD60EHB ++7UVcfwZ2jKajkk3q01VyT/CDo3LLQE= +=yq2Y +-----END PGP SIGNATURE----- +`, commitFromReader.Signature.Signature) + assert.Equal(t, `tree efd3cbedfc360ce9f60e5f92d51221be5afb4bf0 +author Nicole Patricia Mazzuca 1746965490 +0200 +committer Nicole Patricia Mazzuca 1746965630 +0200 +change-id psyxzzozmuvvwrwnpqpvmtwntqsnwzpu + +views: first commit! + +includes a basic month view, and prints a nice view of an imaginary +January where the year starts on a Monday :)`, commitFromReader.Signature.Payload) + assert.Equal(t, "Nicole Patricia Mazzuca ", commitFromReader.Author.String()) +} + func TestHasPreviousCommit(t *testing.T) { bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") From 121623582a16f62d4a9850dd92b9d717338a3d33 Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Sat, 17 May 2025 10:37:51 +0000 Subject: [PATCH 02/94] i18n: update of translations from Codeberg Translate Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Codeberg Translate Co-authored-by: Coral Pink Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Ricky-Tigg Co-authored-by: SomeTr Co-authored-by: guillerpsanchez Co-authored-by: hook Co-authored-by: milimarg Co-authored-by: pixelcode Co-authored-by: xtex Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/es/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/es/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fi/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/sl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hans/ Translation: Forgejo/forgejo Translation: Forgejo/forgejo-next --- options/locale/locale_cs-CZ.ini | 4 +- options/locale/locale_de-DE.ini | 70 ++--- options/locale/locale_es-ES.ini | 5 + options/locale/locale_fi-FI.ini | 400 +++++++++++++------------- options/locale/locale_lv-LV.ini | 10 +- options/locale/locale_sl.ini | 4 +- options/locale/locale_uk-UA.ini | 2 +- options/locale/locale_zh-CN.ini | 228 +++++++-------- options/locale_next/locale_es-ES.json | 17 +- options/locale_next/locale_fr-FR.json | 58 +++- options/locale_next/locale_ru-RU.json | 2 +- 11 files changed, 438 insertions(+), 362 deletions(-) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index e7c65d379e..04c4da2f17 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -502,7 +502,7 @@ activate_account.text_2=Pro aktivaci vašeho účtu klikněte %s na násl activate_email=Ověřte vaši e-mailovou adresu activate_email.title=%s, prosím ověřte vaši e-mailovou adresu -activate_email.text=Pro ověření vaší e-mailové adresy klikněte %s na následující odkaz: +activate_email.text=Pro ověření vaší e-mailové adresy klikněte do %s na následující odkaz: register_notify=Vítejte v %s register_notify.title=%[1]s vítejte v %[2]s @@ -1083,7 +1083,7 @@ quota.sizes.assets.artifacts = Artefakty quota.sizes.assets.packages.all = Balíčky quota.sizes.wiki = Wiki storage_overview = Přehled úložiště -quota.applies_to_user = Následující pravidla kvóty se vztahují na váš účet +quota.applies_to_user = Na váš účet se vztahují následující pravidla kvóty quota.rule.exceeded.helper = Celková velikost objektů pro toto pravidlo přesáhla kvótu. quota.sizes.assets.attachments.releases = Přílohy vydání regenerate_token_success = Token byl znovu vygenerován. Aplikace, které jej používají, již nemají přístup k vašemu účtu a je třeba je aktualizovat s novým tokenem. diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 0e11299306..ed27d53014 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -2323,7 +2323,7 @@ settings.githook_edit_desc=Wenn ein Hook nicht aktiv ist, wird der Standardinhal settings.githook_name=Hook-Name settings.githook_content=Hook-Inhalt settings.update_githook=Hook aktualisieren -settings.add_webhook_desc=Forgejo sendet einen POST-Request mit festgelegtem Content-Type an die Ziel-URL. Mehr Informationen findest du in der Anleitung zu Webhooks (Englisch). +settings.add_webhook_desc=Forgejo sendet eine POST-Anfrage mit festgelegtem Content-Type an die Ziel-URL. Mehr Informationen findest du in der Anleitung zu Webhooks (Englisch). settings.payload_url=Ziel-URL settings.http_method=HTTP-Methode settings.content_type=POST-Content-Type @@ -2385,7 +2385,7 @@ settings.event_pull_request_merge=Pull-Request-Merge settings.event_package=Paket settings.event_package_desc=Paket wurde in einem Repository erstellt oder gelöscht. settings.branch_filter=Branch-Filter -settings.branch_filter_desc=Positivliste für Branches für Push-, Erzeugungs- und Löschevents, als glob-Pattern beschrieben. Es werden Events für alle Branches gemeldet, falls das Pattern * ist, oder falls es leer ist. Siehe die %[2]s-Dokumentation für die Syntax (Englisch). Beispiele: master, {master,release*}. +settings.branch_filter_desc=Positivliste für Branches für Push-, Erzeugungs- und Löschevents, als glob-Pattern beschrieben. Es werden Events für alle Branches gemeldet, falls das Pattern * ist oder falls es leer ist. Siehe die %[2]s-Dokumentation für die Syntax (Englisch). Beispiele: master, {master,release*}. settings.authorization_header=Authorization-Header settings.authorization_header_desc=Wird, falls vorhanden, als Authorization-Header mitgesendet. Beispiele: %s. settings.active=Aktiv @@ -2479,9 +2479,9 @@ settings.require_signed_commits_desc=Pushes auf diesen Branch ablehnen, wenn Com settings.protect_branch_name_pattern=Muster für geschützte Branchnamen settings.protect_patterns=Muster settings.protect_protected_file_patterns=Geschützte Dateimuster (durch Semikolon „;“ getrennt) -settings.protect_protected_file_patterns_desc=Geschützte Dateien dürfen nicht direkt geändert werden, auch wenn der Benutzer Rechte hat, Dateien in diesem Branch hinzuzufügen, zu bearbeiten oder zu löschen. Mehrere Muster können mit Semikolon („;“) getrennt werden. Siehe %s Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. +settings.protect_protected_file_patterns_desc=Geschützte Dateien dürfen nicht direkt geändert werden, auch wenn der Benutzer Rechte hat, Dateien in diesem Branch hinzuzufügen, zu bearbeiten oder zu löschen. Mehrere Muster können mit Semikolon („;“) getrennt werden. Siehe %s-Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. settings.protect_unprotected_file_patterns=Ungeschützte Dateimuster (durch Semikolon „;“ getrennt) -settings.protect_unprotected_file_patterns_desc=Ungeschützte Dateien, die direkt geändert werden dürfen, wenn der Benutzer Schreibzugriff hat, können die Push-Beschränkung umgehen. Mehrere Muster können mit Semikolon („;“) getrennt werden. Siehe %[2]s Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. +settings.protect_unprotected_file_patterns_desc=Ungeschützte Dateien, die direkt geändert werden dürfen, wenn der Benutzer Schreibzugriff hat, können die Push-Beschränkung umgehen. Mehrere Muster können mit Semikolon („;“) getrennt werden. Siehe %[2]s-Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. settings.add_protected_branch=Schutz aktivieren settings.delete_protected_branch=Schutz deaktivieren settings.update_protect_branch_success=Branchschutzregel „%s“ wurde aktualisiert. @@ -2685,7 +2685,7 @@ branch.create_from=`von „%s“` branch.create_success=Branch „%s“ wurde erstellt. branch.branch_already_exists=Branch „%s“ existiert bereits in diesem Repository. branch.branch_name_conflict=Der Branch-Name „%s“ steht in Konflikt mit dem bestehenden Branch „%s“. -branch.tag_collision=Branch „%s“ kann nicht erstellt werden, da in diesem Repository bereits ein Tag mit dem selben Namen existiert. +branch.tag_collision=Branch „%s“ kann nicht erstellt werden, da in diesem Repository bereits ein Tag mit demselben Namen existiert. branch.deleted_by=Von %s gelöscht branch.restore_success=Branch „%s“ wurde wiederhergestellt. branch.restore_failed=Wiederherstellung vom Branch „%s“ gescheitert. @@ -2790,7 +2790,7 @@ pulls.agit_explanation = Mittels AGit-Workflow erstellt. AGit erlaubt Mitwirkend activity.navbar.recent_commits = Neueste Commits activity.navbar.code_frequency = Code-Häufigkeit file_follow = Symlink folgen -error.broken_git_hook = Die Git-Hooks des Repositorys scheinen kaputt zu sein. Bitte folge der Dokumentation um sie zu reparieren, dann pushe einige Commits um den Status zu aktualisieren. +error.broken_git_hook = Die Git-Hooks des Repositorys scheinen kaputt zu sein. Bitte folge der Dokumentation, um sie zu reparieren, dann pushe einige Commits, um den Status zu aktualisieren. pulls.merged_title_desc_one = hat %[1]d Commit von %[2]s nach %[3]s %[4]s zusammengeführt pulls.title_desc_one = möchte %[1]d Commit von %[2]s nach %[3]s zusammenführen open_with_editor = Öffnen mit %s @@ -2815,7 +2815,7 @@ issues.archived_label_description = (Archiviert) %s release.download_count_one = %s Download release.download_count_few = %s Downloads release.system_generated = Dieser Anhang wurde automatisch generiert. -settings.add_webhook.invalid_path = Der Pfad darf kein „.“ oder „..“ enhalten, und darf auch nicht leer sein. Er darf auch nicht mit einem Slash anfangen oder enden. +settings.add_webhook.invalid_path = Der Pfad darf kein „.“ oder „..“ enhalten und darf auch nicht leer sein. Er darf auch nicht mit einem Schrägstrich anfangen oder enden. settings.sourcehut_builds.graphql_url = GraphQL-URL (z.B. https://builds.sr.ht/query) settings.sourcehut_builds.manifest_path = Build-Manifest-Pfad settings.sourcehut_builds.visibility = Job-Sichtbarkeit @@ -2823,9 +2823,9 @@ settings.sourcehut_builds.secrets = Geheimnisse settings.sourcehut_builds.secrets_helper = Dem Job zugriff auf die Build-Geheimnisse geben (benötigt die SECRETS:RO-Berechtigung) settings.web_hook_name_sourcehut_builds = SourceHut-Builds settings.graphql_url = GraphQL-URL -settings.matrix.room_id_helper = Die Raum-ID kann über den Element-Webclient ermittelt werden: Raumeinstellungen > Erweitert > Interne Raum-ID. Beispielsweise %s. +settings.matrix.room_id_helper = Die Raum-ID kann über den Element-Webclient ermittelt werden: Raumeinstellungen > erweitert > interne Raum-ID. Beispielsweise %s. settings.sourcehut_builds.access_token_helper = Zugangstoken, der die JOBS:RW-Freigabe hat. Generiere auf meta.sr.ht einen builds.sr.ht-Token oder einen builds.sr.ht-Token mit Zugriff auf die Secrets. -settings.matrix.access_token_helper = Es wird empfohlen, einen dedizierten Matrix-Account hierfür anzulegen. Der Zugangstoken kann in einem Incognito-Tab über den Element-Webclient geholt werden: Benutzermenü (oben links) > Alle Einstellungen > Hilfe & Über > Erweitert > Zugangstoken (direkt unter der Homeserver-URL). Schließe das Incognito-Tab dann (Abmelden würde den Token ungültig werden lassen). +settings.matrix.access_token_helper = Es wird empfohlen, hierfür ein dediziertes Matrix-Konto anzulegen. Der Zugangstoken kann in einem Inkognito-Tab über den Element-Webclient geholt werden: Benutzermenü (oben links) > alle Einstellungen > Hilfe & Info > erweitert > Zugriffstoken (direkt unter der Heim-Server-URL). Schließe dann den Inkognito-Tab (Abmelden würde den Token ungültig machen). release.hide_archive_links = Automatisch generierte Archive verstecken release.hide_archive_links_helper = Verstecke automatisch generierte Quellcodearchive für diesen Release. Zum Beispiel, wenn du deine eigenen hochlädst. settings.transfer.button = Besitz übertragen @@ -2933,7 +2933,7 @@ org_name_holder=Name der Organisation org_full_name_holder=Vollständiger Name der Organisation org_name_helper=Organisationsnamen sollten kurz und einprägsam sein. create_org=Organisation erstellen -repo_updated=Aktualisiert %s +repo_updated=%s aktualisiert members=Mitglieder teams=Teams code=Quelltext @@ -3006,7 +3006,7 @@ teams.leave.detail=Bist du dir sicher, dass du das Team „%s“ verlassen wills teams.can_create_org_repo=Repositorys erstellen teams.can_create_org_repo_helper=Mitglieder können neue Repositorys in der Organisation erstellen. Der Ersteller erhält Administrator-Zugriff auf das neue Repository. teams.none_access=Kein Zugriff -teams.none_access_helper=Die Option „Kein Zugriff“ hat nur eine Auswirkung auf private Repositorys. +teams.none_access_helper=Die Option „kein Zugriff“ hat nur Auswirkungen auf private Repositorys. teams.general_access=Benutzerdefinierter Zugriff teams.general_access_helper=Mitgliederberechtigungen werden durch folgende Berechtigungstabelle festgelegt. teams.read_access=Lesen @@ -3053,8 +3053,8 @@ teams.invite.by=Von %s eingeladen teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten. follow_blocked_user = Du kannst dieser Organisation nicht folgen, weil diese Organisation dich blockiert hat. open_dashboard = Übersicht öffnen -settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Abkühldauer von einem Tag wieder für alle Verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. -settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Abkühldauer von %[1]d Tagen wieder für alle Verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. +settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Abkühldauer von einem Tag wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. +settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Abkühldauer von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. [admin] dashboard=Übersicht @@ -3066,7 +3066,7 @@ repositories=Repositorys hooks=Webhooks integrations=Integrationen authentication=Authentifizierungsquellen -emails=Benutzer E-Mails +emails=Benutzer-E-Mails config=Konfiguration notices=Systemmitteilungen monitor=Monitoring @@ -3129,7 +3129,7 @@ dashboard.memory_free_times=Speicherfreigaben dashboard.current_heap_usage=Aktuelle Heap-Auslastung dashboard.heap_memory_obtained=Erhaltener Heap-Memory dashboard.heap_memory_idle=Unbenutzter Heap-Memory -dashboard.heap_memory_in_use=Benutzter Heap-Memory +dashboard.heap_memory_in_use=Benutzter-Heap-Memory dashboard.heap_memory_released=Freigegebener Heap-Memory dashboard.heap_objects=Heap-Objekte dashboard.bootstrap_stack_usage=Bootstrap-Stack-Auslastung @@ -3139,7 +3139,7 @@ dashboard.mspan_structures_obtained=Erhaltene MSpan-Structures dashboard.mcache_structures_usage=MCache-Structures-Auslastung dashboard.mcache_structures_obtained=Erhaltene MCache-Structures dashboard.profiling_bucket_hash_table_obtained=Erhaltene Analysesatz-Hashtabellen -dashboard.gc_metadata_obtained=Erhaltene GC-Metadata +dashboard.gc_metadata_obtained=Erhaltene GC-Metadaten dashboard.other_system_allocation_obtained=Andere erhaltene System-Allokationen dashboard.next_gc_recycle=Nächster GC-Zyklus dashboard.last_gc_time=Seit letztem GC-Zyklus @@ -3262,7 +3262,7 @@ packages.size=Größe packages.published=Veröffentlicht defaulthooks=Standard-Webhooks -defaulthooks.desc=Webhooks senden automatisch ein HTTP-POST-Anfragen an einen Server, wenn bestimmte Forgejo-Events ausgelöst werden. Hier definierte Webhooks sind die Standardwerte, die in alle neuen Repositorys kopiert werden. Mehr Infos findest du in der Webhooks-Anleitung (auf Englisch). +defaulthooks.desc=Webhooks senden automatisch HTTP-POST-Anfragen an einen Server, wenn bestimmte Forgejo-Events ausgelöst werden. Hier definierte Webhooks sind die Standardwerte, die in alle neuen Repositorys kopiert werden. Mehr Infos findest du in der Webhooks-Anleitung (auf Englisch). defaulthooks.add_webhook=Standard-Webhook hinzufügen defaulthooks.update_webhook=Standard-Webhook aktualisieren @@ -3343,9 +3343,9 @@ auths.oauth2_required_claim_name_helper=Setze diesen Namen, damit Nutzer aus die auths.oauth2_required_claim_value=Benötigter Claim-Wert auths.oauth2_required_claim_value_helper=Setze diesen Wert, damit Nutzer aus dieser Quelle sich nur anmelden dürfen, wenn sie einen Claim mit diesem Namen und Wert besitzen auths.oauth2_group_claim_name=Claim-Name, der Gruppennamen für diese Quelle angibt. (Optional) -auths.oauth2_admin_group=Gruppen-Claim-Wert für Administratoren. (Optional – erfordert Claim-Namen oben) +auths.oauth2_admin_group=Gruppen-Claim-Wert für Administratoren (optional – erfordert Claim-Namen oben). auths.oauth2_restricted_group=Gruppen-Claim-Wert für eingeschränkte User. (Optional – erfordert Claim-Namen oben) -auths.oauth2_map_group_to_team=Gruppen aus OAuth-Claims den Organisationsteams zuordnen. (Optional – oben muss der Name des Claims angegeben werden) +auths.oauth2_map_group_to_team=Gruppen aus OAuth-Claims den Organisationsteams zuordnen (optional – oben muss der Name des Claims angegeben werden). auths.oauth2_map_group_to_team_removal=Benutzer aus synchronisierten Teams entfernen, wenn der Benutzer nicht zur entsprechenden Gruppe gehört. auths.enable_auto_register=Automatische Registrierung aktivieren auths.sspi_auto_create_users=Benutzer automatisch anlegen @@ -3439,7 +3439,7 @@ config.service_config=Service-Konfiguration config.register_email_confirm=E-Mail-Bestätigung benötigt zum Registrieren config.disable_register=Selbstregistrierung deaktivieren config.allow_only_internal_registration=Registrierung nur über Forgejo selbst erlauben -config.allow_only_external_registration=Registrierung nur über externe Services erlauben +config.allow_only_external_registration=Registrierung nur über externe Dienste erlauben config.enable_openid_signup=OpenID-Selbstregistrierung aktivieren config.enable_openid_signin=OpenID-Anmeldung aktivieren config.show_registration_button=Schaltfläche zum Registrieren anzeigen @@ -3492,7 +3492,7 @@ config.cache_conn=Cache-Anbindung config.cache_item_ttl=Cache-Item-TTL config.session_config=Session-Konfiguration -config.session_provider=Session-Provider +config.session_provider=Session-Anbieter config.provider_config=Provider-Einstellungen config.cookie_name=Cookie-Name config.gc_interval_time=GC-Intervall @@ -3501,7 +3501,7 @@ config.https_only=Nur HTTPS config.cookie_life_time=Cookie-Lebensdauer config.picture_config=Bild-und-Profilbild-Konfiguration -config.picture_service=Bilderservice +config.picture_service=Bilderdienst config.disable_gravatar=Gravatar deaktivieren config.enable_federated_avatar=Föderierte Profilbilder einschalten @@ -3517,11 +3517,11 @@ config.git_clone_timeout=Zeitlimit für Klon config.git_pull_timeout=Zeitlimit für Pull config.git_gc_timeout=Zeitlimit für GC -config.log_config=Konfiguration des Loggings +config.log_config=Protokollierungs-Konfiguration config.logger_name_fmt=Logger: %s config.disabled_logger=Deaktiviert -config.access_log_mode=Zugriffslog-Modus -config.access_log_template=Zugriffslog-Vorlage +config.access_log_mode=Zugriffsprotokoll-Modus +config.access_log_template=Zugriffsprotokoll-Vorlage config.xorm_log_sql=SQL protokollieren config.set_setting_failed=Konfiguration von %s ist fehlgeschlagen @@ -3556,7 +3556,7 @@ monitor.queue.numberworkers=Anzahl der Worker monitor.queue.activeworkers=Aktive Worker monitor.queue.maxnumberworkers=Maximale Anzahl der Worker monitor.queue.numberinqueue=Nummer in der Warteschlange -monitor.queue.review_add=Worker hinzufügen / prüfen +monitor.queue.review_add=Worker hinzufügen/prüfen monitor.queue.settings.title=Pool-Einstellungen monitor.queue.settings.desc=Pools wachsen dynamisch basierend auf der Blockierung der Arbeitswarteschlange. monitor.queue.settings.maxnumberworkers=Maximale Anzahl an Workern @@ -3602,12 +3602,12 @@ config.cache_test_failed = Konnte den Cache nicht untersuchen: %v. config.cache_test_succeeded = Cache-Test erfolgreich, eine Antwort erhalten in %s. config.cache_test = Cache testen config.cache_test_slow = Cache-Test erfolgreich, aber die Antwort ist langsam: %s. -users.block.description = Interaktionen mit diesen Dienst für diesen Benutzer mit seinem Account blockierten und Einloggen verhindern. +users.block.description = Diesem Benutzer verbieten, durch sein Konto mit diesem Dienst zu interagieren, und ihn am Einloggen hindern. users.restricted.description = Nur Interaktionen mit den Repositorys und Organisationen erlauben, wo der Benutzer als Mitarbeiter hinzugefügt wurde. Dies verhindert Zugriff auf öffentliche Repositorys in dieser Instanz. users.local_import.description = Import von Repositorys aus dem lokalen Dateisystem des Servers erlauben. Dies kann ein Sicherheitsproblem sein. users.organization_creation.description = Erstellung neuer Organisationen erlauben. users.activated.description = Abschluss der E-Mail-Verifizierung. Der Besitzer eines nicht aktivierten Accounts wird nicht in der Lage sein, sich einzuloggen, bis die E-Mail-Verifikation abgeschlossen wurde. -users.admin.description = Diesen Benutzer vollständigen Zugriff zu allen administrativen Features gewähren mittels der Web-UI und der API. +users.admin.description = Diesem Benutzer vollständigen Zugriff zu allen administrativen Funktionen gewähren, die über das Web-UI und die API verfügbar sind. emails.delete = E-Mail löschen emails.deletion_success = Die E-Mail-Adresse wurde gelöscht. emails.delete_primary_email_error = Du kannst die primäre E-Mail nicht löschen. @@ -3758,7 +3758,7 @@ conda.install=Um das Paket mit Conda zu installieren, führe den folgenden Befeh container.details.type=Abbildtyp container.details.platform=Plattform container.pull=Downloade das Container-Image aus der Kommandozeile: -container.digest=Digest +container.digest=Prüfsumme container.multi_arch=Betriebsystem / Architektur container.layers=Abbildebenen container.labels=Labels @@ -3778,7 +3778,7 @@ go.install=Installiere das Paket über die Kommandozeile: helm.registry=Diese Paketverwaltung über die Kommandozeile einrichten: helm.install=Nutze folgenden Befehl, um das Paket zu installieren: maven.registry=Setze diese Paketverwaltung in der pom.xml deines Projektes auf: -maven.install=Um das Paket zu verwenden, nimm folgendes in den dependencies-Block in der pom.xml-Datei auf: +maven.install=Um das Paket zu verwenden, nimm Folgendes in den dependencies-Block in der pom.xml-Datei auf: maven.install2=Über die Kommandozeile ausführen: maven.download=Nutze folgendes Kommando, um die Abhängigkeit herunterzuladen: nuget.registry=Diese Registry über die Kommandozeile einrichten: @@ -3853,7 +3853,7 @@ owner.settings.cleanuprules.success.update=Bereinigungsregel wurde aktualisiert. owner.settings.cleanuprules.success.delete=Bereinigungsregel wurde gelöscht. owner.settings.chef.title=Chef-Registry owner.settings.chef.keypair=Schlüsselpaar generieren -owner.settings.chef.keypair.description=Anfragen an die Chef-Registry müssen zur Authentifizierung kryptografisch signiert werden. Beim erstellen eines Schlüsselpaars wird nur der öffentliche Schlüssel in Forgejo gespeichert. Der private Schlüssel wird dir für die Verwendung mit knife bereitgestellt. Das Generieren eines neuen Schlüsselpaars überschreibt das vorherige. +owner.settings.chef.keypair.description=Anfragen an die Chef-Registry müssen zur Authentifizierung kryptografisch signiert werden. Beim Erstellen eines Schlüsselpaars wird nur der öffentliche Schlüssel in Forgejo gespeichert. Der private Schlüssel wird dir für die Verwendung mit knife bereitgestellt. Das Generieren eines neuen Schlüsselpaars überschreibt das vorherige. rpm.repository.multiple_groups = Dieses Paket ist in mehreren Gruppen verfügbar. owner.settings.cargo.rebuild.no_index = Kann nicht erneut erzeugen, es wurde kein Index initialisiert. npm.dependencies.bundle = Gebündelte Abhängigkeiten @@ -4079,12 +4079,12 @@ releases.read = Lesen: Releases ansehen und herunterladen. releases.write = Schreiben: Releases und ihre Assets veröffentlichen, bearbeiten und löschen. wiki.read = Lesen: Das integrierte Wiki und seine Historie lesen. wiki.write = Schreiben: Seiten im integrierten Wiki erstellen, aktualisieren und löschen. -projects.read = Lesen: Zugriff auf Projektboards des Repositories. +projects.read = Lesen: Zugriff auf Projektboards des Repositorys. projects.write = Schreiben: Projekte und Spalten erstellen und bearbeiten. -packages.read = Lesen: Pakete dieses Repositories betrachten und herunterladen. -packages.write = Schreiben: Pakete dieses Repositories veröffentlichen und löschen. +packages.read = Lesen: Pakete dieses Repositorys betrachten und herunterladen. +packages.write = Schreiben: Pakete dieses Repositorys veröffentlichen und löschen. actions.read = Lesen: Integrierte CI/CD-Pipelines und ihre Logs betrachten. -actions.write = Schreiben: Ausstehende CI/CD-Pipelines manuell auslösen, neustarten, abbrechen oder genehmigen. +actions.write = Schreiben: Ausstehende CI/CD-Pipelines manuell auslösen, neu starten, abbrechen oder genehmigen. ext_issues = Zugriff auf den Link zu einem externen Issue-Tracker. Die Berechtigungen werden extern verwaltet. ext_wiki = Zugriff auf den Link zu einem externen Wiki. Die Berechtigungen werden extern verwaltet. pulls.write = Schreiben: Pull-Requests schließen und Metadaten wie Labels, Meilensteine, Zuweisungen, Fälligkeitsdaten und Abhängigkeiten verwalten. diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 79cf5b08ee..63be50f3ce 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -1084,6 +1084,11 @@ quota.sizes.git.lfs = Git LFS quota.sizes.assets.attachments.issues = Archivos adjuntos de incidencia access_token_regeneration = Regenerar token de acceso keep_pronouns_private.description = Esto ocultará sus pronombres a los visitantes que no hayan iniciado sesión. +access_token_regeneration_desc = Regenerar el token revocará el acceso a tu cuenta para todas las aplicaciones que lo estén usando. Esto no se puede deshacer. ¿Continuar? +regenerate_token_success = El token se ha regenerado. Las aplicaciones que lo utilizan ya no tienen acceso a tu cuenta y deben actualizarse con el nuevo token. +quota.applies_to_user = Las siguientes reglas de cuota se aplican a tu cuenta +quota.applies_to_org = Las siguientes reglas de cuota se aplican a esta organización +quota.rule.exceeded.helper = El tamaño total de los objetos para esta regla ha superado la cuota. [repo] owner=Propietario diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index fbb2df0ee0..7fb667f6e2 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -394,7 +394,7 @@ code_no_results=Hakuehtoasi vastaavaa lähdekoodia ei löytynyt. code_last_indexed_at=Viimeksi indeksoitu %s stars_one = %d tähti stars_few = %d tähteä -relevant_repositories = Vain relevantit repositoriot näytetään, näytä suodattamattomat tulokset. +relevant_repositories = Vain asiaankuuluvat tietovarastot näytetään, näytä suodattamattomat tulokset. forks_one = %d haarukka forks_few = %d haarukkaa go_to = Siirry @@ -451,7 +451,7 @@ email_domain_blacklisted=Et voi rekisteröityä sähköpostiosoittellasi. authorize_application=Valtuuta sovellus authorize_redirect_notice=Sinut uudelleen ohjataan osoitteeseen %s jos valtuutat tämän sovelluksen. authorize_application_created_by=Tämän sovelluksen on luonnut %s. -authorize_application_description=Jos myönnät valtuuden, sovellus voi käyttää kaikkia tilitietojasi ja kirjoittaa niihin, mukaan lukien yksityiset repositoriot ja organisaatiot. +authorize_application_description=Jos myönnät valtuuden, se pystyy pääsemään kaikkiin tilitietoihisi ja kirjoittamaan niihin, mukaan lukien yksityiset tietovarastot ja organisaatiot. authorize_title=Valtuutatko "%s" pääsemään tilillesi? authorization_failed=Käyttöoikeuden varmistus epäonnistui sspi_auth_failed=SSPI todennus epäonnistui @@ -524,7 +524,7 @@ admin.new_user.user_info = Käyttäjätiedot activate_email.text = Vahvista sähköpostiosoitteesi napsauttamalla linkkiä aikaikkunan %s sisällä: admin.new_user.subject = Uusi käyttäjä %s rekisteröityi juuri register_notify.text_3 = Jos joku muu teki tämän tilin puolestasi, aseta salasana ensin. -repo.transfer.subject_to_you = %s haluaa siirtää repon "%s" sinulle +repo.transfer.subject_to_you = %s haluaa siirtää tietovaraston "%s" sinulle reply = tai vastaa tähän sähköpostiin suoraan issue.action.new = @%[1]s loi #%[2]d. team_invite.subject = %[1]s kutsui sinut liittymään organisaatioon %[2]s @@ -537,11 +537,11 @@ totp_disabled.text_1 = Tilisi aikapohjainen kertakäyttösalasana (TOTP) poistet issue.action.close = @%[1]s sulki ongelman #%[2]d. issue.action.reopen = @%[1]s avasi uudelleen ongelman #%[2]d. admin.new_user.text = Napsauta tästä hallitaksesi tätä käyttäjää ylläpitonäkymästä. -repo.collaborator.added.text = Sinut on lisätty avustajaksi repoon: +repo.collaborator.added.text = Sinut on lisätty avustajaksi tietovarastoon: primary_mail_change.text_1 = Tilisi ensisijaiseksi sähköpostiosoitteeksi asetettiin %[1]s. Se tarkoittaa, että tämä sähköpostiosoite ei enää vastaanota tilisi ilmoituksia sähköpostitse. team_invite.text_1 = %[1]s on kutsunut sinut liittymään tiimiin %[2]s organisaatiossa %[3]s. -issue_assigned.pull = @%[1]s osoitti sinulle vetopyynnön %[2]s repossa %[3]s. -issue_assigned.issue = @%[1]s osoitti sinulle ongelman %[2]s repossa %[3]s. +issue_assigned.pull = @%[1]s osoitti sinulle vetopyynnön %[2]stietovarastossa %[3]s. +issue_assigned.issue = @%[1]s osoitti sinulle vianlipun %[2]s tietovarastossa %[3]s. register_notify.text_1 = tämä on %s:n rekistöröitymisen vahvistussähköposti! reset_password.text = jos tämä oli sinun toimestasi, ole hyvä ja klikkaa oheista linkkiä palauttaaksesi tilisi %s sisällä: totp_disabled.no_2fa = Muita kaksivaiheisen tunnistautumisen menetelmiä ei ole konfiguroituna, joten et tarvitse kaksivaiheista tunnistautumista kirjautuaaksesi tilillesi. @@ -632,9 +632,9 @@ Biography = Biografia Website = Verkkosivusto Location = Sijainti To = Haaran nimi -still_own_repo = Tilisi omistaa yhden tai useamman repon, poista tai siirrä ne ensin. +still_own_repo = Tilisi omistaa yhden tai useamman tietovaraston; poista tai siirrä ne ensin. organization_leave_success = Olet poistunut organisaatiosta %s. -enterred_invalid_repo_name = Kirjoittamasi repon nimi on virheellinen. +enterred_invalid_repo_name = Syöttämäsi tietovaraston nimi on epäkelvollinen. openid_been_used = OpenID-osoite "%s" on jo käytetty. password_complexity = Salasana ei täytä monimutkaisuusvaatimuksia: still_has_org = Tilisi on jäsen yhdessä tai useamassa organisaatiossa, poistu niistä ensin. @@ -661,7 +661,7 @@ change_avatar=Vaihda profiilikuvasi… repositories=Repot activity=Julkinen toiminta followers_few=%d seuraajaa -starred=Tähdelliset repot +starred=Tähdelliset tietovarastot projects=Projektit overview=Yleiskatsaus following_few=%d seurataan @@ -792,8 +792,8 @@ openid_desc=OpenID mahdollistaa todentamisen delegoinnin ulkopuoliselle palvelun manage_ssh_keys=Hallitse SSH-avaimia manage_gpg_keys=Hallitse GPG-avaimia add_key=Lisää avain -ssh_desc=Nämä julkiset SSH-avaimet on liitetty tiliisi. Vastaavat yksityiset avaimet antavat täyden pääsyn repoihisi. Vahvistettuja SSH-avaimia voi käyttää SSH-allekirjoitettujen Git-kommittien vahvistamiseen. -gpg_desc=Nämä julkiset GPG-avaimet on liitetty tiliisi, ja niitä käytetään kommittien vahvistamiseen. Pidä yksityiset avaimet turvassa, koska ne mahdollistavat kommittien allekirjoittamisen sinun nimissä. +ssh_desc=Nämä julkiset SSH-avaimet on liitetty tiliisi. Vastaavat yksityiset avaimet antavat täyden pääsyn tietovarastoihisi. Vahvistettuja SSH-avaimia voidaan käyttää SSH-allekirjoitettujen Git-sitoumusten varmentamiseen. +gpg_desc=Nämä julkiset GPG-avaimet liitetään tiliisi ja niitä käytetään sitoumuksesi vahvistamiseen. Pidä yksityiset avaimesi turvassa, koska niiden avulla voit allekirjoittaa sitoumuksia henkilöllisyytesi kanssa. ssh_helper=Tarvitsetko apua? Tutustu GitHubin oppaaseen omien SSH-avainten luonnista tai yleisistä ongelmista, joita voit kohdata SSH:n kanssa. gpg_helper=Tarvitsetko apua? Katso GitHubin opas GPG:stä. add_new_key=Lisää SSH avain @@ -916,7 +916,7 @@ blocked_users_none = Käyttäjiä ei ole estetty. location_placeholder = Jaa likimääräinen sijaintisi muiden kanssa retype_new_password = Vahvista uusi salasana create_oauth2_application_success = Loit uuden OAuth2-sovelluksen. -repos_none = Et omista yhtäkään repositoriota. +repos_none = Et omista yhtäkään tietovarastoa. visibility.limited_tooltip = Näkyvissä vain kirjautuneille käyttäjille email_notifications.disable = Poista sähköposti-ilmoitukset käytöstä webauthn_register_key = Lisää turva-avain @@ -931,15 +931,15 @@ email_desc = Ensisijaista sähköpostiosoitettasi käytetään ilmoituksiin, sal tokens_desc = Nämä poletit mahdollistavat pääsyn tilillesi Forgejon rajapintaa vasten. keep_email_private_popup = Sähköpostiosoitettasi ei näytetä profiilissasi, eikä sitä käytetä oletuksena verkkokäyttöliittymän kautta tehtävissä sitoumuksissa, kuten tiedostojen ulospäinlatauksissa, muokkauksissa ja yhdistämissitoumuksissa. Sen sijaan voit käyttää erityistä osoitetta %s sitoumuksien linkittämiseen tiliisi. Tämä vaihtoehto ei vaikuta olemassa oleviin sitoumuksiin. added_on = Lisätty %s -additional_repo_units_hint = Ehdota repositorion lisäyksiköiden käyttöönottoa +additional_repo_units_hint = Ehdota tietovaraston lisäyksiköiden käyttöönottoa revoke_oauth2_grant_success = Pääsy mitätöity. revoke_oauth2_grant = Mitätöi pääsy webauthn_desc = Turva-avaimet ovat kryptografisia avaimia sisältäviä laitteita. Niitä on mahdollista käyttää kaksivaiheiseen todennukseen. Turva-avainten on pakko tukea WebAuthn Authenticator -standardia. permissions_public_only = Vain julkinen -repo_and_org_access = Repojen ja organisaatioiden pääsy +repo_and_org_access = Tietovarastojen ja organisaatioiden pääsy revoke_oauth2_grant_description = Pääsyn mitätöinti tälle kolmannen osapuolen sovellukselle estää sitä pääsemästä dataasi. Oletko varma? twofa_recovery_tip = Jos menetät laitteesi, voit palauttaa pääsyn tiliisi käyttämällä kertakäyttöisen palautusavaimen. -hooks.desc = Lisää webkoukkuja, jotka suoritetaan kaikille omistamillesi repoille. +hooks.desc = Lisää web-koukkuja, jotka aktivoituvat kaikissa omistamissasi tietovarastoissa. revoke_key = Mitätöi permissions_list = Käyttöoikeudet: at_least_one_permission = Pääsymerkin luominen vaatii vähintään yhden käyttöoikeuden @@ -981,7 +981,7 @@ add_email_confirmation_sent = Vahvistusviesti on lähetetty osoitteeseen "%s". V pronouns_custom_label = Mukautetut pronominit openid_deletion_desc = Tämän OpenID-osoitteen poistaminen tililtäsi estää kirjautumisen sitä käyttäen. Jatketaanko? generate_token_name_duplicate = Nimeä %s on jo käytetty sovelluksen nimenä. Käytä eri nimeä. -ssh_signonly = SSH on tällä hetkellä poistettu käytöstä, joten näitä avaimia käytetään vain kommittien allekirjoituksen vahvistamiseen. +ssh_signonly = SSH on tällä hetkellä poistettu käytöstä, joten näitä avaimia käytetään vain sitoumusten allekirjoituksen vahvistamiseen. oauth2_applications_desc = OAuth2-sovellukset mahdollistavat käyttämäsi kolmannen osapuolen sovelluksen todentaa turvallisesti käyttäjiä tähän Forgejo-instanssiin. quota.sizes.assets.attachments.all = Liitteet quota.applies_to_user = Seuraavia kiintiösääntöjä sovelletaan tiliisi @@ -992,9 +992,9 @@ quota.applies_to_org = Seuraavia kiintiösääntöjä sovelletaan tähän organi quota.rule.exceeded = Ylitetty quota.rule.no_limit = Rajoittamaton quota.sizes.all = Kaikki -quota.sizes.repos.all = Repot -quota.sizes.repos.public = Julkiset repot -quota.sizes.repos.private = Yksityiset repot +quota.sizes.repos.all = Tietovarastot +quota.sizes.repos.public = Julkiset tietovarastot +quota.sizes.repos.private = Yksityiset tietovarastot quota.sizes.git.all = Git-sisältö quota.sizes.assets.packages.all = Paketit quota.sizes.wiki = Wiki @@ -1002,7 +1002,7 @@ quota.sizes.wiki = Wiki [repo] owner=Omistaja owner_helper=Jotkin organisaatiot eivät välttämättä näy pudotusvalikossa, koska repojen maksimimäärää on rajoitettu. -repo_name=Repon nimi +repo_name=Tietovaraston nimi repo_name_helper=Hyvä repon nimi on lyhyt, mieleenpainuva ja yksilöllinen. repo_size=Repon koko template=Malli @@ -1011,7 +1011,7 @@ template_helper=Tee reposta mallipohja visibility=Näkyvyys visibility_description=Vain omistaja tai organisaation jäsenet, jos heillä on oikeudet, voivat nähdä sen. visibility_helper_forced=Sivuston ylläpitäjä pakottaa uudet repot olemaan yksityisiä. -fork_repo=Forkkaa repo +fork_repo=Luo tietovaraston haarukka fork_from=Forkkaa lähteestä fork_visibility_helper=Forkatun repon näkyvyyttä ei voi muuttaa. clone_in_vsc=Kloonaa VS Codessa @@ -1025,8 +1025,8 @@ issue_labels_helper=Valitse nimiöjoukko license=Lisenssi license_helper=Valitse lisenssitiedosto readme=README -auto_init=Alusta repo -create_repo=Luo repo +auto_init=Alusta tietovarasto +create_repo=Luo tietovarasto default_branch=Oletushaara mirror_prune=Karsi watchers=Tarkkailijat @@ -1057,7 +1057,7 @@ migrate_items_labels=Tunnisteet migrate_items_issues=Ongelmat migrate_items_pullrequests=Vetopyynnöt migrate_items_releases=Julkaisut -migrate_repo=Tee repomigraatio +migrate_repo=Siirrä tietovarasto migrate.clone_address=Migraatio/kloonaus URL-osoitteesta migrate.github_token_desc=Voit laittaa yhden tai useamman pääsymerkin pilkulla erotellen tähän nopeuttaaksesi migraatiota GitHub APIn vauhtirajojen takia. VAROITUS: Tämän ominaisuuden väärinkäyttö voi rikkoa palveluntarjoajan ehtoja ja johtaa tilin estämiseen. migrate.permission_denied=Sinun ei sallita tuovan paikallisia repoja. @@ -1073,7 +1073,7 @@ unwatch=Lopeta tarkkailu watch=Tarkkaile unstar=Poista tähti star=Tähti -download_archive=Lataa repo +download_archive=Lataa tietovarasto no_desc=Ei kuvausta quick_guide=Pikaopas @@ -1123,9 +1123,9 @@ editor.filename_help=Lisää hakemisto kirjoittamalla sen nimi ja perään kautt editor.or=tai editor.cancel_lower=Peru editor.commit_signed_changes=Sitoudu allekirjoitetut muutokset -editor.commit_changes=Kommitoi muutokset +editor.commit_changes=Sitoudu muutokset editor.add_tmpl=Lisää "<%s>" -editor.commit_directly_to_this_branch=Commitoi suoraan %[1]s haaraan. +editor.commit_directly_to_this_branch=Sitoudu suoraan %[1]s -haaraan. editor.create_new_branch=Luo uusi haara tälle commitille ja aloita vetopyyntö. editor.create_new_branch_np=Luo uusi haara tälle commitille. editor.cancel=Peruuta @@ -1432,7 +1432,7 @@ activity.git_stats_and_deletions=ja activity.git_stats_deletion_1=%d poisto activity.git_stats_deletion_n=%d poistoa -contributors.contribution_type.commits=Kommitit +contributors.contribution_type.commits=Sitoumukset search=Haku search.match=Osuma @@ -1471,7 +1471,7 @@ settings.wiki_delete=Poista wikidata settings.wiki_delete_desc=Repon wikin data poistaminen on pysyvä eikä voi peruuttaa. settings.confirm_wiki_delete=Poista wikidata settings.wiki_deletion_success=Repon wiki data on poistettu. -settings.delete=Poista tämä repo +settings.delete=Poista tämä tietovarasto settings.delete_desc=Repon poistaminen on pysyvä eikä voi peruuttaa. settings.delete_notices_1=- Tätä toimintoa EI VOI peruuttaa myöhemmin. settings.update_settings_success=Repon asetukset on päivitetty. @@ -1500,7 +1500,7 @@ settings.discord_username=Käyttäjätunnus settings.event_desc=Laukaisu päällä: settings.event_send_everything=Kaikki tapahtumat settings.event_choose=Mukautetut tapahtumat… -settings.event_header_repository=Repon tapahtumat +settings.event_header_repository=Tietovaraston tapahtumat settings.event_create=Luo settings.event_create_desc=Haara tai tagi luotu. settings.event_delete=Poista @@ -1581,14 +1581,14 @@ settings.tags.protection.create=Lisää sääntö settings.tags.protection.none=Suojattuja tageja ei ole. settings.bot_token=Botti-poletti settings.matrix.homeserver_url=Kotipalvelimen URL -settings.archive.button=Arkistoi repo -settings.archive.header=Arkistoi tämä repo +settings.archive.button=Arkistoi tietovarasto +settings.archive.header=Arkistoi tämä tietovarasto settings.archive.tagsettings_unavailable=Tagi-asetukset eivät ole käytettävissä arkistoiduissa tietovarastoissa. settings.lfs=LFS settings.lfs_filelist=LFS-tiedostot tallennettu tähän repoon settings.lfs_no_lfs_files=LFS-tiedostoja ei ole tallennettu tähän repoon. settings.lfs_findcommits=Etsi commitit -settings.lfs_lfs_file_no_commits=LFS-tiedostolle ei löytynyt kommitteja +settings.lfs_lfs_file_no_commits=Tälle LFS-tiedostolle ei löytynyt sitoumuksia settings.lfs_noattribute=Tällä polulla ei ole lukittavaa attribuuttia oletushaarassa settings.lfs_delete=Poista LFS-tiedosto OID:lla %s settings.lfs_delete_warning=LFS-tiedoston poistaminen saattaa aiheuttaa "objektia ei ole olemassa" -virheitä uloskirjauksessa. Oletko varma? @@ -1675,23 +1675,23 @@ topic.done=Valmis already_forked = Olet jo forkannut %s fork_to_different_account = Forkkaa toiselle tilille release.compare = Vertaa -release.ahead.commits = %d kommittia +release.ahead.commits = %d sitoumusta all_branches = Kaikki haarat n_tag_few = %s tagia -settings.event_fork_desc = Repo forkattu. +settings.event_fork_desc = Tietovaraston haarukka luotu. actions = Toiminnat -fork_guest_user = Kirjaudu sisään forkataksesi tämän repon. -fork_from_self = Et voi forkata omistamaasi repoa. +fork_guest_user = Kirjaudu sisään luodaksesi tämän tietovaraston haarukan. +fork_from_self = Et voi luoda omistamasi tietovaraston haarukkaa. visibility_fork_helper = (Tämän muuttaminen vaikuttaa kaikkien forkkien näkyvyyteen.) fork = Forkkaa -activity.git_stats_commit_n = %d kommittia +activity.git_stats_commit_n = %d sitoumusta commits.search_branch = Tämä haara n_branch_few = %s haaraa -pulls.show_all_commits = Näytä kaikki kommitit +pulls.show_all_commits = Näytä kaikki sitoumukset commit_graph.select = Valitse haarat -activity.navbar.recent_commits = Viimeisimmät kommitit +activity.navbar.recent_commits = Viimeaikaiset sitoumukset settings.branches.add_new_rule = Lisää uusi sääntö -n_commit_few = %s kommittia +n_commit_few = %s sitoumusta issues.force_push_compare = Vertaa commits.desc = Selaa lähdekoodin muutoshistoriaa. clone_helper = Tarvitseko apua kloonauksen kanssa? Siirry tukisivulle. @@ -1714,7 +1714,7 @@ issues.num_comments_1 = %d kommentti activity.title.issues_n = %d ongelmaa release.detail = Julkaisun tiedot diff.hide_file_tree = Piilota tiedostopuu -issues.role.owner_helper = Tämä käyttäjä on tämän repositorion omistaja. +issues.role.owner_helper = Tämä käyttäjä on tämän tietovaraston omistaja. issues.all_title = Kaikki issues.label_archived_filter = Näytä arkistoidut tunnisteet pulls.close = Sulje vetopyyntö @@ -1731,7 +1731,7 @@ diff.show_more = Näytä enemmän release.deletion_success = Tämä julkaisu on poistettu. issues.filter_milestone_closed = Suljetut merkkipaalut file_copy_permalink = Kopioi pysyväislinkki -empty_message = Tässä repossa ei ole sisältöä. +empty_message = Tämä tietovarasto ei sisällä mitään sisältöä. activity.git_stats_files_changed_n = on muutettu settings.event_pull_request_milestone_desc = Merkkipaalu lisätty, poistettu tai muokattu. settings.event_pull_request_comment = Kommentit @@ -1741,8 +1741,8 @@ issues.filter_milestone_none = Ei merkkipaaluja issues.filter_milestone_open = Avoimet merkkipaalut new_repo_helper = Tietovarasto sisältää kaikki projektitiedostot, mukaan lukien tarkistushistoria. Järjestätkö jo sellaisen muualla? Siirrä tietovarasto. use_template = Käytä tätä mallipohjaa -star_guest_user = Kirjaudu sisään lisätäksesi tähden tähän repoon. -watch_guest_user = Kirjaudu sisään tarkkaillaksesi tätä repoa. +star_guest_user = Kirjaudu sisään lisätäksesi tähden tähän tietovarastoon. +watch_guest_user = Kirjaudu sisään tarkkaillaksesi tätä tietovarastoa. activity.git_stats_author_n = %d tekijää issues.dependency.add_error_dep_exists = Riippuvuus on jo olemassa. wiki.page_content = Sivun sisältö @@ -1767,7 +1767,7 @@ pulls.delete.title = Poistetaanko tämä vetopyyntö? activity.title.issues_1 = %d ongelma contributors.contribution_type.filter_label = Avustuksen tyyppi: settings.protected_branch.delete_rule = Poista sääntö -settings.archive.success = Repo arkistoitiin onnistuneesti. +settings.archive.success = Tietovarasto arkistoitiin onnistuneesti. diff.comment.placeholder = Jätä kommentti release.message = Kuvaile tätä julkaisua branch.delete_desc = Haaran poistaminen on pysyvä toimenpide. Vaikka poistettu haara voi jäädä olemaan lyhyeksi ajaksi, ennen kuin todellisesti poistetaan, poistoa EI VOI perua useimmiten. Jatketaanko? @@ -1783,15 +1783,15 @@ diff.download_diff = Lataa diff-tiedosto settings.event_package = Paketti issues.new.closed_projects = Suljetut projektit settings.event_issue_milestone = Merkkipaalut -branch.branch_already_exists = Haara "%s" on jo olemassa repossa. +branch.branch_already_exists = Haara "%s" on jo olemassa tässä tietovarastossa. projects.card_type.images_and_text = Kuvat ja teksti -default_branch_helper = Oletusarvoinen haara on oletushaara vetopyynnöille ja koodikommiteille. +default_branch_helper = Oletushaara on kantahaara vetopyyntöjä ja koodisitoumuksia varten. author_search_tooltip = Näyttää enintään 30 käyttäjää -migrate_options_mirror_helper = Tästä reposta tulee peili +migrate_options_mirror_helper = Tästä tietovarastosta tulee peili commit_graph.color = Väri commit_graph.hide_pr_refs = Piilota vetopyynnöt executable_file = Suoritettava tiedosto -editor.file_already_exists = Tiedosto nimeltä "%s" on jo olemassa tässä repossa. +editor.file_already_exists = Tiedosto nimeltä "%s" on jo olemassa tässä tietovarastossa. branch.restore = Palauta haara "%s" branch.default_deletion_failed = Haara "%s" on oletushaara. Sitä ei voi poistaa. branch.new_branch = Luo uusi haara @@ -1821,7 +1821,7 @@ settings.slack_color = Väri release.tag_name_already_exist = Julkaisu tällä taginimellä on jo olemassa. pulls.allow_edits_from_maintainers_err = Päivittäminen epäonnistui stars = Tähdet -editor.branch_already_exists = Haara "%s" on jo olemassa tässä repossa. +editor.branch_already_exists = Haara "%s" on jo olemassa tässä tietovarastossa. projects.column.delete = Poista sarake projects.column.color = Väri settings.admin_settings = Ylläpitäjän asetukset @@ -1837,10 +1837,10 @@ activity.opened_prs_count_n = ehdotettua vetopyyntöä activity.title.user_1 = %d käyttäjä activity.title.prs_n = %d vetopyyntöä settings.sourcehut_builds.secrets = Salaisuudet -commit_graph = Kommittikaavio -visibility_helper = Tee reposta yksityinen +commit_graph = Sitoumuskaavio +visibility_helper = Tee tietovarastosta yksityinen pulls.approve_count_1 = %d hyväksyntä -settings.confirm_delete = Poista repositorio +settings.confirm_delete = Poista tietovarasto milestones.new_subheader = Merkkipaalut auttavat hallinnoimaan ongelmia ja seuraamaan niiden korjausten edistymistä. activity.title.user_n = %d käyttäjää activity.title.prs_1 = %d vetopyyntö @@ -1849,10 +1849,10 @@ activity.navbar.pulse = Pulssi wiki.no_search_results = Ei tuloksia settings.federation_settings = Federaation asetukset pull.deleted_branch = (poistettu):%s -settings.transfer.rejected = Repositorion siirto hylättiin. +settings.transfer.rejected = Tietovaraston siirto hylättiin. settings.transfer.modal.title = Siirrä omistajuus settings.event_pull_request_sync = Synkronoitu -editor.commit_empty_file_text = Tiedosto, jonka olet aikeissa kommitoida, on tyhjä. Jatketaanko? +editor.commit_empty_file_text = Tiedosto, jonka olet aikeissa sitoa, on tyhjä. Edetäänkö? diff.load = Lataa diff branch.create_branch_operation = Luo haara activity.title.releases_n = %d julkaisua @@ -1873,12 +1873,12 @@ issues.filter_label_select_no_label = Ei tunnistetta projects.column.set_default = Aseta oletukseksi projects.edit_success = Projekti "%s" on päivitetty. desc.sha256 = SHA256 -n_commit_one = %s kommitti +n_commit_one = %s sitoumus transfer.accept = Hyväksy siirto transfer.reject = Hylkää siirto default_branch_label = oletus repo_desc_helper = Kirjoita lyhyt kuvaus (valinnainen) -create_new_repo_command = Uuden repon luominen komentoriviltä +create_new_repo_command = Uuden tietovaraston luominen komentoriviltä subscribe.issue.guest.tooltip = Kirjaudu sisään tilataksesi tämän ongelman päivitykset. subscribe.pull.guest.tooltip = Kirjaudu sisään tilataksesi tämän vetopyynnön päivitykset. migrate.migrating_failed_no_addr = Migraatio epäonnistui. @@ -1886,8 +1886,8 @@ need_auth = Valtuutus migrate_options = Migraatioasetukset projects.create_success = Projekti "%s" on luotu. projects.description = Kuvaus (valinnainen) -editor.commit_empty_file_header = Kommitoi tyhjä tiedosto -editor.branch_does_not_exist = Haaraa "%s" ei ole olemassa repossa. +editor.commit_empty_file_header = Sitoudu tyhjä tiedosto +editor.branch_does_not_exist = Haaraa "%s" ei ole olemassa tässä tietovarastossa. editor.delete = Poista %s editor.patching = Paikkaus: editor.patch = Toteuta paikkaus @@ -1911,10 +1911,10 @@ milestones.filter_sort.least_complete = Vähiten valmis milestones.filter_sort.most_complete = Eniten valmis activity.title.releases_1 = %d julkaisu settings.branches.update_default_branch = Päivitä oletushaara -settings.transfer.success = Repositorion siirto onnistui. +settings.transfer.success = Tietovaraston siirto onnistui. settings.transfer_abort = Peru siirto settings.sync_mirror = Synkronoi nyt -settings.mirror_settings.docs.doc_link_title = Miten peilaan repoja? +settings.mirror_settings.docs.doc_link_title = Kuinka peilaan tietovarastot? tag.create_tag_operation = Luo tagi branch.rename = Nimeä haara "%s" uudelleen branch.download = Lataa haara "%s" @@ -1939,9 +1939,9 @@ issues.delete.title = Poistetaanko tämä ongelma? migrate.github.description = Tee migraatio github.comista tai GitHub Enterprise -palvelimelta. settings.merge_style_desc = Yhdistämistyylit settings.protected_branch_deletion = Poista haaran suojaus -settings.deletion_success = Repositorio on poistettu. +settings.deletion_success = Tietovarasto on poistettu. editor.filename_is_invalid = Tiedoston nimi on virheellinen: "%s". -push_exist_repo = Olemassa olevan repon työntäminen komentoriviltä +push_exist_repo = Olemassa olevan tietovaraston työntäminen komentoriviltä issues.new.title_empty = Otsikko ei voi olla tyhjä settings.transfer_perform = Suorita siirto activity.git_stats_pushed_n = on työntänyt @@ -1959,17 +1959,17 @@ pulls.cmd_instruction_hint = Näytä komentoriviohjeet settings.wiki_globally_editable = Salli kenen tahansa muokata wikiä pulls.rebase_conflict_summary = Virheviesti wiki.search = Etsi wikistä -activity.commit = Kommittitoiminta +activity.commit = Sitoutumistoiminta editor.cannot_edit_non_text_files = Binääritiedostoja ei voi muokata web-käyttöliittymässä. projects.template.desc_helper = Valitse projektin mallipohja aloittaaksesi -commit.contained_in_default_branch = Tämä kommitti on osa oletushaaraa +commit.contained_in_default_branch = Tämä sitoumus on osa oletushaaraa activity.git_stats_exclude_merges = Poissulkien yhdistämiset -activity.no_git_activity = Tällä ajanjaksolla ei ole ollut kommitointitoimintaa. -activity.git_stats_commit_1 = %d kommitin +activity.no_git_activity = Tällä ajanjaksolla ei ole ollut sitoutumistoimintaa. +activity.git_stats_commit_1 = %d sitoumus activity.git_stats_push_to_all_branches = kaikkiin haaroihin. settings.graphql_url = GraphQL:n URL-osoite branch.create_new_branch = Luo haara haarasta: -settings.archive.error_ismirror = Et voi arkistoida peilattua repoa. +settings.archive.error_ismirror = Et voi arkistoida peilattua tietovarastoa. branch.warning_rename_default_branch = Olet nimeämässä oletushaaran uudelleen. settings.web_hook_name_msteams = Microsoft Teams release.download_count_one = %s lataus @@ -1978,13 +1978,13 @@ diff.file_before = Ennen diff.file_after = Jälkeen settings.protect_new_rule = Luo uusi haaran suojaussääntö settings.branch_filter = Haarasuodatin -object_format_helper = Repositorion objektimuoto. Tätä ei voi muuttaa myöhemmin. SHA1 on yhteensopivin muoto. +object_format_helper = Tietovaraston objektimuoto. Tätä ei voi muuttaa myöhemmin. SHA1 on yhteensopivin muoto. migrate.gogs.description = Tee migraatio notabug.orgista tai muista Gogs-instansseista. migrate.forgejo.description = Tee migraatio codeberg.orgista tai muista Forgejo-instansseista. migrate.gitbucket.description = Tee migraatio GitBucket-instansseista. migrate.onedev.description = Tee migraatio code.onedev.io:sta tai muista OneDev-instansseista. migrate.codebase.description = Tee migraatio codebasehq.comista. -migrate.git.description = Tee repomigraatio mistä tahansa Git-palvelusta. +migrate.git.description = Siirrä tietovarasto mistä tahansa Git-palvelusta. migrate.gitlab.description = Tee migraatio gitlab.comista tai muista GitLab-instansseista. migrate.gitea.description = Tee migraatio gitea.comista tai muista Gitea-instansseista. repo_gitignore_helper_desc = Valitse mitä tiedostoja ei seurata yleisimpien kielten mallipohjista. Tyypilliset artefaktit, joita eri kielten koostamistyökalut tuottavat, lisätään .gitignore-tiedostoon oletusarvoisesti. @@ -1993,36 +1993,36 @@ license_helper_desc = Lisenssi määrää, mitä muut voivat ja eivät voi tehd milestones.filter_sort.earliest_due_data = Lähin määräpäivä issues.filter_type.reviewed_by_you = Katselmoitu toimestasi settings.units.overview = Yleisnäkymä -settings.remove_team_success = Tiimin pääsy repositorioon on poistettu. +settings.remove_team_success = Joukkueen pääsy tietovarastoon on poistettu. migrate.cancel_migrating_confirm = Haluatko perua tämän migraation? settings.units.units = Yksiköt -settings.update_settings_no_unit = Repositorion tulisi sallia edes jonkinlainen vuorovaikutus. +settings.update_settings_no_unit = Tietovaraston tulisi sallia edes jonkinlainen vuorovaikutus. settings.units.add_more = Ota lisää käyttöön -settings.add_team_success = Tiimillä on nyt pääsy repositorioon. +settings.add_team_success = Joukkueella on nyt pääsy tietovarastoon. settings.use_external_issue_tracker = Käytä ulkoista ongelmienseurantaa -settings.transfer_started = Tämä repositorio on merkitty siirrettäväksi ja se odottaa vahvistusta käyttäjältä "%s" -signing.wont_sign.pubkey = Kommittia ei allekirjoiteta, koska sinulla ei ole yhtäkään julkista avainta liitetty tiliisi. -settings.transfer_succeed = Repositorio on siirretty. +settings.transfer_started = Tämä tietovarasto on merkitty siirrettäväksi ja se odottaa vahvistusta "%s":lta +signing.wont_sign.pubkey = Sitoumusta ei allekirjoiteta, koska sinulla ei ole julkista avainta liitetty tiliisi. +settings.transfer_succeed = Tietovarasto on siirretty. activity.git_stats_on_default_branch = Haarassa %s, settings.tracker_issue_style.regexp = Säännöllinen lauseke wiki.reserved_page = Wikisivun nimi "%s" on varattu. pulls.recently_pushed_new_branches = Työnsit haaraan %[1]s %[2]s -signing.will_sign = Tämä kommitti allekirjoitetaan avaimella "%s". -signing.wont_sign.never = Kommitit eivät ole koskaan allekirjoitettuja. +signing.will_sign = Tämä sitoumus allekirjoitetaan avaimella "%s". +signing.wont_sign.never = Sitoumukset eivät ole koskaan allekirjoitettuja. settings.mirror_settings.direction = Suunta -settings.mirror_settings.push_mirror.remote_url = Git-etärepon URL-osoite -settings.issues_desc = Käytä repositorion ongelmienseurantaa +settings.mirror_settings.push_mirror.remote_url = Git-etätietovaraston URL-osoite +settings.issues_desc = Ota tietovaraston vianseuranta käyttöön settings.use_internal_issue_tracker = Käytä sisäänrakennettua ongelmienseurantaa settings.external_tracker_url = Ulkoisen ongelmienseurannan URL-osoite -settings.transfer_abort_success = Repositorion siirto käyttäjälle %s peruttiin. -settings.transfer_quota_exceeded = Uuden omistajan (%s) tilakiintiö on täynnä. Repositoriota ei ole siirretty. -settings.projects_desc = Käytä repositorion projekteja -settings.releases_desc = Käytä repositorion julkaisuja -settings.packages_desc = Käytä repositorion pakettirekisteriä +settings.transfer_abort_success = Tietovaraston siirto %s:hen peruttiin. +settings.transfer_quota_exceeded = Uusi omistaja (%s) on ylittänyt kiintiön. Tietovarastoa ei ole siirretty. +settings.projects_desc = Ota tietovarastoprojektit käyttöön +settings.releases_desc = Ota tietovaraston julkaisut käyttöön +settings.packages_desc = Ota tietovarastopakettien rekisteri käyttöön activity.git_stats_push_to_branch = haaraan %s ja wiki.wiki_page_revisions = Sivun versiot -settings.wiki_desc = Käytä repositorion wikiä -signing.wont_sign.always = Kommitit ovat aina allekirjoitettuja. +settings.wiki_desc = Ota tietovaraston Wiki käyttöön +signing.wont_sign.always = Sitoumukset ovat aina allekirjoitettuja. milestones.edit_subheader = Merkkipaalut järjestävät ongelmia ja seuraavat edistymistä. view_git_blame = Näytä git blame editor.push_rejected = Tämä muutos hylättiin palvelimen toimesta. Tarkista Git-koukut. @@ -2034,26 +2034,26 @@ releases.desc = Seuraa projektin versioita ja latauksia. settings.protect_patterns = Kaavat branch.new_branch_from = Luo uusi haara kohteesta "%s" settings.matrix.message_type = Viestin tyyppi -diff.committed_by = kommitoinut +diff.committed_by = Sitoumuksen toimijana invisible_runes_line = `Tällä rivillä on näkymättömiä Unicode-merkkejä` -editor.fork_before_edit = Sinun täytyy forkata tämä repo tehdäksesi tai ehdottaaksesi muutoksia tähän tiedostoon. -editor.file_deleting_no_longer_exists = Poistettava tiedosto, "%s", ei ole enää olemassa tässä repossa. +editor.fork_before_edit = Sinun on luotava tämän tietovaraston haarukka voidaksesi tehdä tai ehdottaa muutoksia tähän tiedostoon. +editor.file_deleting_no_longer_exists = Poistettavaa tiedostoa "%s" ei enää ole tässä tietovarastossa. editor.add_tmpl.filename = tiedostonimi editor.fail_to_apply_patch = Ei voitu toteuttaa paikkaa "%s" editor.propose_file_change = Ehdota tiedostomuutosta -editor.new_branch_name = Nimeä uusi haara tätä kommittia varten +editor.new_branch_name = Nimeä uusi haara tätä sitoumusta varten editor.new_branch_name_desc = Uuden haaran nimi… -editor.file_editing_no_longer_exists = Muokattava tiedosto, "%s", ei ole enää olemassa tässä repossa. -editor.cannot_commit_to_protected_branch = Suojattuun haaraan "%s" ei voi kommitoida. +editor.file_editing_no_longer_exists = Muokattavaa tiedostoa "%s" ei enää ole tässä tietovarastossa. +editor.cannot_commit_to_protected_branch = Suojattuun haaraan "%s" ei voi sitoutua. issues.remove_request_review = Poista katselmointipyyntö issues.remove_request_review_block = Katselmointipyyntöä ei voi poistaa -pulls.require_signed_wont_sign = Tämä haara vaatii allekirjoitetut kommitit, mutta tätä yhdistämistä ei allekirjoiteta +pulls.require_signed_wont_sign = Haara vaatii allekirjoitettuja sitoumuksia, mutta tätä yhdistämistä ei allekirjoiteta pulls.push_rejected_summary = Koko hylkäysviesti -settings.unarchive.button = Poista repon arkistointi +settings.unarchive.button = Kumoa tietovaraston arkistointi release.type_attachment = Liite tag.create_tag_from = Luo uusi tagi kohteesta"%s" topic.count_prompt = Voit valita korkeintaan 25 aihetta -settings.require_signed_commits = Vaadi allekirjoitetut kommitit +settings.require_signed_commits = Vaadi allekirjoitettuja sitoumuksia editor.push_rejected_summary = Koko hylkäysviesti: release.title = Julkaisun nimi release.tag_helper_existing = Olemassa oleva tagi. @@ -2069,7 +2069,7 @@ archive.title = Tämä tietovarasto on arkistoitu. Voit tarkastella sen tiedosto reactions_more = ja %d lisää mirror_address = Kloonaa URL-osoitteesta migrate_items_merge_requests = Yhdistämispyynnöt -stars_remove_warning = Tämä poistaa kaikki tähdet tästä reposta. +stars_remove_warning = Tämä poistaa kaikki tähdet tästä tietovarastosta. archive.issue.nocomment = Tämä repo on arkistoitu. Et voi kommentoida ongelmia. archive.pull.nocomment = Tämä repo on arkistoitu. Et voi kommentoida vetopyyntöjä. settings.webhook_deletion_desc = Webkoukun poistaminen poistaa sen asetukset ja toimitushistorian. Jatketaanko? @@ -2077,14 +2077,14 @@ settings.discord_icon_url.exceeds_max_length = Kuvakkeen URL-osoite voi sisält settings.event_wiki_desc = Wiki-sivu luotu, nimetty uudelleen, muokattu tai poistettu. settings.event_pull_request_desc = Vetopyyntö avattu, suljettu, avattu uudelleen tai muokattu. settings.protect_branch_name_pattern = Suojatun haaran nimen kaava -issues.dependency.add_error_dep_not_same_repo = Molempien ongelmien tulee olla samassa repossa. +issues.dependency.add_error_dep_not_same_repo = Molempien vikojen tulee olla samassa tietovarastossa. settings.event_release = Julkaisu -pulls.merge_pull_request = Luo yhdistämiskommitti +pulls.merge_pull_request = Luo yhdistämissitoumus settings.pull_mirror_sync_quota_exceeded = Kiintiö ylitetty, ei vedetä muutoksia. settings.wiki_rename_branch_main_notices_1 = Tätä toimintoa EI VOI perua. settings.webhook.test_delivery_desc_disabled = Aktivoi webkoukku testataksesi sitä tekaistulla tapahtumalla. settings.discord_icon_url = Kuvakkeen URL-osoite -settings.archive.branchsettings_unavailable = Haaran asetukset eivät ole saatavilla arkistoiduissa repoissa. +settings.archive.branchsettings_unavailable = Haaran asetukset eivät ole saatavilla arkistoiduissa tietovarastoissa. pulls.ready_for_review = Valmiina katselmointiin? issues.time_spent_total = Käytetty kokonaisaika settings.webhook.test_delivery_desc = Testaa tätä webkoukkua tekaistulla tapahtumalla. @@ -2092,32 +2092,32 @@ pulls.switch_comparison_type = Vaihda vertailutyyppiä settings.hooks_desc = Webkoukut tekevät automaattisesti HTTP POST -pyyntöjä palvelimelle, kun jotkin Forgejo-tapahtumat käynnistyvät. Lue lisää webkoukkujen oppaasta. issues.num_participants_one = %d osallistuja issues.reference_link = Viittaus: %s -settings.transfer_desc = Siirrä tämä repo käyttäjälle tai organisaatiolle, johon sinulla ylläpito-oikeus. +settings.transfer_desc = Siirrä tämä tietovarasto käyttäjälle tai organisaatiolle, johon sinulla on hallintaoikeudet. settings.add_collaborator = Lisää avustaja settings.mirror_settings.push_mirror.none = Push-peilejä ei ole määritetty -settings.collaborator_deletion_desc = Avustajan poistaminen estää hänen pääsyn tähän repoon. Jatketaanko? -settings.archive.text = Repon arkistointi asettaa sen pelkkään lukutilaan. Se piilotetaan hallintapaneelista. Kukaan (et edes sinä!) ei pysty tehdä uusia kommitteja, avata uusia ongelmia tai avata vetopyyntöjä. -settings.mirror_settings.docs = Määritä reposi automaattisesti synkronoimaan kommitit, tagit ja haarat toiseen repoon. -settings.add_collaborator_duplicate = Avustaja on jo lisätty tähän repoon. -settings.add_collaborator_blocked_them = Avustajaa ei voi lisätä, koska kyseinen avustaja on estänyt repon omistajan. -settings.add_collaborator_blocked_our = Avustajaa ei voi lisätä, koska repon omistaja on estänyt hänet. -settings.default_branch_desc = Valitse repon oletushaara, johon vetopyynnöt ja koodikommitit kohdistetaan: +settings.collaborator_deletion_desc = Yhteistyöhenkilön poistaminen peruuttaa hänen pääsynsä tähän tietovarastoon. Jatketaanko? +settings.archive.text = Tietovaraston arkistointi tekee siitä kokonaan vain-lukuisen. Se piilotetaan kojelaudalta. Kukaan –et edes sinä– ei voi tehdä uusia sitoumuksia tai avata vianlippuja tai vetopyyntöjä. +settings.mirror_settings.docs = Määritä tietovarastosi synkronoimaan sitoumukset, tagit ja haarat automaattisesti toisen tietovaraston kanssa. +settings.add_collaborator_duplicate = Yhteistyöhenkilö on jo lisätty tähän tietovarastoon. +settings.add_collaborator_blocked_them = Yhteistyöhenkilöä ei voida lisätä, koska hän on estänyt tietovaraston omistajan. +settings.add_collaborator_blocked_our = Yhteistyöhenkilöä ei voi lisätä, koska tietovaraston omistaja on estänyt hänet. +settings.default_branch_desc = Valitse oletustietovaraston haara vetopyyntöjä ja koodin sitoumuksia varten: issues.role.collaborator = Avustaja -settings.trust_model.collaboratorcommitter.long = Avustaja+kommitoija: Luota avustajien allekirjoituksiin, jotka vastaavat kommitoijaa +settings.trust_model.collaboratorcommitter.long = Yhteistyöhenkilö + Sitoumuksen toimija: Luota yhteistyöhenkilöiden allekirjoituksiin, jotka vastaavat sitoumuksen toimijaa settings.collaborator_deletion = Poista avustaja wiki.desc = Kirjoita ja jaa dokumentaatiota avustajien kesken. settings.trust_model.collaborator = Avustaja -mirror_sync_on_commit = Synkronoi kun kommitit pushataan -settings.mirror_settings.docs.disabled_pull_mirror.instructions = Määritä projektisi automaattisesti pushaamaan kommitit, tagit ja haarat toiseen repoon. Pull-peilit on poistettu käytöstä tämän sivuston ylläpitäjän toimesta. +mirror_sync_on_commit = Synkronoi, kun sitoumuksia työnnetään +settings.mirror_settings.docs.disabled_pull_mirror.instructions = Määritä projektisi työntämään sitoumukset, tagit ja haarat automaattisesti toiseen tietovarastoon. Sivustosi järjestelmänvalvoja on poistanut vetopeilit käytöstä. settings.mirror_settings.docs.more_information_if_disabled = Löydät lisätietoja push- ja pull-peileistä täältä: settings.mirror_settings.push_mirror.add = Lisää push-peili settings.mirror_settings.push_mirror.edit_sync_time = Muokkaa peilin synkronoinnin aikaväliä -settings.trust_model.collaboratorcommitter = Avustaja+kommitoija +settings.trust_model.collaboratorcommitter = Yhteistyöhenkilö + Sitoumuksen toimija settings.trust_model.default = Oletusarvoinen luottamusmalli -settings.admin_enable_health_check = Käytä repositorion terveystarkastuksia (git fsck) +settings.admin_enable_health_check = Ota tietovaraston terveystarkastukset käyttöön (git fsck) settings.remove_collaborator_success = Avustaja on poistettu. -issues.role.collaborator_helper = Tämä käyttäjä on kutsuttu avustajaksi tähän repoon. -settings.pulls_desc = Käytä repositorion vetopyyntöjä +issues.role.collaborator_helper = Tämä käyttäjä on kutsuttu yhteistyöhön tietovaraston parissa. +settings.pulls_desc = Ota tietovaraston vetopyynnöt käyttöön mirror_interval_invalid = Peilauksen aikaväli ei ole kelvollinen. settings.collaboration = Avustajat settings.trust_model = Allekirjoituksen luottamusmalli @@ -2126,32 +2126,32 @@ mirror_interval = Peilauksen aikaväli (kelvolliset yksiköt ovat "h", "m", "s") settings.add_collaborator_success = Avustaja on lisätty. settings.add_collaborator_owner = Omistajaa ei voi lisätä avustajaksi. settings.signing_settings = Allekirjoituksen vahvistuksen asetukset -settings.mirror_settings.docs.disabled_push_mirror.instructions = Määritä projektisi automaattisesti vetämään kommitit, tagit ja haarat toisesta reposta. +settings.mirror_settings.docs.disabled_push_mirror.instructions = Määritä projektisi automaattisesti vetämään sitoumukset, tagit ja haarat toisesta tietovarastosta. settings.trust_model.collaborator.long = Avustaja: Luota avustajien allekirjoituksiin issues.dependency.setting = Käytä riippuvuuksia ongelmiin ja vetopyyntöihin settings.allow_only_contributors_to_track_time = Salli vain avustajien seurata aikaa settings.actions_desc = Käytä integroituja CI-/CD-putkia Forgejo Actionsia hyödyntäen -settings.admin_enable_close_issues_via_commit_in_any_branch = Sulje ongelma kommitin toimesta, joka on tehty muuhun kuin oletusarvoiseen haaraan -settings.mirror_settings.pushed_repository = Työnnetty repo +settings.admin_enable_close_issues_via_commit_in_any_branch = Sulje vianlippu muussa kuin oletushaarassa tehdyllä sitoumuksella +settings.mirror_settings.pushed_repository = Työnnetty tietovarasto pulls.compare_changes_desc = Valitse haara, johon yhdistetään, ja haara, josta vedetään. no_eol.text = Ei EOL:ää auto_init_description = Aloita Git-historia README-tiedostolla ja valinnaisesti License- ja .gitignore-tiedostoilla. new_from_template = Käytä mallipohjaa -new_from_template_description = Voit valita olemassa olevan repon mallipohjan ja toteuttaa sen asetukset. +new_from_template_description = Voit valita olemassa olevan tietovarastomallipohjan tässä ilmentymässä ja käyttää sen asetuksia. new_advanced = Lisäasetukset new_advanced_expand = Laajenna napsauttamalla -template_description = Repojen mallipohjat mahdollistavat uusien repojen luomisen halutulla hakemistorakenteella, tiedostoilla ja valinnaisilla asetuksilla. -settings.enter_repo_name = Kirjoita omistajan ja repon nimi täsmälleen kuten esitetty: +template_description = Mallipohjaisten tietovarastojen avulla käyttäjät voivat luoda uusia tietovarastoja, joilla on sama hakemistorakenne, tiedostot ja valinnaiset asetukset. +settings.enter_repo_name = Syötä omistajan ja tietovaraston nimi täsmälleen kuten esitetty: settings.confirmation_string = Vahvistusteksti -settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi repon %s mukaan lukien koodin, ongelmat, kommentit, wikidatan ja avustaja-asetukset. +settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi %s tietovaraston, joka sisältää koodin, viat, kommentit, Wiki-tiedot ja yhteistyöhenkilön asetukset. issues.filter_assginee_no_select = Kaikki käsittelijät issues.new.assign_to_me = Osoita itselle pulls.closed_at = `sulki tämän vetopyynnön %[2]s` tree_path_not_found_branch = Polkua %[1]s ei ole olemassa haarassa %[2]s transfer.no_permission_to_reject = Sinulla ei ole oikeutta hylätä tätä siirtoa. -generate_repo = Luo repo -tree_path_not_found_commit = Polkua %[1]s ei ole olemassa kommitissa %[2]s -archive.pull.noreview = Tämä repo on arkistoitu. Et voi katselmoida vetopyyntöjä. +generate_repo = Luo tietovarasto +tree_path_not_found_commit = Polkua %[1]s ei ole olemassa sitoumuksessa %[2]s +archive.pull.noreview = Tämä tietovarasto on arkistoitu. Et voi arvioida vetopyyntöjä. tree_path_not_found_tag = Polkua %[1]s ei ole olemassa tagissa %[2]s transfer.no_permission_to_accept = Sinulla ei ole oikeutta hyväksyä tätä siirtoa. settings.web_hook_name_feishu = Feishu / Lark Suite @@ -2161,14 +2161,14 @@ issues.add_label = lisäsi tunnisteen %s %s issues.due_date_added = lisäsi määräpäivän %s %s issues.review.add_review_request = pyysi katselmointia käyttäjältä %[1]s %[2]s issues.ref_pull_from = `viittasi tähän vetopyyntöön %[4]s %[2]s` -pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista %[2]s` +pulls.commit_ref_at = `viittasi tähän vetopyyntöön sitoumuksesta %[2]s` issues.review.comment = katselmoi %s issues.add_labels = lisäsi tunnisteet %s %s issues.review.add_review_requests = pyysi katselmointeja käyttäjiltä %[1]s %[2]s pulls.blocked_by_official_review_requests = Tämä vetopyyntö on estetty, koska siltä puuttuu hyväksyntä yhdeltä tai useammalta viralliselta katselmoijalta. issues.author.tooltip.issue = Tämä käyttäjä on tämän ongelman tekijä. issues.author.tooltip.pr = Tämä käyttäjä on tämän vetopyynnön tekijä. -issues.role.contributor_helper = Tämä käyttäjä on aiemmin kommitoinut tähän repoon. +issues.role.contributor_helper = Tämä käyttäjä on aiemmin sitoutunut tähän tietovarastoon. settings.event_pull_request_label = Tunnisteet issues.due_date_remove = poisti määräpäivän %s %s settings.event_issue_label = Tunnisteet @@ -2235,7 +2235,7 @@ component_loading_info = Tämä saattaa kestää hetken… component_failed_to_load = Odottamaton virhe. component_loading = Ladataan %s… contributors.what = panokset -recent_commits.what = viimeisimmät kommitit +recent_commits.what = viimeaikaiset sitoumukset code_frequency.what = koodifrekvenssi component_loading_failed = Ei voitu ladata %s @@ -2314,13 +2314,13 @@ teams.delete_team_desc=Tiimin poisto peruuttaa sen jäseniltä oikeuden pääst teams.delete_team_success=Tiimi on poistettu. teams.read_permission_desc=Tämä tiimi myöntää jäsenille Luku oikeudet: tiimin jäsenet voivat katsella ja kloonata tiimin varastoja. teams.write_permission_desc=Tämä tiimi myöntää jäsenille Kirjoitus oikeuden: tiimin jäsenet voivat lukea ja kirjoittaa tiimin repoihin. -teams.admin_permission_desc=Tämä tiimi myöntää jäsenille Ylläpito-oikeudet: Tiimin jäsenet voivat työntää (push) ja vetää (pull) tiimin varastoista/varastoihin ja lisätä yhteistyökumppaneita. -teams.repositories=Tiimin repot +teams.admin_permission_desc=Tämä joukkue myöntää järjestelmänvalvojapääsyn: jäsenet voivat lukea joukkueen tietovarastoista, vetää ja lisätä yhteistyöhenkilöitä niihin. +teams.repositories=Joukkueen tietovarastot teams.members.none=Ei jäseniä tässä tiimissä. teams.all_repositories=Kaikki repot teams.invite.by = Kutsunut %s members.leave.detail = Haluatko varmasti poistua organisaatiosta "%s"? -teams.add_all_repos_title = Lisää kaikki repot +teams.add_all_repos_title = Lisää kaikki tietovarastot teams.invite_team_member.list = Odottavat kutsut teams.invite.description = Napsauta alla olevaa painiketta liittyäksesi tiimiin. settings.update_setting_success = Organisaatioasetukset on päivitetty. @@ -2330,17 +2330,17 @@ teams.invite.title = Sinut on kutsuttu tiimiin %s organisaatios teams.add_duplicate_users = Käyttäjä on jo tiimijäsen. settings.visibility.limited = Rajattu (näkyvissä vain kirjautuneille käyttäjille) code = Koodi -teams.remove_all_repos_title = Poista kaikki tiimin repot +teams.remove_all_repos_title = Poista kaikki joukkueen tietovarastot form.name_reserved = Organisaation nimi "%s" on varattu. settings.delete_org_desc = Organisaatio poistetaan pysyvästi. Jatketaanko? -team_access_desc = Repositorion käyttö -teams.specific_repositories = Määritetyt repositoriot +team_access_desc = Tietovarastopääsy +teams.specific_repositories = Määritetyt tietovarastot open_dashboard = Avaa kojelauta -teams.remove_all_repos_desc = Tämä poistaa kaikki repot tiimiltä. -teams.add_all_repos_desc = Tämä lisää kaikki organisaation repot tiimille. +teams.remove_all_repos_desc = Tämä poistaa kaikki tietovarastot joukkueelta. +teams.add_all_repos_desc = Tämä lisää kaikki organisaation tietovarastot joukkueelle. team_unit_disabled = (Pois käytöstä) follow_blocked_user = Et voi seurata tätä organisaatiota, koska organisaatio on estänyt sinut. -teams.can_create_org_repo = Luo repoja +teams.can_create_org_repo = Luo tietovarastoja teams.none_access = Ei pääsyä form.name_pattern_not_allowed = Kaava "%s" ei ole sallittu organisaation nimessä. settings.email = Yhteydenoton sähköposti @@ -2369,34 +2369,34 @@ dashboard.operation_name=Toiminnon nimi dashboard.operation_switch=Vaihda dashboard.operation_run=Suorita dashboard.delete_inactive_accounts=Poista kaikki aktivoimattomat käyttäjät -dashboard.delete_repo_archives=Poista kaikki repojen arkistot (ZIP, TAR.GZ, jne..) +dashboard.delete_repo_archives=Poista kaikki tietovarastojen arkistot (ZIP, TAR.GZ, jne.) dashboard.server_uptime=Palvelimen uptime dashboard.current_goroutine=Nykyiset goroutinet dashboard.current_memory_usage=Nykyinen muistinkäyttö dashboard.total_memory_allocated=Yhteensä muistia varattu dashboard.memory_obtained=Muistia saatu -dashboard.pointer_lookup_times=Pointteri lookup ajat -dashboard.current_heap_usage=Nykyinen heap käyttö -dashboard.heap_memory_obtained=Heap muistia saatu -dashboard.heap_memory_idle=Heap muistia tyhjäkäynnillä -dashboard.heap_memory_in_use=Heap muistia käytössä -dashboard.heap_memory_released=Heap muisti vapautettu -dashboard.heap_objects=Heap objekteja -dashboard.bootstrap_stack_usage=Bootstrap pinon käyttö -dashboard.stack_memory_obtained=Pino muistia saatu -dashboard.mspan_structures_usage=MSpan rakenteiden käyttö -dashboard.mspan_structures_obtained=MSpan rakenteita saatu -dashboard.mcache_structures_usage=MCache rakenteiden käyttö -dashboard.mcache_structures_obtained=MCache rakenteita saatu -dashboard.profiling_bucket_hash_table_obtained=Profilointi Bucket Hash Table saatu -dashboard.gc_metadata_obtained=GC metatietoja saatu -dashboard.other_system_allocation_obtained=Muita järjestestelmän varauksia saatu -dashboard.next_gc_recycle=Seuraava GC-kierrätys -dashboard.last_gc_time=Edellisen GC ajan jälkeen +dashboard.pointer_lookup_times=Osoittimen hakuajat +dashboard.current_heap_usage=Nykyinen heapin käyttö +dashboard.heap_memory_obtained=Heap-muisti saatu +dashboard.heap_memory_idle=Heap-muisti tyhjäkäynnillä +dashboard.heap_memory_in_use=Heap-muisti käytössä +dashboard.heap_memory_released=Heap-muisti vapautettu +dashboard.heap_objects=Heap-objektit +dashboard.bootstrap_stack_usage=Bootstrap-pinon käyttö +dashboard.stack_memory_obtained=Pinonmuisti saatu +dashboard.mspan_structures_usage=MSpan-rakenteiden käyttö +dashboard.mspan_structures_obtained=MSpan-rakenteet saatu +dashboard.mcache_structures_usage=MCache-rakenteiden käyttö +dashboard.mcache_structures_obtained=MCache-rakenteet saatu +dashboard.profiling_bucket_hash_table_obtained=Profilointiämpäritiivistetaulukko saatu +dashboard.gc_metadata_obtained=Roskienkeruumetatiedot saatu +dashboard.other_system_allocation_obtained=Muu järjestestelmän varaus saatu +dashboard.next_gc_recycle=Seuraava roskienkeruukierrätys +dashboard.last_gc_time=Aika edellisestä roskienkeruusta dashboard.total_gc_time=Yhteensä GC tauko -dashboard.total_gc_pause=Yhteensä GC tauko -dashboard.last_gc_pause=Viime GC tauko -dashboard.gc_times=GC aikoja +dashboard.total_gc_pause=Yhteensä roskienkeruutauko +dashboard.last_gc_pause=Viimeinen roskienkeruutauko +dashboard.gc_times=Roskienkeruuajat users.user_manage_panel=Käyttäjätilien hallinta users.new_account=Luo käyttäjätili @@ -2418,7 +2418,7 @@ users.update_profile_success=Käyttäjän tili on päivitetty. users.edit_account=Muokkaa käyttäjätiliä users.max_repo_creation_desc=(Aseta -1 käyttääksesi globaalia oletusrajaa.) users.is_activated=Aktivoitu tili -users.prohibit_login=Ota sisäänkirjautuminen pois käytöstä +users.prohibit_login=Jäädytetty tili users.is_admin=Ylläpitäjätili users.is_restricted=Rajoitettu tili users.allow_git_hook=Voi luoda Git-koukkuja @@ -2453,7 +2453,7 @@ orgs.teams=Tiimit orgs.members=Jäsenet orgs.new_orga=Uusi organisaatio -repos.repo_manage_panel=Repojen hallinta +repos.repo_manage_panel=Tietovarastojen hallinta repos.owner=Omistaja repos.name=Nimi repos.private=Yksityinen @@ -2483,19 +2483,19 @@ auths.domain=Verkkotunnus auths.host=Isäntä auths.port=Portti auths.bind_dn=Liitä DN -auths.bind_password=Liitä salasana -auths.user_base=Käyttäjä hakuperuste +auths.bind_password=Sido salasana +auths.user_base=Käyttäjähakukanta auths.user_dn=Käyttäjä DN auths.search_page_size=Sivukoko auths.filter=Käyttäjäsuodatin -auths.admin_filter=Ylläpitosuodatin +auths.admin_filter=Järjestelmänvalvojasuodatin auths.restricted_filter=Rajoitettu suodatin auths.smtp_auth=SMTP-todennustyyppi auths.smtphost=SMTP-isäntä auths.smtpport=SMTP-portti auths.allowed_domains=Sallitut verkkotunnukset auths.skip_tls_verify=Ohita TLS-vahvistus -auths.pam_service_name=PAM palvelun nimi +auths.pam_service_name=PAM-palvelun nimi auths.oauth2_tokenURL=Pääsymerkki URL auths.enable_auto_register=Ota käyttöön automaattinen rekisteröinti auths.tips=Vinkit @@ -2511,21 +2511,21 @@ auths.deletion_success=Todennuslähde on poistettu. config.server_config=Palvelimen asetukset config.app_name=Ilmentymän otsikko config.app_ver=Forgejo-versio -config.disable_router_log=Poista käytöstä reitittimen loki +config.disable_router_log=Poista reitittimen lokinkirjaaminen käytöstä config.run_mode=Suoritustila config.git_version=Git-versio -config.repo_root_path=Repon juuren polku -config.script_type=Komentosarjan tyyppi -config.reverse_auth_user=Käänteinen todennus käyttäjä +config.repo_root_path=Tietovaraston juuren polku +config.script_type=Komentosarjatyyppi +config.reverse_auth_user=Käänteinen välityspalvelin -todennuskäyttäjä config.ssh_config=SSH-asetukset config.ssh_enabled=Käytössä config.ssh_port=Portti config.ssh_listen_port=Kuuntele porttia config.ssh_root_path=Juuren polku -config.ssh_key_test_path=Polku jossa avaimet testataan +config.ssh_key_test_path=Avaimen testipolku config.ssh_keygen_path=Keygen ('ssh-keygen') polku -config.ssh_minimum_key_size_check=Avaimen vähimmäiskoko tarkistus +config.ssh_minimum_key_size_check=Avaimen vähimmäiskoon tarkistus config.ssh_minimum_key_sizes=Avaimen vähimmäiskoot config.lfs_enabled=Käytössä @@ -2561,13 +2561,13 @@ config.oauth_enabled=Käytössä config.cache_config=Välimuistin asetukset config.cache_adapter=Välimuistin sovitin config.cache_interval=Välimuistin aikaväli -config.cache_conn=Välimuistin yhteys merkkijono +config.cache_conn=Välimuistiyhteys config.session_config=Istunnon asetukset config.session_provider=Istunnon toimittaja -config.provider_config=Toimittajan asetukset +config.provider_config=Toimittajan määritys config.cookie_name=Evästeen nimi -config.gc_interval_time=GC aikaväli aika +config.gc_interval_time=Roskienkeruuaikaväli config.session_life_time=Istunnon elinikä config.https_only=Vain HTTPS config.cookie_life_time=Evästeen elinikä @@ -2575,11 +2575,11 @@ config.cookie_life_time=Evästeen elinikä config.picture_service=Kuvapalvelu config.disable_gravatar=Poista käytöstä Gravatar -config.git_gc_args=Roskienkeruun parametrit -config.git_migrate_timeout=Migraatio aikakatkaistiin -config.git_mirror_timeout=Peilauspäivitys aikakatkaistiin -config.git_clone_timeout=Kloonaus aikakatkaistiin -config.git_gc_timeout=Roskienkeruu aikakatkaistiin +config.git_gc_args=Roskienkeruu-argumentit +config.git_migrate_timeout=Siirron aikakatkaisu +config.git_mirror_timeout=Peilin päivityksen aikakatkaisu +config.git_clone_timeout=Kloonitoiminnon aikakatkaisu +config.git_gc_timeout=Roskienkeruun aikakatkaisu config.log_config=Lokiasetukset config.disabled_logger=Pois käytöstä @@ -2678,7 +2678,7 @@ config.register_email_confirm = Vaadi sähköpostivahvistus rekisteröitymiseen config.ssh_domain = SSH-palvelimen verkkotunnus config.app_slogan = Ilmentymän tunnuslause config.lfs_content_path = LFS-sisällön polku -users.max_repo_creation = Repojen enimmäismäärä +users.max_repo_creation = Tietovarastojen enimmäismäärä defaulthooks.update_webhook = Päivitä oletusarvoinen webkoukku auths.auth_manage_panel = Todennuslähteiden hallinta config.custom_conf = Asetustiedoston polku @@ -2715,17 +2715,17 @@ repos.unadopted = Omaksumattomat tietovarastot [action] -create_repo=loi repon %s +create_repo=loi tietovaraston %s rename_repo=uudelleennimetty repo %[1]s nimelle %[3]s transfer_repo=siirretty repo %s kohteeseen %s push_tag=työnsi tagin %[3]s kohteeseen %[4]s delete_tag=poisti tagin %[2]s kohteesta %[3]s compare_commits_general=Vertaa committeja create_branch=loi haaran %[3]s repossa %[4]s -compare_commits = Vertaa %d kommittia +compare_commits = Vertaa %d sitoumukset compare_branch = Vertaa review_dismissed_reason = Syy: -commit_repo = työnsi haaraan %[3]s repossa %[4]s +commit_repo = työnsi haaraan %[3]s %[4]s:ssa create_issue = `avasi ongelman %[3]s#%[2]s` reopen_issue = `avasi uudelleen ongelman %[3]s#%[2]s` create_pull_request = `loi vetopyynnön %[3]s#%[2]s` @@ -2778,11 +2778,11 @@ subscriptions = Tilaukset [gpg] error.no_committer_account=Committaajan sähköpostiosoitteeseen ei ole linkitetty tiliä -error.not_signed_commit=Kommitti ei ole allekirjoitettu +error.not_signed_commit=Ei allekirjoitettu sitoumus error.extract_sign = Allekirjoituksen purkaminen epäonnistui default_key = Allekirjoitettu oletusavaimella -error.failed_retrieval_gpg_keys = Ei saatu yhtäkään kommitin tekijän tiliin liitettyä avainta -error.generate_hash = Tiivisteen luominen kommitista epäonnistui +error.failed_retrieval_gpg_keys = Sitoumuksen toimijan tiliin liitetyn avaimen nouto epäonnistui +error.generate_hash = Sitoumuksen tiivisteen luominen epäonnistui [units] unit = Yksikkö @@ -2806,7 +2806,7 @@ debian.install = Asenna paketti seuraavalla komennolla: owner.settings.cleanuprules.edit = Muokkaa siivoussääntöä arch.version.groups = Ryhmä details.project_site = Projektin verkkosivusto -details.repository_site = Repositorion verkkosivusto +details.repository_site = Tietovaraston verkkosivusto container.pull = Vedä levykuva komentoriviltä: generic.download = Lataa paketti komentoriviltä: dependency.version = Versio @@ -2827,7 +2827,7 @@ pypi.requires = Vaatii Pythonin alpine.install = Asenna paketti seuraavalla komennolla: debian.repository.components = Komponentit cran.install = Asenna paketti seuraavalla komennolla: -settings.link.select = Valitse repo +settings.link.select = Valitse tietovarasto owner.settings.chef.title = Chef-rekisteri owner.settings.cleanuprules.add = Lisää siivoussääntö versions = Versiot @@ -2856,7 +2856,7 @@ debian.registry = Määritä tämä rekisteri komentoriviltä: rpm.registry = Määritä rekisteri komentoriviltä: maven.install = Käytä pakettia sisällyttämällä seuraava dependencies-lohkoon pom.xml-tiedostossa: npm.registry = Määritä rekisteri projektin .npmrc-tiedostossa: -alpine.repository = Repositorion tiedot +alpine.repository = Tietovaraston tiedot cargo.registry = Määritä tämä rekisteri Cargon asetustiedostossa (esimerkiksi ~/.cargo/config.toml): cargo.install = Asenna paketti Cargolla suorittamalla seuraava komento: composer.install = Asenna paketti Composerilla suorittamalla seuraava komento: @@ -2883,8 +2883,8 @@ helm.registry = Määritä tämä rekisteri komentoriviltä: pub.install = Asenna paketti Dartilla suorittamalla seuraava komento: owner.settings.cargo.title = Cargon rekisteri-indeksi settings.delete.description = Paketin poistaminen on peruuttamaton toimenpide, sitä ei voi perua. -settings.link.success = Repositorion linkki päivitettiin onnistuneesti. -settings.link.button = Päivitä repositorion linkki +settings.link.success = Tietovaraston linkki päivitettiin onnistuneesti. +settings.link.button = Päivitä tietovaraston linkki owner.settings.cleanuprules.preview.overview = %d pakettia on ajastettu poistettavaksi. owner.settings.cargo.initialize.success = Cargo-indeksi luotiin onnistuneesti. vagrant.install = Lisää Vagrant-boksi suorittamalla seuraava komento: @@ -2894,10 +2894,10 @@ npm.dependencies.development = Kehitysriippuvuudet composer.dependencies.development = Kehitysriippuvuudet owner.settings.cleanuprules.success.update = Siivoussääntö on päivitetty. owner.settings.cleanuprules.success.delete = Siivoussääntö on poistettu. -settings.link = Linkitä tämä paketti repositorioon +settings.link = Linkitä tämä paketti tietovarastoon maven.download = Lataa riippuvuus suorittamalla komentorivillä: registry.documentation = Lisätietoja %s-rekisteristä on dokumentaatiossa. -owner.settings.chef.keypair.description = Avainpari vaaditaan Chef-rekisteriin tunnistautumista varten. Jos olet luonut avainparin aiemmin, uuden avainparin luominen hylkää aiemman avainparin. +owner.settings.chef.keypair.description = Chef-rekisteriin lähetettävät pyynnöt on allekirjoitettava salauskirjoituksella todennuskeinona. Avainparia luotaessa vain julkinen avain tallennetaan Forgejoon. Yksityinen avain toimitetaan sinulle käytettäväksi knifen kanssa. Uuden avainparin luominen korvaa edellisen. owner.settings.cleanuprules.keep.pattern = Säilytä kaavaa vastaavat versiot owner.settings.cleanuprules.pattern_full_match = Toteuta kaavio paketin koko nimeen owner.settings.cleanuprules.keep.title = Näitä sääntöjä vastaavat versiot säilytetään, vaikka ne vastaisivat alla olevaa poistosääntöä. @@ -2915,16 +2915,16 @@ owner.settings.cleanuprules.preview.none = Siivoussääntö ei vastaa yhtäkää arch.pacman.conf = Lisää palvelin asiaan liittyvällä jakelulla ja arkkitehtuurilla tiedostoon /etc/pacman.conf : published_by = Julkaistu %[1]s käyttäjän %[3]s toimesta alpine.registry.key = Lataa rekisterin julkinen RSA-avain hakemistoon /etc/apk/keys/ vahvistaaksesi indeksin allekirjoituksen: -alpine.registry = Määritä tämä rekisteri lisäämällä URL-osoite tiedostoon /etc/apk/repositories: +alpine.registry = Aseta tämä rekisteri lisäämällä URL-osoite tiedostoon /etc/apk/tietovarastot: rubygems.dependencies.runtime = Ajonaikaiset riippuvuudet owner.settings.cargo.rebuild.error = Cargo-indeksin rakentaminen uudelleen epäonnistui: %v owner.settings.cargo.rebuild = Rakenna indeksi uudelleen -empty.repo = Lähetitkö paketin, mutta se ei näy täällä? Siirry paketin asetuksiin ja linkitä se tähän repoon. +empty.repo = Latasitko ulospäin paketin, mutta se ei näy täällä? Siirry paketin asetuksiin ja linkitä se tähän tietovarastoon. alpine.registry.info = Valitse $branch ja $repository alla olevasta listasta. container.images.title = Levykuvat owner.settings.cargo.initialize = Alusta indeksi -owner.settings.cargo.initialize.description = Erityinen Git-repoindeksi vaaditaan Cargo-rekisterin käyttämiseksi. Tämän valinnan käyttäminen luo (tarvittaessa uudelleen) repon ja määrittää sen asetukset automaattisesti. -settings.link.error = Repositorion linkin päivittäminen epäonnistui. +owner.settings.cargo.initialize.description = Cargo-rekisterin käyttöä varten tarvitaan erityinen indeksi Git -tietovarasto. Tämän vaihtoehdon käyttäminen luo (uudelleen) tietovaraston ja määrittää sen automaattisesti. +settings.link.error = Tietovaraston linkin päivittäminen epäonnistui. alt.repository.multiple_groups = Tämä paketti on saatavilla useissa ryhmissä. alt.repository.architectures = Arkkitehtuurit alt.install = Asenna paketti @@ -2969,7 +2969,7 @@ runners.delete_runner_success = Testinajaja poistettu onnistuneesti runners.reset_registration_token = Uudelleenaseta rekisteröintiavain runs.scheduled = Ajastettu runs.status = Tila -runs.empty_commit_message = (tyhjä commit-viesti) +runs.empty_commit_message = (tyhjä sitoumusviesti) variables.deletion = Poista muuttuja runners.new_notice = Testinajajan aloitusohjeet workflow.dispatch.input_required = Arvo syötteelle "%s" vaadittu. @@ -3003,7 +3003,7 @@ runs.no_workflows.quick_start = Etkö tiedä kuinka Forgejo Actions toimii? Kats runners.new = Luo uusi testinajaja runners.version = Versio runs.expire_log_message = Lokitiedostot on tyhjätty vanhenemisen vuoksi. -runners.delete_runner_notice = Mikäli testinajajalla on keskeneräinen tehtävä, se pysäytetään ja merkitään epäonnistuneeksi. Tämä saattaa johtaa testinajoprosessin rikkoutumiseen. +runners.delete_runner_notice = Jos tehtävä on käynnissä tällä suorittajalla, se lopetetaan ja merkitään epäonnistuneeksi. Se voi rikkoa koonnin työnkulun. runners.update_runner_failed = Testinajajan päivitys epäonnistui variables.deletion.success = Muuttuja poistettu. variables.edit = Muokkaa muuttujaa diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 4a98ff3bd7..a7604220e0 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1602,7 +1602,7 @@ issues.filter_type.review_requested=Pieprasīta izskatīšana issues.filter_type.reviewed_by_you=Manis izskatītie issues.filter_sort=Kārtot issues.filter_sort.latest=Jaunākie -issues.filter_sort.oldest=Vecakie +issues.filter_sort.oldest=Vecākie issues.filter_sort.recentupdate=Nesen atjauninātie issues.filter_sort.leastupdate=Vissenāk atjauninātie issues.filter_sort.mostcomment=Visvairāk piebilžu @@ -3708,7 +3708,7 @@ empty.repo=Šeit netiek parādīta augšupielādēta pakotne? Jādodas uz dokumentācijā. filter.type=Veids filter.type.all=Visas -filter.no_result=Pēc norādītajiem kritērijiem nekas netika atrasts. +filter.no_result=Norādītajām atlasīšanas vērtībām nekas neatbilst. filter.container.tagged=Ar birku filter.container.untagged=Bez birkas published_by=Laida klajā %[3]s %[1]s @@ -3829,10 +3829,10 @@ owner.settings.cargo.rebuild.success=Cargo indekss tika sekmīgi pārbūvēts. owner.settings.cleanuprules.title=Notīrīšanas kārtulas owner.settings.cleanuprules.add=Pievienot notīrīšanas kārtulu owner.settings.cleanuprules.edit=Labot notīrīšanas kārtulu -owner.settings.cleanuprules.none=Vēl nav pieejama neviena tīrīšanas kārtula. -owner.settings.cleanuprules.preview=Attīrīšanas kārtulas priekšskatījums +owner.settings.cleanuprules.none=Vēl nav pieejama neviena notīrīšanas kārtula. +owner.settings.cleanuprules.preview=Notīrīšanas kārtulas priekšskatījums owner.settings.cleanuprules.preview.overview=Ir paredzēta %d pakotņu noņemšana. -owner.settings.cleanuprules.preview.none=Attīrīšanas kārtulai neatbilst neviena pakotne. +owner.settings.cleanuprules.preview.none=Notīrīšanas kārtulai neatbilst neviena pakotne. owner.settings.cleanuprules.enabled=Iespējots owner.settings.cleanuprules.pattern_full_match=Pielietot paraugu visam pakotnes nosaukumam owner.settings.cleanuprules.keep.title=Versijas, kas atbilst šīm kārtulām, tiks paturētas, pat ja tās atbildīs zemāk esošajai noņemšanas kārtulai. diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index 7754796558..608e05afa4 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -106,7 +106,7 @@ tracked_time_summary = Povzetek spremljanega časa na podlagi filtrov seznama za email = E-poštni naslov captcha = CAPTCHA sources = Viri -issues = Vprašanja +issues = Težave milestones = Mejniki ok = OK copy_branch = Kopiranje imena veje @@ -126,7 +126,7 @@ filter.not_mirror = Ni zrcaljen filter.public = Javni filter.private = Zasebno filter.not_archived = Ni arhivirano -pull_requests = Zahteve za umik +pull_requests = Zahteve za spajanje [install] reinstall_confirm_check_3 = Potrjujete, da ste popolnoma prepričani, da se ta program Forgejo izvaja s pravilno lokacijo app.ini, in da ste prepričani, da ga morate znova namestiti. Potrjujete, da se zavedate zgoraj navedenih tveganj. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 7cc293212b..42773de48d 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -3395,7 +3395,7 @@ alpine.repository.repositories=Репозиторії conan.details.repository=Репозиторій owner.settings.cleanuprules.enabled=Увімкнено about = Про цей пакунок -empty = Поки що тут немає пакунків. +empty = Пакунків поки що немає. empty.documentation = Докладніше про реєстр пакунків читайте в документації. registry.documentation = Докладніше про реєстр %s читайте в документації. settings.delete.notice = Ви збираєтеся видалити %s (%s). Цю операцію не можна скасувати, ви впевнені? diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index b71a900b72..a3ddf05ff9 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -18,7 +18,7 @@ template=模板 language=语言选项 notifications=通知 active_stopwatch=活跃时间跟踪器 -tracked_time_summary=基于工单列表过滤器的跟踪时间概要 +tracked_time_summary=议题列表过滤器的跟踪时间摘要 create_new=创建… user_profile_and_more=个人信息与设置… signed_in_as=已登录用户 @@ -77,7 +77,7 @@ forks=派生 activities=最近活动 pull_requests=合并请求 -issues=工单 +issues=议题 milestones=里程碑 ok=确定 @@ -112,7 +112,7 @@ preview=预览 loading=正在加载… error=错误 -error404=您尝试访问的页面不存在已被移除您无权查看。 +error404=您正在尝试访问的页面不存在已被移除您无权查看。 go_back=返回 never=从未 @@ -217,7 +217,7 @@ string.desc=Z - A [error] occurred=发生了一个错误 -report_message=如果您确定这是一个 Forgejo 的 bug,请在 Codeberg 上搜索相关问题或在必要时创建一个新工单。 +report_message=如果您确定这是一个 Forgejo 的 bug,请在 Codeberg 上搜索相关问题或在必要时创建一个新议题。 missing_csrf=错误的请求:没有 CSRF 令牌 invalid_csrf=错误的请求:无效的 CSRF 令牌 not_found=找不到目标。 @@ -518,7 +518,7 @@ reset_password.text=如果此请求是您本人作出的,则请在 %s register_success=注册成功 issue_assigned.pull=@%[1]s 已将仓库 %[3]s 中的合并请求 %[2]s 指派给您。 -issue_assigned.issue=@%[1]s 已将仓库 %[3]s 中的工单 %[2]s 指派给您。 +issue_assigned.issue=@%[1]s 已将仓库 %[3]s 中的议题 %[2]s 指派给您。 issue.x_mentioned_you=@%s 提到了您: issue.action.force_push=%[1]s 强制从 %[3]s 推送 %[2]s 至 [4]s。 @@ -772,7 +772,7 @@ cancel=取消操作 language=界面语言 ui=主题 hidden_comment_types=隐藏的注释类型 -hidden_comment_types_description=此处选中的注释类型不会显示在工单页面中。例如,勾选“标签”将移除所有“<用户>添加/删除了<标签>”注释。 +hidden_comment_types_description=此处选中的注释类型不会显示在议题页面中。例如,勾选“标签”将移除所有“<用户>添加/删除了<标签>”注释。 hidden_comment_types.ref_tooltip=注释此问题在何处被提及过,如另一个问题、代码提交等… hidden_comment_types.issue_ref_tooltip=注释用户在何处更改了与此问题相关联的分支/标签 comment_type_group_reference=引用 @@ -788,7 +788,7 @@ comment_type_group_lock=锁定状态 comment_type_group_review_request=评审请求 comment_type_group_pull_request_push=添加的提交 comment_type_group_project=项目 -comment_type_group_issue_ref=工单引用 +comment_type_group_issue_ref=议题引用 saved_successfully=您的设置已成功保存。 privacy=隐私设置 keep_activity_private=隐藏个人资料页面中的活动 @@ -1024,7 +1024,7 @@ repos_none=你并不拥有任何仓库。 delete_account=删除当前帐户 delete_prompt=此操作将永久删除您的用户帐户。它 不能 被撤消。 -delete_with_all_comments=你的帐户年龄小于 %s。为了避免幽灵评论,所有工单/合并请求的评论都将与它一起被删除。 +delete_with_all_comments=你的帐户年龄小于 %s。为了避免幽灵评论,所有议题/合并请求的评论都将与它一起被删除。 confirm_delete_account=确认删除帐户 delete_account_title=删除当前帐户 delete_account_desc=确实要永久删除此用户帐户吗? @@ -1078,7 +1078,7 @@ quota.sizes.git.all = Git 内容 quota.sizes.git.lfs = Git LFS quota.sizes.assets.all = 资源 quota.sizes.assets.attachments.all = 附件 -quota.sizes.assets.attachments.issues = 工单附件 +quota.sizes.assets.attachments.issues = 议题附件 quota.sizes.assets.attachments.releases = 版本发布附件 quota.sizes.assets.artifacts = 制品 quota.sizes.assets.packages.all = 软件包 @@ -1211,12 +1211,12 @@ template.git_hooks_tooltip=你目前无法修改或删除被添加过的 Git Hoo template.webhooks=Web 钩子 template.topics=主题 template.avatar=头像 -template.issue_labels=工单标签 +template.issue_labels=议题标签 template.one_item=必须至少选择一个模板项 template.invalid=必须选择一个模板仓库 -archive.title=此仓库已存档。您可以查看文件和克隆仓库,但无法对其状态进行任何更改,例如推送和创建新工单、合并请求或评论。 -archive.title_date=此仓库已于 %s 存档。您可以查看文件或克隆它,但无法对其状态进行任何更改,例如推送和创建新工单、合并请求或评论。 +archive.title=此仓库已存档。您可以查看文件和克隆仓库,但无法对其状态进行任何更改,例如推送和创建新议题、合并请求或评论。 +archive.title_date=此仓库已于 %s 存档。您可以查看文件或克隆它,但无法对其状态进行任何更改,例如推送和创建新议题、合并请求或评论。 archive.issue.nocomment=此仓库已存档,您不能在工单添加评论。 archive.pull.nocomment=此仓库已存档,您不能在合并请求添加评论。 @@ -1238,7 +1238,7 @@ migrate_items=迁移项目 migrate_items_wiki=百科 migrate_items_milestones=里程碑 migrate_items_labels=标签 -migrate_items_issues=工单 +migrate_items_issues=议题 migrate_items_pullrequests=合并请求 migrate_items_merge_requests=合并请求 migrate_items_releases=版本发布 @@ -1273,7 +1273,7 @@ migrate.migrating_topics=正在迁移主题 migrate.migrating_milestones=正在迁移里程碑 migrate.migrating_labels=正在迁移标签 migrate.migrating_releases=正在迁移发布 -migrate.migrating_issues=正在迁移工单 +migrate.migrating_issues=正在迁移议题 migrate.migrating_pulls=正在迁移合并请求 migrate.cancel_migrating_title=取消迁移 migrate.cancel_migrating_confirm=您想要取消此次迁移吗? @@ -1311,7 +1311,7 @@ filter_branch_and_tag=过滤分支或标签 find_tag=查找Git标签 branches=分支列表 tags=标签列表 -issues=工单 +issues=议题 pulls=合并请求 project_board=项目 packages=软件包 @@ -1470,11 +1470,11 @@ commitstatus.failure=失败 commitstatus.pending=待定 commitstatus.success=成功 -ext_issues=外部工单 +ext_issues=外部议题 ext_issues.desc=链接到外部工单跟踪系统。 projects=项目 -projects.desc=在项目看板中管理工单和合并请求。 +projects.desc=在项目看板中管理议题和合并请求。 projects.description=描述(可选) projects.description_placeholder=描述 projects.create=创建项目 @@ -1483,10 +1483,10 @@ projects.new=创建项目 projects.new_subheader=在一个地方协调、跟踪和更新您的工作,让项目保持透明并按计划进行。 projects.create_success=项目 %s 创建成功。 projects.deletion=删除项目 -projects.deletion_desc=删除项目会从所有相关的工单中移除它。是否继续? +projects.deletion_desc=删除项目会从所有相关的议题中移除它。是否继续? projects.deletion_success=该项目已被删除。 projects.edit=编辑项目 -projects.edit_subheader=项目用于组织工单和跟踪进展情况。 +projects.edit_subheader=项目用于组织议题和跟踪进展情况。 projects.modify=更新项目 projects.edit_success=项目 %s 更新成功。 projects.type.none=无 @@ -1501,11 +1501,11 @@ projects.column.new_title=名称 projects.column.new_submit=创建列 projects.column.new=创建列 projects.column.set_default=设为默认 -projects.column.set_default_desc=设置此列为未分类工单和合并请求的默认值 +projects.column.set_default_desc=设置此列为未分类议题和合并请求的默认值 projects.column.unset_default=取消设为默认 projects.column.unset_default_desc=取消此列为默认值 projects.column.delete=删除列 -projects.column.deletion_desc=删除项目列会将所有相关工单移到默认的列。是否继续? +projects.column.deletion_desc=删除项目列会将所有相关议题移到默认的列。是否继续? projects.column.color=颜色 projects.open=开放中 projects.close=关闭 @@ -1520,7 +1520,7 @@ issues.filter_milestones=筛选里程碑 issues.filter_projects=筛选项目 issues.filter_labels=筛选标签 issues.filter_reviewers=筛选评审员 -issues.new=创建工单 +issues.new=创建议题 issues.new.title_empty=标题不能为空 issues.new.labels=标签 issues.new.no_label=未选择标签 @@ -1543,12 +1543,12 @@ issues.new.no_reviewers=无评审员 issues.choose.get_started=开始 issues.choose.open_external_link=开启 issues.choose.blank=默认模板 -issues.choose.blank_about=从默认模板创建一个工单。 +issues.choose.blank_about=从默认模板创建一个议题。 issues.choose.ignore_invalid_templates=已忽略无效模板 issues.choose.invalid_templates=发现了 %v 个无效模板 issues.choose.invalid_config=问题配置包含错误: issues.no_ref=未指定分支或标签 -issues.create=创建工单 +issues.create=创建议题 issues.new_label=创建标签 issues.new_label_placeholder=标签名称 issues.new_label_desc_placeholder=描述 @@ -1598,7 +1598,7 @@ issues.filter_assginee_no_assignee=未指派 issues.filter_poster=作者 issues.filter_poster_no_select=所有作者 issues.filter_type=类型筛选 -issues.filter_type.all_issues=所有工单 +issues.filter_type.all_issues=所有议题 issues.filter_type.assigned_to_you=指派给您的 issues.filter_type.created_by_you=由您创建的 issues.filter_type.mentioning_you=提及您的 @@ -1644,7 +1644,7 @@ issues.commented_at=`评论于 %s` issues.delete_comment_confirm=您确定要删除该条评论吗? issues.context.copy_link=复制链接 issues.context.quote_reply=回复 -issues.context.reference_issue=在新工单中引用 +issues.context.reference_issue=在新议题中引用 issues.context.edit=编辑 issues.context.delete=删除 issues.no_content=没有提供说明。 @@ -1655,15 +1655,15 @@ issues.close_comment_issue=评论并关闭 issues.reopen_issue=重新开放 issues.reopen_comment_issue=重新打开评论 issues.create_comment=评论 -issues.closed_at=`于%[2]s关闭此工单` +issues.closed_at=`于%[2]s关闭此议题` issues.reopened_at=`重新打开此问题 %[2]s` -issues.commit_ref_at=`于%[2]s在代码提交中引用了该工单` -issues.ref_issue_from=`引用了工单 %[4]s %[2]s` +issues.commit_ref_at=`于%[2]s在代码提交中引用了该议题` +issues.ref_issue_from=`引用了议题 %[4]s %[2]s` issues.ref_pull_from=`引用了合并请求 %[4]s %[2]s` -issues.ref_closing_from=`于 %[2]s 从合并请求 %[4]s引用了此工单,将关闭此工单` -issues.ref_reopening_from=`于 %[2]s 引用了合并请求 %[4]s 将重新讨论此工单 ` -issues.ref_closed_from=`关闭了这个工单 %[4]s %[2]s` -issues.ref_reopened_from=`重新打开这个工单 %[4]s %[2]s` +issues.ref_closing_from=`于 %[2]s 从合并请求 %[4]s引用了此议题,将关闭此议题` +issues.ref_reopening_from=`于 %[2]s 引用了合并请求 %[4]s 将重新讨论此议题 ` +issues.ref_closed_from=`关闭了这个议题 %[4]s %[2]s` +issues.ref_reopened_from=`重新打开这个议题 %[4]s %[2]s` issues.ref_from=`来自 %[1]s` issues.author=作者 issues.author_helper=此用户是作者。 @@ -1695,9 +1695,9 @@ issues.label_archive=存档标签 issues.label_archived_filter=显示存档标签 issues.label_archive_tooltip=在标签搜索时,默认情况下存档标签将被排除在外。 issues.label_exclusive_desc=命名标签为 scope/item 以使其与其他以 scope/ 开头的标签互斥。 -issues.label_exclusive_warning=在编辑工单或合并请求的标签时,任何冲突的范围标签都将被删除。 +issues.label_exclusive_warning=在编辑议题或合并请求的标签时,任何冲突的范围标签都将被删除。 issues.label_count=%d 个标签 -issues.label_open_issues=%d 个开放中的工单 +issues.label_open_issues=%d 个开放中的议题 issues.label_edit=编辑 issues.label_delete=删除 issues.label_modify=编辑标签 @@ -1714,37 +1714,37 @@ issues.attachment.download=`点击下载 '%s'` issues.subscribe=订阅 issues.unsubscribe=取消订阅 issues.unpin_issue=取消置顶 -issues.max_pinned=您不能置顶更多工单 -issues.pin_comment=于 %s 置顶本工单 -issues.unpin_comment=于 %s 取消置顶本工单 +issues.max_pinned=您不能置顶更多议题 +issues.pin_comment=于 %s 置顶本议题 +issues.unpin_comment=于 %s 取消置顶本议题 issues.lock=锁定对话 issues.unlock=解锁对话 issues.lock.unknown_reason=由于未知原因无法锁定。 -issues.lock_duplicate=一个工单不能被锁定两次。 -issues.unlock_error=无法解锁一个未锁定的工单。 -issues.lock_with_reason=于 %[2]s 以 %[1]s 锁定本工单,并限制仅限协作者发言 +issues.lock_duplicate=一个议题不能被锁定两次。 +issues.unlock_error=无法解锁一个未锁定的议题。 +issues.lock_with_reason=于 %[2]s 以 %[1]s 锁定本议题,并限制仅限协作者发言 issues.lock_no_reason=于 %s 锁定本议题并限制仅协作者可发言 issues.unlock_comment=于 %s 解锁此议题 issues.lock_confirm=锁定 issues.unlock_confirm=解锁 -issues.lock.notice_1=- 其他用户不能评论此工单。 +issues.lock.notice_1=- 其他用户不能评论此议题。 issues.lock.notice_2=- 您和仓库其他协作者仍可评论并可见。 -issues.lock.notice_3=- 您可以在未来再次解锁这个工单。 -issues.unlock.notice_1=- 每个人都可以再次就这一工单发表评论。 -issues.unlock.notice_2=- 您可以在未来再次解锁这个工单。 +issues.lock.notice_3=- 您可以在未来再次解锁这个议题。 +issues.unlock.notice_1=- 每个人都可以再次就这一议题发表评论。 +issues.unlock.notice_2=- 您可以在未来再次解锁这个议题。 issues.lock.reason=锁定原因 issues.lock.title=锁定有关此问题的对话。 issues.unlock.title=解锁有关此问题的对话。 issues.comment_on_locked=您不能对锁定的问题发表评论。 issues.delete=删除 -issues.delete.title=是否删除工单? -issues.delete.text=您真的要删除这个工单吗?(该操作将会永久删除所有内容。如果您需要保留,请关闭它) +issues.delete.title=是否删除议题? +issues.delete.text=您真的要删除这个议题吗?(该操作将会永久删除所有内容。如果您需要保留,请关闭它) issues.tracker=时间跟踪 issues.start_tracking_short=启动计时器 issues.start_tracking=开始时间跟踪 issues.start_tracking_history=`开始工作 %s` -issues.tracker_auto_close=当此工单关闭时,自动停止计时器 -issues.tracking_already_started=`你已经开始对 另一个工单 进行时间跟踪!` +issues.tracker_auto_close=当此议题关闭时,自动停止计时器 +issues.tracking_already_started=`你已经开始对 另一个议题 进行时间跟踪!` issues.stop_tracking=停止计时器 issues.stop_tracking_history=`停止工作 %s` issues.cancel_tracking=放弃 @@ -1779,37 +1779,37 @@ issues.due_date_modified=于 %[3]s 将到期日从 %[2]s 修改为 %[1]s issues.due_date_remove=于 %[2]s 删除了到期时间 %[1]s issues.due_date_overdue=超期 issues.due_date_invalid=到期日期无效或超出范围。请使用“yyyy-mm-dd”格式。 -issues.dependency.title=依赖工单 +issues.dependency.title=依赖议题 issues.dependency.issue_no_dependencies=未设置依赖项。 issues.dependency.pr_no_dependencies=没有设置依赖项。 issues.dependency.no_permission_1=您没有读取 %d 依赖关系的权限 issues.dependency.no_permission_n=您没有读取 %d 依赖关系的权限 issues.dependency.no_permission.can_remove=您没有读取此依赖关系的权限,但可以删除此依赖关系 -issues.dependency.add=添加依赖工单… +issues.dependency.add=添加依赖议题… issues.dependency.cancel=取消 issues.dependency.remove=删除 issues.dependency.remove_info=删除此依赖项 issues.dependency.added_dependency=`添加了一个新的依赖项 %s` issues.dependency.removed_dependency=`移除了一个依赖项 %s` -issues.dependency.pr_closing_blockedby=以下工单阻止了关闭此合并请求 -issues.dependency.issue_closing_blockedby=关闭此工单被以下工单阻止 -issues.dependency.issue_close_blocks=此工单阻止了以下工单的关闭 -issues.dependency.pr_close_blocks=此合并请求阻止以下工单的关闭 -issues.dependency.issue_close_blocked=您需要关闭所有阻止此工单的工单, 然后才能关闭它。 -issues.dependency.issue_batch_close_blocked=无法批量关闭您所选择的工单,因为 #%d 工单仍然有处于打开状态的依赖工单 -issues.dependency.pr_close_blocked=您需要关闭所有阻止此合并请求的工单, 然后才能合并它。 +issues.dependency.pr_closing_blockedby=以下议题阻止了关闭此合并请求 +issues.dependency.issue_closing_blockedby=关闭此议题被以下议题阻止 +issues.dependency.issue_close_blocks=此议题阻止了以下议题的关闭 +issues.dependency.pr_close_blocks=此合并请求阻止以下议题的关闭 +issues.dependency.issue_close_blocked=您需要关闭所有阻止此议题的议题, 然后才能关闭它。 +issues.dependency.issue_batch_close_blocked=无法批量关闭您所选择的议题,因为 #%d 议题仍然有处于打开状态的依赖议题 +issues.dependency.pr_close_blocked=您需要关闭所有阻止此合并请求的议题, 然后才能合并它。 issues.dependency.blocks_short=阻止 issues.dependency.blocked_by_short=依赖于 issues.dependency.remove_header=删除依赖项 -issues.dependency.issue_remove_text=此操作将从工单中删除依赖。是否要继续? +issues.dependency.issue_remove_text=此操作将从议题中删除依赖。是否要继续? issues.dependency.pr_remove_text=此操作将从合并请求中删除依赖。是否要继续? -issues.dependency.setting=为工单和合并请求启用依赖 -issues.dependency.add_error_same_issue=你不能让一个工单依赖于自己。 +issues.dependency.setting=为议题和合并请求启用依赖 +issues.dependency.add_error_same_issue=你不能让一个议题依赖于自己。 issues.dependency.add_error_dep_issue_not_exist=依赖项不存在。 issues.dependency.add_error_dep_not_exist=依赖项不存在。 issues.dependency.add_error_dep_exists=依赖项已存在。 -issues.dependency.add_error_cannot_create_circular=您不能创建依赖, 使得两个工单相互阻止。 -issues.dependency.add_error_dep_not_same_repo=这两个工单必须在同一仓库。 +issues.dependency.add_error_cannot_create_circular=您不能创建依赖, 使得两个议题相互阻止。 +issues.dependency.add_error_dep_not_same_repo=这两个议题必须在同一仓库。 issues.review.self.approval=您不能批准您自己的合并请求。 issues.review.self.rejection=您不能请求对您自己的合并请求进行更改。 issues.review.approve=于 %s 批准此合并请求 @@ -2004,7 +2004,7 @@ milestones.update_ago=已更新 %s milestones.no_due_date=暂无截止日期 milestones.open=开放中 milestones.close=关闭 -milestones.new_subheader=里程碑可以帮助您组织工单并跟踪其进度。 +milestones.new_subheader=里程碑可以帮助您组织议题并跟踪其进度。 milestones.completeness=%d%% 已完成 milestones.create=创建里程碑 milestones.title=标题 @@ -2014,19 +2014,19 @@ milestones.clear=清除 milestones.invalid_due_date_format=到期时间的格式必须是“yyyy-mm-dd”。 milestones.create_success=里程碑 %s 创建成功。 milestones.edit=编辑里程碑 -milestones.edit_subheader=里程碑组织工单,合并请求和跟踪进度。 +milestones.edit_subheader=里程碑组织议题,合并请求和跟踪进度。 milestones.cancel=取消 milestones.modify=更新里程碑 milestones.edit_success=里程碑 %s 已经更新。 milestones.deletion=删除里程碑 -milestones.deletion_desc=删除该里程碑将会移除所有工单中相关的信息。是否继续? +milestones.deletion_desc=删除该里程碑将会移除所有议题中相关的信息。是否继续? milestones.deletion_success=里程碑已被删除。 milestones.filter_sort.earliest_due_data=到期日从远到近 milestones.filter_sort.latest_due_date=到期日从近到远 milestones.filter_sort.least_complete=完成度从低到高 milestones.filter_sort.most_complete=完成度从高到低 -milestones.filter_sort.most_issues=工单从多到少 -milestones.filter_sort.least_issues=工单从少到多 +milestones.filter_sort.most_issues=议题从多到少 +milestones.filter_sort.least_issues=议题从少到多 signing.will_sign=这个提交将用密钥 "%s" 签名。 signing.wont_sign.error=在检查提交是否可以被签名时出错。 @@ -2097,21 +2097,21 @@ activity.title.prs_merged_by=%[2]s 共合并了 %[1]s activity.title.prs_opened_by=%[2]s 共创建了 %[1]s activity.merged_prs_label=已合并 activity.opened_prs_label=已创建 -activity.active_issues_count_1=%d 项活动的工单 -activity.active_issues_count_n=%d 项活动的工单 -activity.closed_issues_count_1=已关闭的工单 -activity.closed_issues_count_n=已关闭的工单 -activity.title.issues_1=%d 项工单 -activity.title.issues_n=%d 项工单 +activity.active_issues_count_1=%d 项活动的议题 +activity.active_issues_count_n=%d 项活动的议题 +activity.closed_issues_count_1=已关闭的议题 +activity.closed_issues_count_n=已关闭的议题 +activity.title.issues_1=%d 项议题 +activity.title.issues_n=%d 项议题 activity.title.issues_closed_from=%[2]s 共关闭了 %[1]s activity.title.issues_created_by=%[2]s 共创建了 %[1]s activity.closed_issue_label=已关闭 -activity.new_issues_count_1=新工单 -activity.new_issues_count_n=新工单 +activity.new_issues_count_1=新议题 +activity.new_issues_count_n=新议题 activity.new_issue_label=打开的 activity.title.unresolved_conv_1=%d 项未解决的会话 activity.title.unresolved_conv_n=%d 项未解决的会话 -activity.unresolved_conv_desc=这些最近更新的工单和合并请求还没有解决。 +activity.unresolved_conv_desc=这些最近更新的议题和合并请求还没有解决。 activity.unresolved_conv_label=打开 activity.title.releases_1=%d 个版本发布 activity.title.releases_n=%d 个版本发布 @@ -2207,21 +2207,21 @@ settings.use_external_wiki=使用外部百科 settings.external_wiki_url=外部百科链接 settings.external_wiki_url_error=外部百科链接不是有效的 URL。 settings.external_wiki_url_desc=当点击百科标签时,访问者将被重定向到外部百科系统的URL。 -settings.issues_desc=启用仓库工单系统 -settings.use_internal_issue_tracker=使用内置的工单系统 -settings.use_external_issue_tracker=使用外部的工单系统 -settings.external_tracker_url=外部工单系统 URL +settings.issues_desc=启用仓库议题系统 +settings.use_internal_issue_tracker=使用内置的议题系统 +settings.use_external_issue_tracker=使用外部的议题系统 +settings.external_tracker_url=外部议题系统 URL settings.external_tracker_url_error=外部百科链接无效。 -settings.external_tracker_url_desc=当点击工单标签时,访问者将被重定向到外部工单系统的URL。 -settings.tracker_url_format=外部工单系统的 URL 格式 -settings.tracker_url_format_error=外部工单追踪器链接无效。 -settings.tracker_issue_style=外部工单系统的编号格式 +settings.external_tracker_url_desc=当点击议题标签时,访问者将被重定向到外部议题系统的URL。 +settings.tracker_url_format=外部议题系统的 URL 格式 +settings.tracker_url_format_error=外部议题追踪器链接无效。 +settings.tracker_issue_style=外部议题系统的编号格式 settings.tracker_issue_style.numeric=纯数字形式 settings.tracker_issue_style.alphanumeric=英文字母数字组合形式 settings.tracker_issue_style.regexp=正则表达式 settings.tracker_issue_style.regexp_pattern=正则表达式模式 settings.tracker_issue_style.regexp_pattern_desc=第一个被捕获的组将取代 {index}。 -settings.tracker_url_format_desc=使用占位符 {user}, {repo}{index} 作为用户名、仓库名和工单索引。 +settings.tracker_url_format_desc=使用占位符 {user}, {repo}{index} 作为用户名、仓库名和议题索引。 settings.enable_timetracker=启用时间跟踪 settings.allow_only_contributors_to_track_time=仅允许成员跟踪时间 settings.pulls_desc=启用合并请求 @@ -2242,7 +2242,7 @@ settings.admin_indexer_commit_sha=上次索引的提交 settings.admin_indexer_unindexed=未索引 settings.reindex_button=添加到重新索引队列 settings.reindex_requested=已请求重新索引 -settings.admin_enable_close_issues_via_commit_in_any_branch=通过在非默认分支中提交来关闭工单 +settings.admin_enable_close_issues_via_commit_in_any_branch=通过在非默认分支中提交来关闭议题 settings.danger_zone=危险操作区 settings.new_owner_has_same_repo=新的仓库拥有者已经存在同名仓库。请选择另一个名字。 settings.convert=转换为普通仓库 @@ -2292,7 +2292,7 @@ settings.wiki_deletion_success=已成功删除仓库百科数据。 settings.delete=删除本仓库 settings.delete_desc=删除仓库是永久性的, 无法撤消。 settings.delete_notices_1=- 此操作不可被回滚。 -settings.delete_notices_2=- 此操作将永久删除仓库 %s,包括 Git 数据、 工单、评论、百科和协作者的操作权限。 +settings.delete_notices_2=- 此操作将永久删除仓库 %s,包括 Git 数据、 议题、评论、百科和协作者的操作权限。 settings.delete_notices_fork_1=- 在此仓库删除后,它的派生仓库将变成独立仓库。 settings.deletion_success=仓库已被删除。 settings.update_settings_success=仓库设置已更新。 @@ -2370,17 +2370,17 @@ settings.event_push=推送 settings.event_push_desc=推送到 Git 仓库。 settings.event_repository=仓库 settings.event_repository_desc=创建或删除仓库。 -settings.event_header_issue=工单事件 +settings.event_header_issue=议题事件 settings.event_issues=修改 -settings.event_issues_desc=工单已打开、已关闭、已重新打开或已编辑。 +settings.event_issues_desc=议题已打开、已关闭、已重新打开或已编辑。 settings.event_issue_assign=指派 -settings.event_issue_assign_desc=工单已被指派或取消指派。 +settings.event_issue_assign_desc=议题已被指派或取消指派。 settings.event_issue_label=标签 -settings.event_issue_label_desc=工单标签被添加或移除。 +settings.event_issue_label_desc=议题标签被添加或移除。 settings.event_issue_milestone=里程碑 settings.event_issue_milestone_desc=里程碑被添加、移除或修改。 settings.event_issue_comment=评论 -settings.event_issue_comment_desc=工单评论被创建、编辑或删除。 +settings.event_issue_comment_desc=议题评论被创建、编辑或删除。 settings.event_header_pull_request=合并请求事件 settings.event_pull_request=修改 settings.event_pull_request_desc=合并请求被打开、被关闭、被重新打开或被编辑。 @@ -2543,7 +2543,7 @@ settings.matrix.room_id=房间ID settings.matrix.message_type=消息类型 settings.archive.button=存档仓库 settings.archive.header=存档此仓库 -settings.archive.text=存档仓库将使其完全只读。它将在首页隐藏。没有人(甚至包括你!)能够进行新的提交,或打开工单及合并请求。 +settings.archive.text=存档仓库将使其完全只读。它将在首页隐藏。没有人(甚至包括你!)能够进行新的提交,或打开议题及合并请求。 settings.archive.success=仓库已成功存档。 settings.archive.error=仓库在存档时出现异常。请通过日志获取详细信息。 settings.archive.error_ismirror=不能存档镜像仓库。 @@ -2552,7 +2552,7 @@ settings.archive.tagsettings_unavailable=标签设置对已存档的仓库不可 settings.archive.mirrors_unavailable = 镜像对已存档的仓库不可用。 settings.unarchive.button=撤销仓库存档 settings.unarchive.header=撤销此仓库存档 -settings.unarchive.text=撤销存档将恢复仓库接收提交、推送,以及新工单和合并请求的能力。 +settings.unarchive.text=撤销存档将恢复仓库接收提交、推送,以及新议题和合并请求的能力。 settings.unarchive.success=仓库已成功撤销存档。 settings.unarchive.error=仓库在取消存档时出现异常。请通过日志获取详细信息。 settings.update_avatar_success=仓库头像已经更新。 @@ -2754,7 +2754,7 @@ rss.must_be_on_branch = 您必须处于一个分支上才能拥有一个RSS订 admin.manage_flags = 管理标志 admin.failed_to_replace_flags = 替换仓库标志失败 clone_in_vscodium = 在 VSCodium 中克隆 -issues.blocked_by_user = 由于你已被仓库所有者屏蔽,你无法在此仓库创建工单。 +issues.blocked_by_user = 由于你已被仓库所有者屏蔽,你无法在此仓库创建议题。 issues.comment.blocked_by_user = 因为你已被仓库所有者或工单作者屏蔽,你无法对此工单进行评论。 settings.wiki_rename_branch_main_desc = 将百科内部使用的分支重命名为“%s”。此更改是永久性的且不可撤销。 editor.invalid_commit_mail = 用于创建提交的邮件地址无效。 @@ -2834,16 +2834,16 @@ form.string_too_long = 给定的字符串长度超过 %d 个字符。 n_release_one = %s 版本发布 n_release_few = %s 版本发布 project = 项目 -issues.edit.already_changed = 无法保存对工单的更改。工单似乎已经被另一个用户修改了,为了防止修改被覆盖,请刷新页面后再次尝试编辑 +issues.edit.already_changed = 无法保存对议题的更改。议题似乎已经被另一个用户修改了,为了防止修改被覆盖,请刷新页面后再次尝试编辑 pulls.edit.already_changed = 无法保存对合并请求的更改。内容似乎已经被另一个用户修改了,为了防止修改被覆盖,请刷新页面后再次尝试编辑 comments.edit.already_changed = 无法保存对评论的更改。内容似乎已经被另一个用户修改了,为了防止修改被覆盖,请刷新页面后再次尝试编辑 -subscribe.issue.guest.tooltip = 登录以订阅工单。 +subscribe.issue.guest.tooltip = 登录以订阅议题。 subscribe.pull.guest.tooltip = 登录以订阅此合并请求。 settings.federation_following_repos = 关注的仓库URL地址,多个地址以 “;” 分隔,不需要前后空格。 settings.federation_settings = 邦联设置 settings.federation_apapiurl = 此仓库的邦联URL地址。将其作为关注的仓库URL地址填写到另一个仓库的邦联设置中。 settings.federation_not_enabled = 当前实例未启用邦联功能。 -issues.author.tooltip.issue = 此用户是本工单的作者。 +issues.author.tooltip.issue = 此用户是本议题的作者。 issues.author.tooltip.pr = 此用户是此合并请求的作者。 release.type_attachment = 附件 release.type_external_asset = 外部资源 @@ -2883,7 +2883,7 @@ diff.git-notes.remove-header = 移除注释 diff.git-notes.remove-body = 此注释将被移除。 issues.num_reviews_one = %d 评审 issues.num_reviews_few = %d 评审 -issues.summary_card_alt = 仓库 %[2]s 中标题为 %[1]s 的工单的摘要卡片 +issues.summary_card_alt = 仓库 %[2]s 中标题为 %[1]s 的议题的摘要卡片 editor.add_tmpl.filename = 文件名 settings.default_update_style_desc = 用于更新落后于基础分支的合并请求的默认更新样式。 pulls.sign_in_require = 登录以创建新的合并请求。 @@ -2905,7 +2905,7 @@ archive.pull.noreview = 此仓库已存档,您无法评审合并请求。 commits.view_single_diff = 查看该提交对本文件的更改 pulls.editable = 可编辑 pulls.editable_explanation = 此合并请求允许维护者进行编辑。你可以直接向其贡献。 -issues.reopen.blocked_by_user = 由于你已被仓库所有者或工单作者屏蔽,你不能重新打开此工单。 +issues.reopen.blocked_by_user = 由于你已被仓库所有者或议题作者屏蔽,你不能重新打开此议题。 pulls.comment.blocked_by_user = 由于你已被仓库所有者或合并请求作者屏蔽,你不能在此合并请求发表评论。 issues.filter_no_results = 无结果 issues.filter_no_results_placeholder = 尝试调整搜索筛选条件。 @@ -2980,7 +2980,7 @@ settings.delete_org_title=删除组织 settings.delete_org_desc=此组织将会被永久删除,确认继续吗? settings.hooks_desc=在此处添加的 Web 钩子将会应用到该组织下的 所有仓库。 -settings.labels_desc=添加能够被该组织下的 所有仓库 的工单使用的标签。 +settings.labels_desc=添加能够被该组织下的 所有仓库 的议题使用的标签。 members.membership_visibility=成员可见性: members.public=可见 @@ -3157,7 +3157,7 @@ dashboard.cancel_abandoned_jobs=取消放弃的操作任务 dashboard.start_schedule_tasks=开始安排操作任务 dashboard.sync_branch.started=分支同步已开始 dashboard.sync_tag.started = 标签同步已开始 -dashboard.rebuild_issue_indexer=重建工单索引 +dashboard.rebuild_issue_indexer=重建议题索引 users.user_manage_panel=管理用户帐户 users.new_account=创建新帐户 @@ -3199,7 +3199,7 @@ users.cannot_delete_self=你不能删除自己 users.still_own_repo=此用户仍然拥有一个或多个仓库。必须首先删除或转让这些仓库。 users.still_has_org=此用户是组织的成员。必须先从组织中删除用户。 users.purge=清理用户 -users.purge_help=强制删除用户和用户拥有的任何仓库、组织和软件包。所有评论和工单也将被删除。 +users.purge_help=强制删除用户和用户拥有的任何仓库、组织和软件包。所有评论和议题也将被删除。 users.still_own_packages=此用户仍然拥有一个或多个软件包,请先删除这些软件包。 users.deletion_success=用户帐户已被删除。 users.reset_2fa=重置两步验证 @@ -3245,7 +3245,7 @@ repos.private=私有 repos.watches=关注数 repos.stars=点赞数 repos.forks=派生数 -repos.issues=工单数 +repos.issues=议题数 repos.size=大小 repos.lfs_size=LFS 大小 @@ -3458,7 +3458,7 @@ config.allow_dots_in_usernames = 允许用户在用户名中使用英文句号 config.default_allow_only_contributors_to_track_time=仅允许成员跟踪时间 config.no_reply_address=隐藏电子邮件域名 config.default_visibility_organization=新组织的默认可见性 -config.default_enable_dependencies=默认情况下启用工单依赖 +config.default_enable_dependencies=默认情况下启用议题依赖 config.webhook_config=Web 钩子配置 config.queue_length=队列长度 @@ -3615,13 +3615,13 @@ monitor.duration = 时长(秒) create_repo=创建了仓库 %s rename_repo=重命名仓库 %[1]s%[3]s commit_repo=推送到了仓库 %[4]s%[3]s 分支 -create_issue=`创建了工单 %[3]s#%[2]s` -close_issue=`关闭了工单 %[3]s#%[2]s` -reopen_issue=`重新开放了工单 %[3]s#%[2]s` +create_issue=`创建了议题 %[3]s#%[2]s` +close_issue=`关闭了议题 %[3]s#%[2]s` +reopen_issue=`重新开放了议题 %[3]s#%[2]s` create_pull_request=`创建了合并请求 %[3]s#%[2]s` close_pull_request=`关闭了合并请求 %[3]s#%[2]s` reopen_pull_request=`重新开放了合并请求 %[3]s#%[2]s` -comment_issue=`评论了工单 %[3]s#%[2]s` +comment_issue=`评论了议题 %[3]s#%[2]s` comment_pull=`评论了合并请求 %[3]s#%[2]s` merge_pull_request=`合并了合并请求 %[3]s#%[2]s` auto_merge_pull_request=`自动合并了拉取请求 %[3]s#%[2]s` @@ -4038,7 +4038,7 @@ match = 匹配 match_tooltip = 仅包含与搜索词完全匹配的结果 fuzzy_tooltip = 包含与搜索词相近的结果 exact = 精确 -issue_kind = 搜索工单… +issue_kind = 搜索议题… pull_kind = 搜索合并请求… exact_tooltip = 仅包含与搜索词精确匹配的结果 milestone_kind = 搜索里程碑… @@ -4069,9 +4069,9 @@ test = 好的 code.write = 写入:推送到仓库,创建分支和标签。 code.read = 读取:访问并克隆仓库的代码。 actions.read = 读取:查看集成的 CI/CD 管道及其日志。 -issues.write = 写入:关闭工单并管理元数据,如标签、里程碑、指派成员、截止日期和依赖。 +issues.write = 写入:关闭议题并管理元数据,如标签、里程碑、指派成员、截止日期和依赖。 releases.write = 写入:发布、编辑和删除版本发布及其资源。 -issues.read = 读取:阅读并创建工单和评论。 +issues.read = 读取:阅读并创建议题和评论。 pulls.read = 读取:阅读并创建合并请求。 releases.read = 读取:查看并下载版本发布。 wiki.read = 读取:阅读集成的百科及其历史。 @@ -4080,7 +4080,7 @@ projects.read = 读取:访问仓库项目看板。 packages.read = 读取:查看并下载指派给仓库的软件包。 packages.write = 写入:发布并删除指派给仓库的软件包。 actions.write = 写入:手动触发、重启、取消或批准待处理的 CI/CD 管道。 -ext_issues = 访问外部工单系统的链接。权限由外部管理。 +ext_issues = 访问外部议题系统的链接。权限由外部管理。 ext_wiki = 访问外部百科的链接。权限由外部管理。 projects.write = 写入:创建项目和列并进行编辑。 pulls.write = 写入:关闭合并请求并管理元数据,如标签、里程碑、指派成员、截止日期和依赖。 diff --git a/options/locale_next/locale_es-ES.json b/options/locale_next/locale_es-ES.json index f6dd2edebf..37edef9211 100644 --- a/options/locale_next/locale_es-ES.json +++ b/options/locale_next/locale_es-ES.json @@ -15,5 +15,20 @@ "themes.names.forgejo-dark": "Forgejo oscuro", "themes.names.forgejo-light": "Forgejo claro", "home.welcome.no_activity": "Sin actividad", - "meta.last_line": "¡Gracias por traducir Forgejo! Esta línea no es vista por los usuarios pero sirve para otros propósitos en la gestión de la traducción. Puedes colocar un dato curioso en la traducción en lugar de traducirla." + "meta.last_line": "¡Gracias por traducir Forgejo! Esta línea no es vista por los usuarios pero sirve para otros propósitos en la gestión de la traducción. Puedes colocar un dato curioso en la traducción en lugar de traducirla.", + "relativetime.now": "ahora", + "relativetime.mins": { + "one": "hace %d minuto", + "many": "hace %d minutos", + "other": "hace %d minutos" + }, + "relativetime.hours": { + "one": "hace %d hora", + "many": "hace %d horas", + "other": "hace %d horas" + }, + "relativetime.future": "en el futuro", + "home.explore_repos": "Explorar repositorios", + "home.explore_users": "Explorar usuarios", + "home.explore_orgs": "Explorar organizaciones" } diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index 8575e6d55f..cff682b10a 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -9,5 +9,61 @@ "many": "souhaite fusionner %[1]d révision(s) depuis %[2]s vers %[3]s", "other": "" }, - "search.milestone_kind": "Recherche dans les jalons..." + "search.milestone_kind": "Recherche dans les jalons…", + "mail.actions.run_info_ref": "Branche : %[1]s (%[2]s)", + "discussion.locked": "Cette discussion a été bloqué. Les commentaires sont limités aux contributeurs.", + "relativetime.now": "maintenant", + "relativetime.future": "dans le future", + "relativetime.mins": { + "one": "il y a %d minute", + "many": "il y a %d minutes", + "other": "il y a %d minutes" + }, + "relativetime.hours": { + "one": "il y a %d heure", + "many": "il y a %d heures", + "other": "il y a %d heures" + }, + "relativetime.days": { + "one": "il y a %d jour", + "many": "il y a %d jours", + "other": "il y a %d jours" + }, + "relativetime.months": { + "one": "il y a %d mois", + "many": "il y a %d mois", + "other": "il y a %d mois" + }, + "relativetime.years": { + "one": "il y a %d an", + "many": "il y a %d ans", + "other": "il y a %d ans" + }, + "relativetime.1day": "hier", + "relativetime.2days": "il y a 2 jours", + "relativetime.1week": "la semaine dernière", + "relativetime.2weeks": "il y a 2 semaines", + "relativetime.2months": "il y a 2 mois", + "relativetime.1year": "l'année dernière", + "relativetime.2years": "il y a 2 ans", + "themes.names.forgejo-light": "Forgejo clair", + "themes.names.forgejo-dark": "Forgejo sombre", + "themes.names.forgejo-auto": "Forgejo (suivre le thème du système)", + "alert.asset_load_failed": "Échec du chargement des fichiers d'asset de {path}. Faites en sorte que les fichiers d'asset puissent être accessibles.", + "install.invalid_lfs_path": "Impossible de créer le root LFS au chemin spécifié : %[1]s", + "alert.range_error": " doit être un nombre entre %[1]s et %[2]s.", + "home.welcome.no_activity": "Pas d'activité", + "home.welcome.activity_hint": "Il n'y a rien dans votre fil d'actualité. Vos actions et activités de vos dépôts que vous suivez s'afficheront ici.", + "home.explore_repos": "Explorer les dépôts", + "home.explore_users": "Explorer les utilisateurs", + "home.explore_orgs": "Explorer les organisations", + "relativetime.weeks": { + "one": "il y a %d semaine", + "many": "il y a %d semaines", + "other": "il y a %d semaines" + }, + "error.not_found.title": "Page non trouvée", + "relativetime.1month": "le mois dernier", + "incorrect_root_url": "Cette instance Forgejo est configuré pour être servi sur \"%s\". Vous êtes actuellement en train de regarder Forgejo avec une URL différente, ce qui pourrait casser certaines parties de cette application. L'URL canonique est controllée par les administrateurs Forgejo grâce au paramètre ROOT_URL dans le app.ini.", + "meta.last_line": "Merci de traduire Forgejo ! Cette ligne n'est pas vue par les utilisateurs mais sert à d'autres fins dans la gestion de la traduction. Vous pouvez mettre une fun fact dans la traduction au lieu de la traduire. Miaou." } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index 697483f61d..86011f8ac9 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -23,7 +23,7 @@ "alert.asset_load_failed": "Не удалось получить ресурсы из {path}. Убедитесь, что файлы ресурсов доступны.", "install.invalid_lfs_path": "Не удалось расположить корень LFS по указанному пути: %[1]s", "alert.range_error": " - число должно быть в диапазоне от %[1]s-%[2]s.", - "meta.last_line": "Skip-CI это круто, но мне нужно запустить тесты для слияния переводов с основной веткой.", + "meta.last_line": "Triggering CI skip again.", "mail.actions.not_successful_run_subject": "Провал раб. потока %[1]s в репозитории %[2]s", "mail.actions.successful_run_after_failure_subject": "Возобновление раб. потока %[1]s в репозитории %[2]s", "mail.actions.run_info_ref": "Ветвь: %[1]s (%[2]s)", From 0b09ef8380837cb34f5bad7031466c2cad9f9ea4 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sat, 17 May 2025 15:49:57 +0500 Subject: [PATCH 03/94] fix(ui): fix force-push compare line layout --- web_src/css/repo.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 8b43e4bbf4..e2627fda8f 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -907,7 +907,7 @@ td .commit-summary { @media (min-width: 768px) { .repository.view.issue .comment-list .timeline-item .forced-push { display: grid; - grid-auto-flow: column; + grid-template-columns: 1fr auto; column-gap: 1rem; } } From dc56486b1f39846acf06422482880f2012d28c02 Mon Sep 17 00:00:00 2001 From: floss4good Date: Sun, 18 May 2025 08:05:16 +0000 Subject: [PATCH 04/94] feat!: Abusive content reporting (#6977) This implements milestones 1. and 4. from **Task F. Moderation features: Reporting** (part of [amendment of the workplan](https://codeberg.org/forgejo/sustainability/src/branch/main/2022-12-01-nlnet/2025-02-07-extended-workplan.md#task-f-moderation-features-reporting) for NLnet 2022-12-035): > 1. A reporting feature is implemented in the database. It ensures that content remains available for review, even if a user deletes it after a report was sent. > 4. Users can report the most relevant content types (at least: issue comments, repositories, users) ### See also: - forgejo/discussions#291 - forgejo/discussions#304 - forgejo/design#30 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6977 Reviewed-by: Gusted Reviewed-by: Otto Co-authored-by: floss4good Co-committed-by: floss4good --- custom/conf/app.example.ini | 9 + models/issues/comment.go | 20 +- models/issues/issue_update.go | 6 + models/issues/moderation.go | 106 +++++++++++ models/moderation/abuse_report.go | 177 ++++++++++++++++++ models/moderation/shadow_copy.go | 76 ++++++++ models/repo/moderation.go | 70 +++++++ models/user/moderation.go | 112 +++++++++++ models/user/user.go | 10 + modules/repository/create.go | 6 + modules/setting/moderation.go | 15 ++ modules/setting/setting.go | 1 + options/locale_next/locale_en-US.json | 18 ++ routers/web/admin/config.go | 2 + routers/web/moderation/report.go | 125 +++++++++++++ routers/web/repo/issue.go | 1 + routers/web/shared/user/header.go | 1 + routers/web/web.go | 6 + services/context/org.go | 4 +- services/context/repo.go | 1 + services/forms/report_abuse.go | 28 +++ services/issue/issue.go | 14 +- services/moderation/reporting.go | 129 +++++++++++++ services/repository/delete.go | 6 + services/user/delete.go | 6 + services/user/email.go | 6 + templates/admin/config.tmpl | 10 + templates/moderation/new_abuse_report.tmpl | 45 +++++ templates/org/header.tmpl | 8 + templates/repo/header.tmpl | 8 + .../repo/issue/view_content/context_menu.tmpl | 8 + templates/shared/user/profile_big_avatar.tmpl | 5 + web_src/css/form.css | 4 + web_src/css/user.css | 3 +- 34 files changed, 1040 insertions(+), 6 deletions(-) create mode 100644 models/issues/moderation.go create mode 100644 models/moderation/abuse_report.go create mode 100644 models/moderation/shadow_copy.go create mode 100644 models/repo/moderation.go create mode 100644 models/user/moderation.go create mode 100644 modules/setting/moderation.go create mode 100644 routers/web/moderation/report.go create mode 100644 services/forms/report_abuse.go create mode 100644 services/moderation/reporting.go create mode 100644 templates/moderation/new_abuse_report.tmpl diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index bdc9ba7b2e..41599d411e 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1572,6 +1572,15 @@ LEVEL = Info ;; - manage_gpg_keys: a user cannot configure gpg keys ;;EXTERNAL_USER_DISABLE_FEATURES = +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;[moderation] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; When true enables moderation capabilities; default is false. +;; If enabled it will be possible for users to report abusive content (new actions are added in the UI and /report_abuse route will be enabled) and a new Moderation section will be added to Admin settings where the reports can be reviewed. +;ENABLED = false + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[openid] diff --git a/models/issues/comment.go b/models/issues/comment.go index ce4cb6ea93..dc38f83f79 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -1,6 +1,6 @@ -// Copyright 2018 The Gitea Authors. -// Copyright 2016 The Gogs Authors. -// All rights reserved. +// Copyright 2016 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package issues @@ -324,6 +324,9 @@ type Comment struct { NewCommit string `xorm:"-"` CommitsNum int64 `xorm:"-"` IsForcePush bool `xorm:"-"` + + // If you add new fields that might be used to store abusive content (mainly string fields), + // please also add them in the CommentData struct and the corresponding constructor. } func init() { @@ -1149,6 +1152,11 @@ func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *us } defer committer.Close() + // If the comment was reported as abusive, a shadow copy should be created before first update. + if err := IfNeededCreateShadowCopyForComment(ctx, c); err != nil { + return err + } + if err := c.LoadIssue(ctx); err != nil { return err } @@ -1184,6 +1192,12 @@ func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *us // DeleteComment deletes the comment func DeleteComment(ctx context.Context, comment *Comment) error { e := db.GetEngine(ctx) + + // If the comment was reported as abusive, a shadow copy should be created before deletion. + if err := IfNeededCreateShadowCopyForComment(ctx, comment); err != nil { + return err + } + if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil { return err } diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 9d0bc84454..d15533390e 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -1,4 +1,5 @@ // Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package issues @@ -275,6 +276,11 @@ func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User } } + // If the issue was reported as abusive, a shadow copy should be created before first update. + if err := IfNeededCreateShadowCopyForIssue(ctx, issue); err != nil { + return err + } + issue.Content = content issue.ContentVersion = contentVersion + 1 diff --git a/models/issues/moderation.go b/models/issues/moderation.go new file mode 100644 index 0000000000..635d295db0 --- /dev/null +++ b/models/issues/moderation.go @@ -0,0 +1,106 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package issues + +import ( + "context" + + "forgejo.org/models/moderation" + "forgejo.org/modules/json" + "forgejo.org/modules/timeutil" +) + +// IssueData represents a trimmed down issue that is used for preserving +// only the fields needed for abusive content reports (mainly string fields). +type IssueData struct { + RepoID int64 + Index int64 + PosterID int64 + Title string + Content string + ContentVersion int + CreatedUnix timeutil.TimeStamp + UpdatedUnix timeutil.TimeStamp +} + +// newIssueData creates a trimmed down issue to be used just to create a JSON structure +// (keeping only the fields relevant for moderation purposes) +func newIssueData(issue *Issue) IssueData { + return IssueData{ + RepoID: issue.RepoID, + Index: issue.Index, + PosterID: issue.PosterID, + Content: issue.Content, + Title: issue.Title, + ContentVersion: issue.ContentVersion, + CreatedUnix: issue.CreatedUnix, + UpdatedUnix: issue.UpdatedUnix, + } +} + +// CommentData represents a trimmed down comment that is used for preserving +// only the fields needed for abusive content reports (mainly string fields). +type CommentData struct { + PosterID int64 + IssueID int64 + Content string + ContentVersion int + CreatedUnix timeutil.TimeStamp + UpdatedUnix timeutil.TimeStamp +} + +// newCommentData creates a trimmed down comment to be used just to create a JSON structure +// (keeping only the fields relevant for moderation purposes) +func newCommentData(comment *Comment) CommentData { + return CommentData{ + PosterID: comment.PosterID, + IssueID: comment.IssueID, + Content: comment.Content, + ContentVersion: comment.ContentVersion, + CreatedUnix: comment.CreatedUnix, + UpdatedUnix: comment.UpdatedUnix, + } +} + +// IfNeededCreateShadowCopyForIssue checks if for the given issue there are any reports of abusive content submitted +// and if found a shadow copy of relevant issue fields will be stored into DB and linked to the above report(s). +// This function should be called before a issue is deleted or updated. +func IfNeededCreateShadowCopyForIssue(ctx context.Context, issue *Issue) error { + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeIssue, issue.ID) + if err != nil { + return err + } + + if shadowCopyNeeded { + issueData := newIssueData(issue) + content, err := json.Marshal(issueData) + if err != nil { + return err + } + return moderation.CreateShadowCopyForIssue(ctx, issue.ID, string(content)) + } + + return nil +} + +// IfNeededCreateShadowCopyForComment checks if for the given comment there are any reports of abusive content submitted +// and if found a shadow copy of relevant comment fields will be stored into DB and linked to the above report(s). +// This function should be called before a comment is deleted or updated. +func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment) error { + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeComment, comment.ID) + if err != nil { + return err + } + + if shadowCopyNeeded { + commentData := newCommentData(comment) + content, err := json.Marshal(commentData) + if err != nil { + return err + } + return moderation.CreateShadowCopyForComment(ctx, comment.ID, string(content)) + } + + return nil +} diff --git a/models/moderation/abuse_report.go b/models/moderation/abuse_report.go new file mode 100644 index 0000000000..dadd61a95e --- /dev/null +++ b/models/moderation/abuse_report.go @@ -0,0 +1,177 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "context" + "database/sql" + "errors" + "slices" + + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" + + "xorm.io/builder" +) + +// ReportStatusType defines the statuses a report (of abusive content) can have. +type ReportStatusType int + +const ( + // ReportStatusTypeOpen represents the status of open reports that were not yet handled in any way. + ReportStatusTypeOpen ReportStatusType = iota + 1 // 1 + // ReportStatusTypeHandled represents the status of valid reports, that have been acted upon. + ReportStatusTypeHandled // 2 + // ReportStatusTypeIgnored represents the status of ignored reports, that were closed without any action. + ReportStatusTypeIgnored // 3 +) + +type ( + // AbuseCategoryType defines the categories in which a user can include the reported content. + AbuseCategoryType int + + // AbuseCategoryItem defines a pair of value and it's corresponding translation key + // (used to add options within the dropdown shown when new reports are submitted). + AbuseCategoryItem struct { + Value AbuseCategoryType + TranslationKey string + } +) + +const ( + AbuseCategoryTypeOther AbuseCategoryType = iota + 1 // 1 (Other violations of platform rules) + AbuseCategoryTypeSpam // 2 + AbuseCategoryTypeMalware // 3 + AbuseCategoryTypeIllegalContent // 4 +) + +// GetAbuseCategoriesList returns a list of pairs with the available abuse category types +// and their corresponding translation keys +func GetAbuseCategoriesList() []AbuseCategoryItem { + return []AbuseCategoryItem{ + {AbuseCategoryTypeSpam, "moderation.abuse_category.spam"}, + {AbuseCategoryTypeMalware, "moderation.abuse_category.malware"}, + {AbuseCategoryTypeIllegalContent, "moderation.abuse_category.illegal_content"}, + {AbuseCategoryTypeOther, "moderation.abuse_category.other_violations"}, + } +} + +// ReportedContentType defines the types of content that can be reported +// (i.e. user/organization profile, repository, issue/pull, comment). +type ReportedContentType int + +const ( + // ReportedContentTypeUser should be used when reporting abusive users or organizations. + ReportedContentTypeUser ReportedContentType = iota + 1 // 1 + + // ReportedContentTypeRepository should be used when reporting a repository with abusive content. + ReportedContentTypeRepository // 2 + + // ReportedContentTypeIssue should be used when reporting an issue or pull request with abusive content. + ReportedContentTypeIssue // 3 + + // ReportedContentTypeComment should be used when reporting a comment with abusive content. + ReportedContentTypeComment // 4 +) + +var allReportedContentTypes = []ReportedContentType{ + ReportedContentTypeUser, + ReportedContentTypeRepository, + ReportedContentTypeIssue, + ReportedContentTypeComment, +} + +func (t ReportedContentType) IsValid() bool { + return slices.Contains(allReportedContentTypes, t) +} + +// AbuseReport represents a report of abusive content. +type AbuseReport struct { + ID int64 `xorm:"pk autoincr"` + Status ReportStatusType `xorm:"INDEX NOT NULL DEFAULT 1"` + // The ID of the user who submitted the report. + ReporterID int64 `xorm:"NOT NULL"` + // Reported content type: user/organization profile, repository, issue/pull or comment. + ContentType ReportedContentType `xorm:"INDEX NOT NULL"` + // The ID of the reported item (based on ContentType: user, repository, issue or comment). + ContentID int64 `xorm:"NOT NULL"` + // The abuse category selected by the reporter. + Category AbuseCategoryType `xorm:"INDEX NOT NULL"` + // Remarks provided by the reporter. + Remarks string + // The ID of the corresponding shadow-copied content when exists; otherwise null. + ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"` + CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` +} + +var ErrSelfReporting = errors.New("reporting yourself is not allowed") + +func init() { + // RegisterModel will create the table if does not already exist + // or any missing columns if the table was previously created. + // It will not drop or rename existing columns (when struct has changed). + db.RegisterModel(new(AbuseReport)) +} + +// IsShadowCopyNeeded reports whether one or more reports were already submitted +// for contentType and contentID and not yet linked to a shadow copy (regardless their status). +func IsShadowCopyNeeded(ctx context.Context, contentType ReportedContentType, contentID int64) (bool, error) { + return db.GetEngine(ctx).Cols("id").Where(builder.IsNull{"shadow_copy_id"}).Exist( + &AbuseReport{ContentType: contentType, ContentID: contentID}, + ) +} + +// AlreadyReportedByAndOpen returns if doerID has already submitted a report for contentType and contentID that is still Open. +func AlreadyReportedByAndOpen(ctx context.Context, doerID int64, contentType ReportedContentType, contentID int64) bool { + reported, _ := db.GetEngine(ctx).Exist(&AbuseReport{ + Status: ReportStatusTypeOpen, + ReporterID: doerID, + ContentType: contentType, + ContentID: contentID, + }) + return reported +} + +// ReportAbuse creates a new abuse report in the DB with 'Open' status. +// If the reported content is the user profile of the reporter ErrSelfReporting is returned. +// If there is already an open report submitted by the same user for the same content, +// the request will be ignored without returning an error (and a warning will be logged). +func ReportAbuse(ctx context.Context, report *AbuseReport) error { + if report.ContentType == ReportedContentTypeUser && report.ReporterID == report.ContentID { + return ErrSelfReporting + } + + if AlreadyReportedByAndOpen(ctx, report.ReporterID, report.ContentType, report.ContentID) { + log.Warn("Seems that user %d wanted to report again the content with type %d and ID %d; this request will be ignored.", report.ReporterID, report.ContentType, report.ContentID) + return nil + } + + report.Status = ReportStatusTypeOpen + _, err := db.GetEngine(ctx).Insert(report) + + return err +} + +/* +// MarkAsHandled will change the status to 'Handled' for all reports linked to the same item (user, repository, issue or comment). +func MarkAsHandled(ctx context.Context, contentType ReportedContentType, contentID int64) error { + return updateStatus(ctx, contentType, contentID, ReportStatusTypeHandled) +} + +// MarkAsIgnored will change the status to 'Ignored' for all reports linked to the same item (user, repository, issue or comment). +func MarkAsIgnored(ctx context.Context, contentType ReportedContentType, contentID int64) error { + return updateStatus(ctx, contentType, contentID, ReportStatusTypeIgnored) +} + +// updateStatus will set the provided status for any reports linked to the item with the given type and ID. +func updateStatus(ctx context.Context, contentType ReportedContentType, contentID int64, status ReportStatusType) error { + _, err := db.GetEngine(ctx).Where(builder.Eq{ + "content_type": contentType, + "content_id": contentID, + }).Cols("status").Update(&AbuseReport{Status: status}) + + return err +} +*/ diff --git a/models/moderation/shadow_copy.go b/models/moderation/shadow_copy.go new file mode 100644 index 0000000000..cdd8f69c52 --- /dev/null +++ b/models/moderation/shadow_copy.go @@ -0,0 +1,76 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "context" + "database/sql" + "fmt" + + "forgejo.org/models/db" + "forgejo.org/modules/log" + "forgejo.org/modules/timeutil" + + "xorm.io/builder" +) + +type AbuseReportShadowCopy struct { + ID int64 `xorm:"pk autoincr"` + RawValue string `xorm:"NOT NULL"` + CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` +} + +// Returns the ID encapsulated in a sql.NullInt64 struct. +func (sc AbuseReportShadowCopy) NullableID() sql.NullInt64 { + return sql.NullInt64{Int64: sc.ID, Valid: sc.ID > 0} +} + +func init() { + // RegisterModel will create the table if does not already exist + // or any missing columns if the table was previously created. + // It will not drop or rename existing columns (when struct has changed). + db.RegisterModel(new(AbuseReportShadowCopy)) +} + +func CreateShadowCopyForUser(ctx context.Context, userID int64, content string) error { + return createShadowCopy(ctx, ReportedContentTypeUser, userID, content) +} + +func CreateShadowCopyForRepository(ctx context.Context, repoID int64, content string) error { + return createShadowCopy(ctx, ReportedContentTypeRepository, repoID, content) +} + +func CreateShadowCopyForIssue(ctx context.Context, issueID int64, content string) error { + return createShadowCopy(ctx, ReportedContentTypeIssue, issueID, content) +} + +func CreateShadowCopyForComment(ctx context.Context, commentID int64, content string) error { + return createShadowCopy(ctx, ReportedContentTypeComment, commentID, content) +} + +func createShadowCopy(ctx context.Context, contentType ReportedContentType, contentID int64, content string) error { + err := db.WithTx(ctx, func(ctx context.Context) error { + sess := db.GetEngine(ctx) + + shadowCopy := &AbuseReportShadowCopy{RawValue: content} + affected, err := sess.Insert(shadowCopy) + if err != nil { + return err + } else if affected == 0 { + log.Warn("Something went wrong while trying to create the shadow copy for reported content with type %d and ID %d.", contentType, contentID) + } + + _, err = sess.Where(builder.Eq{ + "content_type": contentType, + "content_id": contentID, + }).And(builder.IsNull{"shadow_copy_id"}).Update(&AbuseReport{ShadowCopyID: shadowCopy.NullableID()}) + if err != nil { + return fmt.Errorf("could not link the shadow copy (%d) to reported content with type %d and ID %d - %w", shadowCopy.ID, contentType, contentID, err) + } + + return nil + }) + + return err +} diff --git a/models/repo/moderation.go b/models/repo/moderation.go new file mode 100644 index 0000000000..d7b87dffa0 --- /dev/null +++ b/models/repo/moderation.go @@ -0,0 +1,70 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package repo + +import ( + "context" + + "forgejo.org/models/moderation" + "forgejo.org/modules/json" + "forgejo.org/modules/timeutil" +) + +// RepositoryData represents a trimmed down repository that is used for preserving +// only the fields needed for abusive content reports (mainly string fields). +type RepositoryData struct { + OwnerID int64 + OwnerName string + Name string + Description string + Website string + Topics []string + Avatar string + CreatedUnix timeutil.TimeStamp + UpdatedUnix timeutil.TimeStamp +} + +// newRepositoryData creates a trimmed down repository to be used just to create a JSON structure +// (keeping only the fields relevant for moderation purposes) +func newRepositoryData(repo *Repository) RepositoryData { + return RepositoryData{ + OwnerID: repo.OwnerID, + OwnerName: repo.OwnerName, + Name: repo.Name, + Description: repo.Description, + Website: repo.Website, + Topics: repo.Topics, + Avatar: repo.Avatar, + CreatedUnix: repo.CreatedUnix, + UpdatedUnix: repo.UpdatedUnix, + } +} + +// IfNeededCreateShadowCopyForRepository checks if for the given repository there are any reports of abusive content submitted +// and if found a shadow copy of relevant repository fields will be stored into DB and linked to the above report(s). +// This function should be called when a repository is deleted or updated. +func IfNeededCreateShadowCopyForRepository(ctx context.Context, repo *Repository, forUpdates bool) error { + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeRepository, repo.ID) + if err != nil { + return err + } + + if shadowCopyNeeded { + if forUpdates { + // get the unmodified repository fields + repo, err = GetRepositoryByID(ctx, repo.ID) + if err != nil { + return err + } + } + repoData := newRepositoryData(repo) + content, err := json.Marshal(repoData) + if err != nil { + return err + } + return moderation.CreateShadowCopyForRepository(ctx, repo.ID, string(content)) + } + + return nil +} diff --git a/models/user/moderation.go b/models/user/moderation.go new file mode 100644 index 0000000000..afda497f02 --- /dev/null +++ b/models/user/moderation.go @@ -0,0 +1,112 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package user + +import ( + "context" + "reflect" + "slices" + "sync" + + "forgejo.org/models/moderation" + "forgejo.org/modules/json" + "forgejo.org/modules/timeutil" + + "xorm.io/xorm/names" +) + +// UserData represents a trimmed down user that is used for preserving +// only the fields needed for abusive content reports (mainly string fields). +type UserData struct { //revive:disable-line:exported + Name string + FullName string + Email string + LoginName string + Location string + Website string + Pronouns string + Description string + CreatedUnix timeutil.TimeStamp + UpdatedUnix timeutil.TimeStamp + // This field was intentionally renamed so that is not the same with the one from User struct. + // If we keep it the same as in User, during login it might trigger the creation of a shadow copy. + // TODO: Should we decide that this field is not that relevant for abuse reporting purposes, better remove it. + LastLogin timeutil.TimeStamp `json:"LastLoginUnix"` + Avatar string + AvatarEmail string +} + +// newUserData creates a trimmed down user to be used just to create a JSON structure +// (keeping only the fields relevant for moderation purposes) +func newUserData(user *User) UserData { + return UserData{ + Name: user.Name, + FullName: user.FullName, + Email: user.Email, + LoginName: user.LoginName, + Location: user.Location, + Website: user.Website, + Pronouns: user.Pronouns, + Description: user.Description, + CreatedUnix: user.CreatedUnix, + UpdatedUnix: user.UpdatedUnix, + LastLogin: user.LastLoginUnix, + Avatar: user.Avatar, + AvatarEmail: user.AvatarEmail, + } +} + +// userDataColumnNames builds (only once) and returns a slice with the column names +// (e.g. FieldName -> field_name) corresponding to UserData struct fields. +var userDataColumnNames = sync.OnceValue(func() []string { + mapper := new(names.GonicMapper) + udType := reflect.TypeOf(UserData{}) + columnNames := make([]string, 0, udType.NumField()) + for i := 0; i < udType.NumField(); i++ { + columnNames = append(columnNames, mapper.Obj2Table(udType.Field(i).Name)) + } + return columnNames +}) + +// IfNeededCreateShadowCopyForUser checks if for the given user there are any reports of abusive content submitted +// and if found a shadow copy of relevant user fields will be stored into DB and linked to the above report(s). +// This function should be called before a user is deleted or updated. +// +// For deletions alteredCols argument must be omitted. +// +// In case of updates it will first checks whether any of the columns being updated (alteredCols argument) +// is relevant for moderation purposes (i.e. included in the UserData struct). +func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCols ...string) error { + // TODO: this can be triggered quite often (e.g. by routers/web/repo/middlewares.go SetDiffViewStyle()) + + shouldCheckIfNeeded := len(alteredCols) == 0 // no columns being updated, therefore a deletion + if !shouldCheckIfNeeded { + // for updates we need to go further only if certain column are being changed + for _, colName := range userDataColumnNames() { + if shouldCheckIfNeeded = slices.Contains(alteredCols, colName); shouldCheckIfNeeded { + break + } + } + } + + if !shouldCheckIfNeeded { + return nil + } + + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, user.ID) + if err != nil { + return err + } + + if shadowCopyNeeded { + userData := newUserData(user) + content, err := json.Marshal(userData) + if err != nil { + return err + } + return moderation.CreateShadowCopyForUser(ctx, user.ID, string(content)) + } + + return nil +} diff --git a/models/user/user.go b/models/user/user.go index 7544e24be6..d75fe56a20 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -153,6 +153,9 @@ type User struct { KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"` KeepPronounsPrivate bool `xorm:"NOT NULL DEFAULT false"` EnableRepoUnitHints bool `xorm:"NOT NULL DEFAULT true"` + + // If you add new fields that might be used to store abusive content (mainly string fields), + // please also add them in the UserData struct and the corresponding constructor. } func init() { @@ -610,6 +613,7 @@ var ( "pulls", "milestones", "notifications", + "report_abuse", "favicon.ico", "manifest.json", // web app manifests @@ -919,6 +923,12 @@ func UpdateUserCols(ctx context.Context, u *User, cols ...string) error { return err } + // If the user was reported as abusive and any of the columns being updated is relevant + // for moderation purposes a shadow copy should be created before first update. + if err := IfNeededCreateShadowCopyForUser(ctx, u, cols...); err != nil { + return err + } + _, err := db.GetEngine(ctx).ID(u.ID).Cols(cols...).Update(u) return err } diff --git a/modules/repository/create.go b/modules/repository/create.go index cace61341c..060b995bc5 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -1,4 +1,5 @@ // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package repository @@ -241,6 +242,11 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili e := db.GetEngine(ctx) + // If the repository was reported as abusive, a shadow copy should be created before first update. + if err := repo_model.IfNeededCreateShadowCopyForRepository(ctx, repo, true); err != nil { + return err + } + if _, err = e.ID(repo.ID).AllCols().Update(repo); err != nil { return fmt.Errorf("update: %w", err) } diff --git a/modules/setting/moderation.go b/modules/setting/moderation.go new file mode 100644 index 0000000000..5f35a284d6 --- /dev/null +++ b/modules/setting/moderation.go @@ -0,0 +1,15 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package setting + +// Moderation settings +var Moderation = struct { + Enabled bool `ini:"ENABLED"` +}{ + Enabled: false, +} + +func loadModerationFrom(rootCfg ConfigProvider) { + mustMapSetting(rootCfg, "moderation", &Moderation) +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 80db4dfa3c..75c24580b2 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -221,6 +221,7 @@ func LoadSettings() { loadProjectFrom(CfgProvider) loadMimeTypeMapFrom(CfgProvider) loadF3From(CfgProvider) + loadModerationFrom(CfgProvider) } // LoadSettingsForInstall initializes the settings for install diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index ffcd606128..ec5c313a90 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -55,6 +55,24 @@ "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", + "admin.config.moderation_config": "Moderation configuration", + "moderation.report_abuse": "Report abuse", + "moderation.report_content": "Report content", + "moderation.report_abuse_form.header": "Report abuse to administrator", + "moderation.report_abuse_form.details": "This form should be used to report users who create spam profiles, repositories, issues, comments or behave inappropriately.", + "moderation.report_abuse_form.invalid": "Invalid arguments", + "moderation.report_abuse_form.already_reported": "You've already reported this content", + "moderation.abuse_category": "Category", + "moderation.abuse_category.placeholder": "Select a category", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.illegal_content": "Illegal content", + "moderation.abuse_category.other_violations": "Other violations of platform rules", + "moderation.report_remarks": "Remarks", + "moderation.report_remarks.placeholder": "Please provide some details regarding the abuse you are reporting.", + "moderation.submit_report": "Submit report", + "moderation.reporting_failed": "Unable to submit the new abuse report: %v", + "moderation.reported_thank_you": "Thank you for your report. The administration has been made aware of it.", "mail.actions.successful_run_after_failure_subject": "Workflow %[1]s recovered in repository %[2]s", "mail.actions.not_successful_run_subject": "Workflow %[1]s failed in repository %[2]s", "mail.actions.successful_run_after_failure": "Workflow %[1]s recovered in repository %[2]s", diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go index f99a193960..dcc99ff1a8 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -1,5 +1,6 @@ // Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package admin @@ -145,6 +146,7 @@ func Config(ctx *context.Context) { ctx.Data["Service"] = setting.Service ctx.Data["DbCfg"] = setting.Database ctx.Data["Webhook"] = setting.Webhook + ctx.Data["Moderation"] = setting.Moderation ctx.Data["MailerEnabled"] = false if setting.MailService != nil { diff --git a/routers/web/moderation/report.go b/routers/web/moderation/report.go new file mode 100644 index 0000000000..39ca9e8824 --- /dev/null +++ b/routers/web/moderation/report.go @@ -0,0 +1,125 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "errors" + "net/http" + + "forgejo.org/models/moderation" + "forgejo.org/modules/base" + "forgejo.org/modules/log" + "forgejo.org/modules/web" + "forgejo.org/services/context" + "forgejo.org/services/forms" + moderation_service "forgejo.org/services/moderation" +) + +const ( + tplSubmitAbuseReport base.TplName = "moderation/new_abuse_report" +) + +// NewReport renders the page for new abuse reports. +func NewReport(ctx *context.Context) { + contentID := ctx.FormInt64("id") + if contentID <= 0 { + setMinimalContextData(ctx) + ctx.RenderWithErr(ctx.Tr("moderation.report_abuse_form.invalid"), tplSubmitAbuseReport, nil) + log.Warn("The content ID is expected to be an integer greater that 0; the provided value is %s.", ctx.FormString("id")) + return + } + + contentTypeString := ctx.FormString("type") + var contentType moderation.ReportedContentType + switch contentTypeString { + case "user", "org": + contentType = moderation.ReportedContentTypeUser + case "repo": + contentType = moderation.ReportedContentTypeRepository + case "issue", "pull": + contentType = moderation.ReportedContentTypeIssue + case "comment": + contentType = moderation.ReportedContentTypeComment + default: + setMinimalContextData(ctx) + ctx.RenderWithErr(ctx.Tr("moderation.report_abuse_form.invalid"), tplSubmitAbuseReport, nil) + log.Warn("The provided content type `%s` is not among the expected values.", contentTypeString) + return + } + + if moderation.AlreadyReportedByAndOpen(ctx, ctx.Doer.ID, contentType, contentID) { + setMinimalContextData(ctx) + ctx.RenderWithErr(ctx.Tr("moderation.report_abuse_form.already_reported"), tplSubmitAbuseReport, nil) + return + } + + setContextDataAndRender(ctx, contentType, contentID) +} + +// setMinimalContextData adds minimal values (Title and CancelLink) into context data. +func setMinimalContextData(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("moderation.report_abuse") + ctx.Data["CancelLink"] = ctx.Doer.DashboardLink() +} + +// setContextDataAndRender adds some values into context data and renders the new abuse report page. +func setContextDataAndRender(ctx *context.Context, contentType moderation.ReportedContentType, contentID int64) { + setMinimalContextData(ctx) + ctx.Data["ContentID"] = contentID + ctx.Data["ContentType"] = contentType + ctx.Data["AbuseCategories"] = moderation.GetAbuseCategoriesList() + ctx.HTML(http.StatusOK, tplSubmitAbuseReport) +} + +// CreatePost handles the POST for creating a new abuse report. +func CreatePost(ctx *context.Context) { + form := *web.GetForm(ctx).(*forms.ReportAbuseForm) + + if form.ContentID <= 0 || !form.ContentType.IsValid() { + setMinimalContextData(ctx) + ctx.RenderWithErr(ctx.Tr("moderation.report_abuse_form.invalid"), tplSubmitAbuseReport, nil) + return + } + + if ctx.HasError() { + setContextDataAndRender(ctx, form.ContentType, form.ContentID) + return + } + + can, err := moderation_service.CanReport(*ctx, ctx.Doer, form.ContentType, form.ContentID) + if err != nil { + if errors.Is(err, moderation_service.ErrContentDoesNotExist) || errors.Is(err, moderation_service.ErrDoerNotAllowed) { + ctx.Flash.Error(ctx.Tr("moderation.report_abuse_form.invalid")) + ctx.Redirect(ctx.Doer.DashboardLink()) + } else { + ctx.ServerError("Failed to check if user can report content", err) + } + return + } else if !can { + ctx.Flash.Error(ctx.Tr("moderation.report_abuse_form.invalid")) + ctx.Redirect(ctx.Doer.DashboardLink()) + return + } + + report := moderation.AbuseReport{ + ReporterID: ctx.Doer.ID, + ContentType: form.ContentType, + ContentID: form.ContentID, + Category: form.AbuseCategory, + Remarks: form.Remarks, + } + + if err := moderation.ReportAbuse(ctx, &report); err != nil { + if errors.Is(err, moderation.ErrSelfReporting) { + ctx.Flash.Error(ctx.Tr("moderation.reporting_failed", err)) + ctx.Redirect(ctx.Doer.DashboardLink()) + } else { + ctx.ServerError("Failed to save new abuse report", err) + } + return + } + + ctx.Flash.Success(ctx.Tr("moderation.reported_thank_you")) + ctx.Redirect(ctx.Doer.DashboardLink()) +} diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 2a8f267422..b97c268ae2 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1477,6 +1477,7 @@ func ViewIssue(ctx *context.Context) { ctx.Data["IssueType"] = "all" } + ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects) ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index c26ff19165..5873df8a24 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -38,6 +38,7 @@ func prepareContextForCommonProfile(ctx *context.Context) { func PrepareContextForProfileBigAvatar(ctx *context.Context) { prepareContextForCommonProfile(ctx) + ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlocked(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate diff --git a/routers/web/web.go b/routers/web/web.go index 3372a5bca2..f8a13dab7e 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -32,6 +32,7 @@ import ( "forgejo.org/routers/web/feed" "forgejo.org/routers/web/healthcheck" "forgejo.org/routers/web/misc" + "forgejo.org/routers/web/moderation" "forgejo.org/routers/web/org" org_setting "forgejo.org/routers/web/org/setting" "forgejo.org/routers/web/repo" @@ -474,6 +475,11 @@ func registerRoutes(m *web.Route) { m.Get("/search", repo.SearchIssues) }, reqSignIn) + if setting.Moderation.Enabled { + m.Get("/report_abuse", reqSignIn, moderation.NewReport) + m.Post("/report_abuse", reqSignIn, web.Bind(forms.ReportAbuseForm{}), moderation.CreatePost) + } + m.Get("/pulls", reqSignIn, user.Pulls) m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones) diff --git a/services/context/org.go b/services/context/org.go index 31ad60704f..3ddc40b6b3 100644 --- a/services/context/org.go +++ b/services/context/org.go @@ -1,5 +1,6 @@ // Copyright 2014 The Gogs Authors. All rights reserved. -// Copyright 2020 The Gitea Authors. +// Copyright 2020 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package context @@ -165,6 +166,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled ctx.Data["IsPublicMember"] = func(uid int64) bool { is, _ := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, uid) return is diff --git a/services/context/repo.go b/services/context/repo.go index 369a94c870..cce3a5fa70 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -593,6 +593,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues) ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests) ctx.Data["CanWriteActions"] = ctx.Repo.CanWrite(unit_model.TypeActions) + ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled canSignedUserFork, err := repo_module.CanUserForkRepo(ctx, ctx.Doer, ctx.Repo.Repository) if err != nil { diff --git a/services/forms/report_abuse.go b/services/forms/report_abuse.go new file mode 100644 index 0000000000..5e9d7dc45f --- /dev/null +++ b/services/forms/report_abuse.go @@ -0,0 +1,28 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forms + +import ( + "net/http" + + "forgejo.org/models/moderation" + "forgejo.org/modules/web/middleware" + "forgejo.org/services/context" + + "code.forgejo.org/go-chi/binding" +) + +// ReportAbuseForm is used to interact with the UI of the form that submits new abuse reports. +type ReportAbuseForm struct { + ContentID int64 + ContentType moderation.ReportedContentType + AbuseCategory moderation.AbuseCategoryType `binding:"Required" locale:"moderation.abuse_category"` + Remarks string `binding:"Required;MinSize(20);MaxSize(500)" preprocess:"TrimSpace" locale:"moderation.report_remarks"` +} + +// Validate validates the fields of ReportAbuseForm. +func (f *ReportAbuseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetValidateContext(req) + return middleware.Validate(errs, ctx.Data, f, ctx.Locale) +} diff --git a/services/issue/issue.go b/services/issue/issue.go index f6a3e90b10..1ec41476b4 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -1,4 +1,5 @@ // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package issue @@ -59,7 +60,6 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_mo // ChangeTitle changes the title of this issue, as the given user. func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, title string) error { oldTitle := issue.Title - issue.Title = title if oldTitle == title { return nil @@ -73,6 +73,12 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode return user_model.ErrBlockedByUser } + // If the issue was reported as abusive, a shadow copy should be created before first update. + if err := issues_model.IfNeededCreateShadowCopyForIssue(ctx, issue); err != nil { + return err + } + + issue.Title = title if err := issues_model.ChangeIssueTitle(ctx, issue, doer, oldTitle); err != nil { return err } @@ -252,6 +258,12 @@ func deleteIssue(ctx context.Context, issue *issues_model.Issue) error { defer committer.Close() e := db.GetEngine(ctx) + + // If the issue was reported as abusive, a shadow copy should be created before deletion. + if err := issues_model.IfNeededCreateShadowCopyForIssue(ctx, issue); err != nil { + return err + } + if _, err := e.ID(issue.ID).NoAutoCondition().Delete(issue); err != nil { return err } diff --git a/services/moderation/reporting.go b/services/moderation/reporting.go new file mode 100644 index 0000000000..e01156dc11 --- /dev/null +++ b/services/moderation/reporting.go @@ -0,0 +1,129 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package moderation + +import ( + "errors" + + "forgejo.org/models/issues" + "forgejo.org/models/moderation" + "forgejo.org/models/perm" + access_model "forgejo.org/models/perm/access" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unit" + "forgejo.org/models/user" + "forgejo.org/modules/log" + "forgejo.org/services/context" +) + +var ( + ErrContentDoesNotExist = errors.New("the content to be reported does not exist") + ErrDoerNotAllowed = errors.New("doer not allowed to access the content to be reported") +) + +// CanReport checks if doer has access to the content they are reporting +// (user, organization, repository, issue, pull request or comment). +// When reporting repositories the user should have at least read access to any repo unit type. +// When reporting issues, pull requests or comments the user should have at least read access +// to 'TypeIssues', respectively 'TypePullRequests' unit for the repository where the content belongs. +// When reporting users or organizations doer should be able to view the reported entity. +func CanReport(ctx context.Context, doer *user.User, contentType moderation.ReportedContentType, contentID int64) (bool, error) { + hasAccess := false + var issueID int64 + var repoID int64 + unitType := unit.TypeInvalid // used when checking access for issues, pull requests or comments + + if contentType == moderation.ReportedContentTypeUser { + reportedUser, err := user.GetUserByID(ctx, contentID) + if err != nil { + if user.IsErrUserNotExist(err) { + log.Warn("User #%d wanted to report user #%d but it does not exist.", doer.ID, contentID) + return false, ErrContentDoesNotExist + } + return false, err + } + + hasAccess = user.IsUserVisibleToViewer(ctx, reportedUser, ctx.Doer) + if !hasAccess { + log.Warn("User #%d wanted to report user/org #%d but they are not able to see that profile.", doer.ID, contentID) + return false, ErrDoerNotAllowed + } + } else { + // for comments and issues/pulls we need to get the parent repository + switch contentType { + case moderation.ReportedContentTypeComment: + comment, err := issues.GetCommentByID(ctx, contentID) + if err != nil { + if issues.IsErrCommentNotExist(err) { + log.Warn("User #%d wanted to report comment #%d but it does not exist.", doer.ID, contentID) + return false, ErrContentDoesNotExist + } + return false, err + } + if !comment.Type.HasContentSupport() { + // this is not a comment with text and/or attachments + log.Warn("User #%d wanted to report comment #%d but it is not a comment with content.", doer.ID, contentID) + return false, nil + } + issueID = comment.IssueID + case moderation.ReportedContentTypeIssue: + issueID = contentID + case moderation.ReportedContentTypeRepository: + repoID = contentID + } + + if issueID > 0 { + issue, err := issues.GetIssueByID(ctx, issueID) + if err != nil { + if issues.IsErrIssueNotExist(err) { + log.Warn("User #%d wanted to report issue #%d (or one of its comments) but it does not exist.", doer.ID, issueID) + return false, ErrContentDoesNotExist + } + return false, err + } + + repoID = issue.RepoID + if issue.IsPull { + unitType = unit.TypePullRequests + } else { + unitType = unit.TypeIssues + } + } + + if repoID > 0 { + repo, err := repo_model.GetRepositoryByID(ctx, repoID) + if err != nil { + if repo_model.IsErrRepoNotExist(err) { + log.Warn("User #%d wanted to report repository #%d (or one of its issues / comments) but it does not exist.", doer.ID, repoID) + return false, ErrContentDoesNotExist + } + return false, err + } + + if issueID > 0 { + // for comments and issues/pulls doer should have at least read access to the corresponding repo unit (issues, respectively pull requests) + hasAccess, err = access_model.HasAccessUnit(ctx, doer, repo, unitType, perm.AccessModeRead) + if err != nil { + return false, err + } else if !hasAccess { + log.Warn("User #%d wanted to report issue #%d or one of its comments from repository #%d but they don't have access to it.", doer.ID, issueID, repoID) + return false, ErrDoerNotAllowed + } + } else { + // for repositories doer should have at least read access to at least one repo unit + perm, err := access_model.GetUserRepoPermission(ctx, repo, doer) + if err != nil { + return false, err + } + hasAccess = perm.CanReadAny(unit.AllRepoUnitTypes...) + if !hasAccess { + log.Warn("User #%d wanted to report repository #%d but they don't have access to it.", doer.ID, repoID) + return false, ErrDoerNotAllowed + } + } + } + } + + return hasAccess, nil +} diff --git a/services/repository/delete.go b/services/repository/delete.go index 7c83ba12cd..f4124fb9e2 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -1,4 +1,5 @@ // Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package repository @@ -89,6 +90,11 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID } } + // If the repository was reported as abusive, a shadow copy should be created before deletion. + if err := repo_model.IfNeededCreateShadowCopyForRepository(ctx, repo, false); err != nil { + return err + } + if cnt, err := sess.ID(repoID).Delete(&repo_model.Repository{}); err != nil { return err } else if cnt != 1 { diff --git a/services/user/delete.go b/services/user/delete.go index 9ce917cd27..9caa24c373 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -1,4 +1,5 @@ // Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package user @@ -216,6 +217,11 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) } // ***** END: ExternalLoginUser ***** + // If the user was reported as abusive, a shadow copy should be created before deletion. + if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u); err != nil { + return err + } + if _, err = db.DeleteByID[user_model.User](ctx, u.ID); err != nil { return fmt.Errorf("delete: %w", err) } diff --git a/services/user/email.go b/services/user/email.go index f49efde1be..7a01fa77b3 100644 --- a/services/user/email.go +++ b/services/user/email.go @@ -1,4 +1,5 @@ // Copyright 2024 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package user @@ -203,6 +204,11 @@ func MakeEmailAddressPrimary(ctx context.Context, u *user_model.User, newPrimary oldPrimaryEmail := u.Email + // If the user was reported as abusive, a shadow copy should be created before first update (of certain columns). + if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u, "email"); err != nil { + return err + } + // 1. Update user table u.Email = newPrimaryEmail.Email if _, err = sess.ID(u.ID).Cols("email").Update(u); err != nil { diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 8f2b1c12e3..12504b8824 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -247,6 +247,16 @@ +

+ {{ctx.Locale.Tr "admin.config.moderation_config"}} +

+
+
+
{{ctx.Locale.Tr "enabled"}}
+
{{if .Moderation.Enabled}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
+
+
+

{{ctx.Locale.Tr "admin.config.cache_config"}}

diff --git a/templates/moderation/new_abuse_report.tmpl b/templates/moderation/new_abuse_report.tmpl new file mode 100644 index 0000000000..1964e0eac1 --- /dev/null +++ b/templates/moderation/new_abuse_report.tmpl @@ -0,0 +1,45 @@ +{{template "base/head" .}} +
+
+
+
+ {{.CsrfTokenHtml}} +

+ {{ctx.Locale.Tr "moderation.report_abuse_form.header"}} +

+
+ {{template "base/alert" .}} +

{{ctx.Locale.Tr "moderation.report_abuse_form.details"}}

+ + + + + + + {{ctx.Locale.Tr "moderation.abuse_category"}} + + + + {{ctx.Locale.Tr "moderation.report_remarks"}} + + + + +
+
+ {{ctx.Locale.Tr "cancel"}} + {{if .ContentID}} + + {{end}} +
+
+
+
+
+
+{{template "base/footer" .}} diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl index 4359b819a1..4097e34b24 100644 --- a/templates/org/header.tmpl +++ b/templates/org/header.tmpl @@ -21,6 +21,14 @@ {{if .IsOrganizationMember}} {{ctx.Locale.Tr "org.open_dashboard"}} {{end}} + {{if and .IsModerationEnabled .IsSigned (not .IsOrganizationOwner)}} + + {{end}} {{if .RenderedDescription}}
{{.RenderedDescription}}
{{end}} diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 5162fd429b..b6cbfc7283 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -67,6 +67,14 @@ {{if not $.DisableForks}} {{template "repo/header_fork" $}} {{end}} + {{if and $.IsModerationEnabled $.IsSigned (not $.IsRepositoryAdmin)}} + + {{end}} {{end}} diff --git a/templates/repo/issue/view_content/context_menu.tmpl b/templates/repo/issue/view_content/context_menu.tmpl index 5fc5af400f..6f43f768b3 100644 --- a/templates/repo/issue/view_content/context_menu.tmpl +++ b/templates/repo/issue/view_content/context_menu.tmpl @@ -23,5 +23,13 @@ {{end}} {{end}} {{end}} + {{if and .ctxData.IsModerationEnabled .ctxData.IsSigned (not .IsCommentPoster)}} + {{$contentType := "comment"}} + {{if eq .item .ctxData.Issue}} + {{if .ctxData.Issue.IsPull}} {{$contentType = "pull"}} {{else}} {{$contentType = "issue"}} {{end}} + {{end}} +
+ {{ctx.Locale.Tr "moderation.report_content"}} + {{end}} diff --git a/templates/shared/user/profile_big_avatar.tmpl b/templates/shared/user/profile_big_avatar.tmpl index e14bd56a70..81ac6b78ce 100644 --- a/templates/shared/user/profile_big_avatar.tmpl +++ b/templates/shared/user/profile_big_avatar.tmpl @@ -123,6 +123,11 @@ {{end}} + {{if .IsModerationEnabled}} +
  • + {{ctx.Locale.Tr "moderation.report_abuse"}} +
  • + {{end}} {{end}} diff --git a/web_src/css/form.css b/web_src/css/form.css index 916d76fd0c..3a53b6d8f2 100644 --- a/web_src/css/form.css +++ b/web_src/css/form.css @@ -491,6 +491,7 @@ input:-webkit-autofill:active, margin: 0; } +.moderation.new-report form, .repository.new.repo form, .repository.new.migrate form, .repository.new.fork form { @@ -504,11 +505,13 @@ input:-webkit-autofill:active, } @media (min-width: 768px) { + .moderation.new-report form, .repository.new.repo form, .repository.new.migrate form, .repository.new.fork form { width: 800px !important; } + .moderation.new-report form .header, .repository.new.repo form .header, .repository.new.migrate form .header, .repository.new.fork form .header { @@ -555,6 +558,7 @@ input:-webkit-autofill:active, margin-right: 0 !important; } +.moderation.new-report form .header, .repository.new.repo form .header, .repository.new.migrate form .header, .repository.new.fork form .header { diff --git a/web_src/css/user.css b/web_src/css/user.css index bceb16fcf9..b554f4e0b1 100644 --- a/web_src/css/user.css +++ b/web_src/css/user.css @@ -38,7 +38,8 @@ } .user.profile .ui.card .extra.content > ul > li.follow .ui.button, -.user.profile .ui.card .extra.content > ul > li.block .ui.button { +.user.profile .ui.card .extra.content > ul > li.block .ui.button, +.user.profile .ui.card .extra.content > ul > li.report .ui.button { align-items: center; display: flex; justify-content: center; From a775c855cbb6fab58107e644b30a762247e3e92e Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 18 May 2025 18:46:44 +0000 Subject: [PATCH 05/94] Update module github.com/yuin/goldmark to v1.7.12 (forgejo) (#7901) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7901 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index aea551d6e2..cdfc2f042d 100644 --- a/go.mod +++ b/go.mod @@ -96,7 +96,7 @@ require ( github.com/urfave/cli/v2 v2.27.6 github.com/valyala/fastjson v1.6.4 github.com/yohcop/openid-go v1.0.1 - github.com/yuin/goldmark v1.7.11 + github.com/yuin/goldmark v1.7.12 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc gitlab.com/gitlab-org/api/client-go v0.126.0 go.uber.org/mock v0.5.1 diff --git a/go.sum b/go.sum index 255835b68e..9e91974564 100644 --- a/go.sum +++ b/go.sum @@ -554,8 +554,8 @@ github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBz github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo= -github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY= +github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ= github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= From b6dcae9b50f194463579ebb14115f66d8ea92574 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 18 May 2025 18:48:40 +0000 Subject: [PATCH 06/94] Update module github.com/alecthomas/chroma/v2 to v2.18.0 (forgejo) (#7890) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7890 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cdfc2f042d..943e798209 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/ProtonMail/go-crypto v1.1.6 github.com/PuerkitoBio/goquery v1.10.3 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 - github.com/alecthomas/chroma/v2 v2.17.2 + github.com/alecthomas/chroma/v2 v2.18.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.5.1 github.com/buildkite/terminal-to-html/v3 v3.16.8 diff --git a/go.sum b/go.sum index 9e91974564..7f6f6ecb32 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2/go.mod h1:JitQWJ8JuV4Y github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= -github.com/alecthomas/chroma/v2 v2.17.2 h1:Rm81SCZ2mPoH+Q8ZCc/9YvzPUN/E7HgPiPJD8SLV6GI= -github.com/alecthomas/chroma/v2 v2.17.2/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= +github.com/alecthomas/chroma/v2 v2.18.0 h1:6h53Q4hW83SuF+jcsp7CVhLsMozzvQvO8HBbKQW+gn4= +github.com/alecthomas/chroma/v2 v2.18.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= From 75258cfa2a0cc6b931d12237da8f1c0973e4b4ac Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Mon, 19 May 2025 14:04:28 +0200 Subject: [PATCH 07/94] chore(ui): cleanup unused color CSS (#7898) Careful removal of unused classes and rules from editable CSS. One class was used once in the UI and could have been easily replaced, others were only used in a devtest page. Removed completely: * pink * olive * violet * background Removed partially: * blue Some of them are also present in generated Fomantic code, but it's not possible to remove them easily here. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7898 Reviewed-by: Otto Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- templates/devtest/gitea-ui.tmpl | 8 -- templates/repo/pulse.tmpl | 2 +- web_src/css/base.css | 52 ---------- web_src/css/modules/button.css | 164 -------------------------------- web_src/css/modules/label.css | 5 - 5 files changed, 1 insertion(+), 230 deletions(-) diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl index 5490f71784..1c88333520 100644 --- a/templates/devtest/gitea-ui.tmpl +++ b/templates/devtest/gitea-ui.tmpl @@ -40,8 +40,6 @@ - - @@ -52,16 +50,10 @@

    Do not use if there is no strong requirement. Do not use grey/black buttons, they don't work well with dark theme.

    - - - - - - diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl index 082fc8eb92..3fdecf5cc3 100644 --- a/templates/repo/pulse.tmpl +++ b/templates/repo/pulse.tmpl @@ -49,7 +49,7 @@ {{else}}
    - +
    {{end}} {{ctx.Locale.TrN .Activity.ActiveIssueCount "repo.activity.active_issues_count_1" "repo.activity.active_issues_count_n" .Activity.ActiveIssueCount}} diff --git a/web_src/css/base.css b/web_src/css/base.css index 64e42be553..b95cce3c3a 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -676,10 +676,6 @@ img.ui.avatar, color: var(--color-yellow) !important; } -.text.olive { - color: var(--color-olive) !important; -} - .text.green { color: var(--color-green) !important; } @@ -692,18 +688,10 @@ img.ui.avatar, color: var(--color-blue) !important; } -.text.violet { - color: var(--color-violet) !important; -} - .text.purple { color: var(--color-purple) !important; } -.text.pink { - color: var(--color-pink) !important; -} - .text.brown { color: var(--color-brown) !important; } @@ -853,46 +841,6 @@ img.ui.avatar, font-weight: var(--font-weight-normal); } -.ui .background.red { - background-color: var(--color-red) !important; -} - -.ui .background.blue { - background-color: var(--color-blue) !important; -} - -.ui .background.black { - background-color: var(--color-black) !important; -} - -.ui .background.grey { - background-color: var(--color-grey) !important; -} - -.ui .background.light.grey { - background-color: var(--color-grey) !important; -} - -.ui .background.green { - background-color: var(--color-green) !important; -} - -.ui .background.purple { - background-color: var(--color-purple) !important; -} - -.ui .background.yellow { - background-color: var(--color-yellow) !important; -} - -.ui .background.orange { - background-color: var(--color-orange) !important; -} - -.ui .background.gold { - background-color: var(--color-gold) !important; -} - .ui .migrate { color: var(--color-text-light-2) !important; } diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css index 0799ab80ec..469a309a11 100644 --- a/web_src/css/modules/button.css +++ b/web_src/css/modules/button.css @@ -349,47 +349,6 @@ It needs some tricks to tweak the left/right borders with active state */ border-color: var(--color-yellow-dark-2); } -/* olive */ - -.ui.olive.labels .label, -.ui.ui.ui.olive.label, -.ui.olive.button, -.ui.olive.buttons .button, -.ui.olive.button:focus, -.ui.olive.buttons .button:focus { - background: var(--color-olive); -} - -.ui.olive.button:hover, -.ui.olive.buttons .button:hover { - background: var(--color-olive-dark-1); -} - -.ui.olive.button:active, -.ui.olive.buttons .button:active { - background: var(--color-olive-dark-2); -} - -.ui.basic.olive.buttons .button, -.ui.basic.olive.button, -.ui.basic.olive.buttons .button:focus, -.ui.basic.olive.button:focus { - color: var(--color-olive); - border-color: var(--color-olive); -} - -.ui.basic.olive.buttons .button:hover, -.ui.basic.olive.button:hover { - color: var(--color-olive-dark-1); - border-color: var(--color-olive-dark-1); -} - -.ui.basic.olive.buttons .button:active, -.ui.basic.olive.button:active { - color: var(--color-olive-dark-2); - border-color: var(--color-olive-dark-2); -} - /* green */ .ui.green.labels .label, @@ -472,88 +431,6 @@ It needs some tricks to tweak the left/right borders with active state */ border-color: var(--color-teal-dark-2); } -/* blue */ - -.ui.blue.labels .label, -.ui.ui.ui.blue.label, -.ui.blue.button, -.ui.blue.buttons .button, -.ui.blue.button:focus, -.ui.blue.buttons .button:focus { - background: var(--color-blue); -} - -.ui.blue.button:hover, -.ui.blue.buttons .button:hover { - background: var(--color-blue-dark-1); -} - -.ui.blue.button:active, -.ui.blue.buttons .button:active { - background: var(--color-blue-dark-2); -} - -.ui.basic.blue.buttons .button, -.ui.basic.blue.button, -.ui.basic.blue.buttons .button:focus, -.ui.basic.blue.button:focus { - color: var(--color-blue); - border-color: var(--color-blue); -} - -.ui.basic.blue.buttons .button:hover, -.ui.basic.blue.button:hover { - color: var(--color-blue-dark-1); - border-color: var(--color-blue-dark-1); -} - -.ui.basic.blue.buttons .button:active, -.ui.basic.blue.button:active { - color: var(--color-blue-dark-2); - border-color: var(--color-blue-dark-2); -} - -/* violet */ - -.ui.violet.labels .label, -.ui.ui.ui.violet.label, -.ui.violet.button, -.ui.violet.buttons .button, -.ui.violet.button:focus, -.ui.violet.buttons .button:focus { - background: var(--color-violet); -} - -.ui.violet.button:hover, -.ui.violet.buttons .button:hover { - background: var(--color-violet-dark-1); -} - -.ui.violet.button:active, -.ui.violet.buttons .button:active { - background: var(--color-violet-dark-2); -} - -.ui.basic.violet.buttons .button, -.ui.basic.violet.button, -.ui.basic.violet.buttons .button:focus, -.ui.basic.violet.button:focus { - color: var(--color-violet); - border-color: var(--color-violet); -} - -.ui.basic.violet.buttons .button:hover, -.ui.basic.violet.button:hover { - color: var(--color-violet-dark-1); - border-color: var(--color-violet-dark-1); -} - -.ui.basic.violet.buttons .button:active, -.ui.basic.violet.button:active { - color: var(--color-violet-dark-2); - border-color: var(--color-violet-dark-2); -} - /* purple */ .ui.purple.labels .label, @@ -595,47 +472,6 @@ It needs some tricks to tweak the left/right borders with active state */ border-color: var(--color-purple-dark-2); } -/* pink */ - -.ui.pink.labels .label, -.ui.ui.ui.pink.label, -.ui.pink.button, -.ui.pink.buttons .button, -.ui.pink.button:focus, -.ui.pink.buttons .button:focus { - background: var(--color-pink); -} - -.ui.pink.button:hover, -.ui.pink.buttons .button:hover { - background: var(--color-pink-dark-1); -} - -.ui.pink.button:active, -.ui.pink.buttons .button:active { - background: var(--color-pink-dark-2); -} - -.ui.basic.pink.buttons .button, -.ui.basic.pink.button, -.ui.basic.pink.buttons .button:focus, -.ui.basic.pink.button:focus { - color: var(--color-pink); - border-color: var(--color-pink); -} - -.ui.basic.pink.buttons .button:hover, -.ui.basic.pink.button:hover { - color: var(--color-pink-dark-1); - border-color: var(--color-pink-dark-1); -} - -.ui.basic.pink.buttons .button:active, -.ui.basic.pink.button:active { - color: var(--color-pink-dark-2); - border-color: var(--color-pink-dark-2); -} - /* brown */ .ui.brown.labels .label, diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css index bc30baafac..4dc469631d 100644 --- a/web_src/css/modules/label.css +++ b/web_src/css/modules/label.css @@ -203,11 +203,6 @@ a.ui.ui.ui.basic.yellow.label:hover { border-color: var(--color-yellow-dark-1); color: var(--color-yellow-dark-1); } -.ui.ui.ui.olive.label { - background: var(--color-olive); - border-color: var(--color-olive); - color: var(--color-white); -} .ui.ui.ui.green.label { background: var(--color-green); From c0187aa789039cbbb3c4f2534d8cd0c790cce13c Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 20 May 2025 10:47:08 +0200 Subject: [PATCH 08/94] Migrate renovate config (#7877) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7877 Reviewed-by: Michael Kriese Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- renovate.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/renovate.json b/renovate.json index 3903e316db..5a059302dc 100644 --- a/renovate.json +++ b/renovate.json @@ -15,7 +15,7 @@ "prConcurrentLimit": 10, "osvVulnerabilityAlerts": true, "automergeStrategy": "squash", - "labels": ["dependency-upgrade","test/not-needed"], + "labels": ["dependency-upgrade", "test/not-needed"], "packageRules": [ { "description": "Require approval for python minor version", @@ -156,7 +156,7 @@ { "description": "Update deps inside Makefile", "customType": "regex", - "fileMatch": ["^Makefile$"], + "managerFilePatterns": ["Makefile"], "matchStrings": [ " \\?= (?.+?)@(?.+?) # renovate: datasource=(?.+?)(?: packageName=(?.+?))?( versioning=(?.+?))?\\s" ] From 43fee560115d2fb262cc0c8c3ca60a6a802d5c8d Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Tue, 20 May 2025 11:00:56 +0200 Subject: [PATCH 09/94] chore(renovate): disable indirect major updates for stable branches (#7914) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7914 Reviewed-by: Earl Warren Co-authored-by: Michael Kriese Co-committed-by: Michael Kriese --- renovate.json | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/renovate.json b/renovate.json index 5a059302dc..44a5032c17 100644 --- a/renovate.json +++ b/renovate.json @@ -145,21 +145,19 @@ "matchPackageNames": ["monaco-editor"], "minimumReleaseAge": "30 days" }, + { + "description": "Disable indirect major for stable branches", + "matchBaseBranches": ["/^v\\d+\\.\\d+\\/forgejo$/"], + "matchManagers": ["gomod"], + "matchUpdateTypes": ["major"], + "matchDepTypes": ["indirect"], + "enabled": false + }, { "description": "Require approval for stable branches (must be last rule to override all others)", "matchBaseBranches": ["/^v\\d+\\.\\d+\\/forgejo$/"], "dependencyDashboardApproval": true, "schedule": ["at any time"] } - ], - "customManagers": [ - { - "description": "Update deps inside Makefile", - "customType": "regex", - "managerFilePatterns": ["Makefile"], - "matchStrings": [ - " \\?= (?.+?)@(?.+?) # renovate: datasource=(?.+?)(?: packageName=(?.+?))?( versioning=(?.+?))?\\s" - ] - } ] } From a0f902f635a7a8d5474e4d0ff9eb97c5da476496 Mon Sep 17 00:00:00 2001 From: Ryan Lerch Date: Tue, 20 May 2025 16:37:15 +0200 Subject: [PATCH 10/94] fix(ui): make limits clearer in create repo form (#7402) Resolves: #7341 Previously, the Create Repository button was only enabled if a user was able to create a repo in their own namespace. However, if they had reached the global repo limit, but were stlll able to create a repo in an org, the button would still be disabled. In this pull request, the create repo form now: 1. Behaves like it always did previously if the user has not reached the repo limit. 2. If the User has reached the repo limit, and they are unable to create a repo in any of their orgs (or they have no orgs), the create repo form is displayed as: ![Screenshot](/attachments/9f22f43d-0036-4c48-b794-54302c0f241c) 3. If the User has reached the repo limit, and the **limit is greater than zero**, an alert appears at the top of the form, and they are only allowed to choose from the orgs that they are allowed to create repos in: ![Screenshot](/attachments/f5508e05-74fd-4858-9e95-967bd7017abd) 4. If the User has reached the repo limit, and the **limit is equal to zero**, no alert is displayed, as no user can create repos on that instance, and they are only allowed to choose from the orgs that they are allowed to create repos in: ![localhost_3000_repo_create (4).png](/attachments/e7a87da8-c19c-47e1-845e-2afc3667ab02) Co-authored-by: 0ko <0ko@noreply.codeberg.org> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7402 Reviewed-by: Gusted Co-authored-by: Ryan Lerch Co-committed-by: Ryan Lerch --- models/fixtures/user.yml | 2 +- options/locale_next/locale_en-US.json | 1 + templates/repo/create.tmpl | 70 ++++++++++++++----------- templates/repo/create_basic.tmpl | 28 ++++++---- tests/integration/repo_generate_test.go | 57 ++++++++++++++++++++ 5 files changed, 116 insertions(+), 42 deletions(-) diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 630505b8b4..52080b092f 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -93,7 +93,7 @@ login_name: org3 type: 1 salt: ZogKvWdyEx - max_repo_creation: -1 + max_repo_creation: 1000 is_active: false is_admin: false is_restricted: false diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index ec5c313a90..195f58af80 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -46,6 +46,7 @@ "one": "wants to merge %[1]d commit from %[2]s into %[3]s", "other": "wants to merge %[1]d commits from %[2]s into %[3]s" }, + "repo.form.cannot_create": "All spaces in which you can create repositories have reached the limit of repositories.", "search.milestone_kind": "Search milestones…", "incorrect_root_url": "This Forgejo instance is configured to be served on \"%s\". You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini.", "themes.names.forgejo-auto": "Forgejo (follow system theme)", diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 7ee8587435..7c07f80c86 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -8,42 +8,48 @@ {{ctx.Locale.Tr "new_repo.title"}}
    - {{template "base/alert" .}} - {{template "repo/create_helper" .}} + {{if or .CanCreateRepo .Orgs}} + {{template "base/alert" .}} + {{template "repo/create_helper" .}} - {{if not .CanCreateRepo}} + {{if and (not .CanCreateRepo) (ne .MaxCreationLimit 0)}} +
    +

    {{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimit}}

    +
    + {{end}} +
    + {{template "repo/create_basic" .}} +
    + +
    + + {{ctx.Locale.Tr "repo.new_from_template"}} + {{ctx.Locale.Tr "repo.new_from_template_description"}} + + {{template "repo/create_from_template" .}} +
    + +
    +
    + {{ctx.Locale.Tr "repo.auto_init"}} + {{template "repo/create_init" .}} +
    + +
    + {{ctx.Locale.Tr "repo.new_advanced"}} +
    {{ctx.Locale.Tr "repo.new_advanced_expand"}} + {{template "repo/create_advanced" .}} +
    +
    +
    + + {{else}}
    -

    {{ctx.Locale.TrN .MaxCreationLimit "repo.form.reach_limit_of_creation_1" "repo.form.reach_limit_of_creation_n" .MaxCreationLimit}}

    + {{ctx.Locale.Tr "repo.form.cannot_create"}}
    {{end}} -
    - {{template "repo/create_basic" .}} -
    - -
    - - {{ctx.Locale.Tr "repo.new_from_template"}} - {{ctx.Locale.Tr "repo.new_from_template_description"}} - - {{template "repo/create_from_template" .}} -
    - -
    -
    - {{ctx.Locale.Tr "repo.auto_init"}} - {{template "repo/create_init" .}} -
    - -
    - {{ctx.Locale.Tr "repo.new_advanced"}} -
    {{ctx.Locale.Tr "repo.new_advanced_expand"}} - {{template "repo/create_advanced" .}} -
    -
    -
    -
    diff --git a/templates/repo/create_basic.tmpl b/templates/repo/create_basic.tmpl index 0396629fef..90545c2769 100644 --- a/templates/repo/create_basic.tmpl +++ b/templates/repo/create_basic.tmpl @@ -2,17 +2,27 @@ {{ctx.Locale.Tr "repo.owner"}} {{end}}
    {{ctx.Locale.Tr "repo.diff.commit"}} - {{ShortSha .CommitID}} + + {{ShortSha .CommitID}} +
    diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl index 7249becbab..621fc44bf5 100644 --- a/templates/repo/commits_table.tmpl +++ b/templates/repo/commits_table.tmpl @@ -10,9 +10,13 @@ {{if .IsDiffCompare}} {{end}} diff --git a/templates/repo/editor/cherry_pick.tmpl b/templates/repo/editor/cherry_pick.tmpl index f9c9eef5aa..49b210f75c 100644 --- a/templates/repo/editor/cherry_pick.tmpl +++ b/templates/repo/editor/cherry_pick.tmpl @@ -11,7 +11,7 @@
    {{DateUtils.TimeSince .Delivered}} diff --git a/templates/repo/shabox.tmpl b/templates/repo/shabox.tmpl index a33256b2ed..270eab7759 100644 --- a/templates/repo/shabox.tmpl +++ b/templates/repo/shabox.tmpl @@ -16,6 +16,6 @@ {{ShortSha .sha1}} {{- if .signature -}} - {{template "repo/shabox_badge" dict "verification" .verification "svgSize" .svgSize}} + {{template "repo/shabox_badge" dict "verification" .verification}} {{- end -}} diff --git a/templates/repo/shabox_badge.tmpl b/templates/repo/shabox_badge.tmpl index 4650a9fcdd..ef3b567177 100644 --- a/templates/repo/shabox_badge.tmpl +++ b/templates/repo/shabox_badge.tmpl @@ -1,15 +1,15 @@ -
    +
    {{if .verification.Verified}} -
    - {{if ne .verification.SigningUser.ID 0}} - {{svg "gitea-lock" .svgSize}} - {{ctx.AvatarUtils.Avatar .verification.SigningUser 28 "signature"}} - {{else}} - {{svg "gitea-lock-cog" .svgSize}} - {{ctx.AvatarUtils.AvatarByEmail .verification.SigningEmail "" 28 "signature"}} - {{end}} -
    + + {{if ne .verification.SigningUser.ID 0}} + {{svg "gitea-lock"}} + {{ctx.AvatarUtils.Avatar .verification.SigningUser 28}} + {{else}} + {{svg "gitea-lock-cog"}} + {{ctx.AvatarUtils.AvatarByEmail .verification.SigningEmail "" 28}} + {{end}} + {{else}} - {{svg "gitea-unlock" .svgSize}} + {{svg "gitea-unlock"}} {{end}}
    diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index e93a0fbb48..d0ecb1fc36 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -96,7 +96,6 @@ "commitLink" (printf "%s/commit/%s" $repoLink .Sha1) "signature" .Signature "verification" .Verification - "svgSize" 13 )}} {{RenderCommitMessage $.Context .Message ($repo.ComposeMetas ctx)}} diff --git a/web_src/css/base.css b/web_src/css/base.css index b95cce3c3a..a962ea031a 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -809,19 +809,6 @@ img.ui.avatar, margin-left: 25px; } -.ui .sha.label { - font-family: var(--fonts-monospace); - font-size: 13px; - font-weight: var(--font-weight-normal); - margin: 0 6px; - padding: 5px 10px; - flex-shrink: 0; -} - -.ui .sha.label .shortsha { - display: inline-block; /* not sure whether it is still needed */ -} - .ui .button.truncate { display: inline-block; max-width: 100%; diff --git a/web_src/css/features/gitgraph.css b/web_src/css/features/gitgraph.css index 726ac7e9e2..9f44e884b7 100644 --- a/web_src/css/features/gitgraph.css +++ b/web_src/css/features/gitgraph.css @@ -125,14 +125,7 @@ } #git-graph-container #rev-list .sha.label { - padding-top: 5px; - padding-bottom: 3px; -} - -#git-graph-container #rev-list .sha.label .ui.detail.icon.button { - padding-top: 3px; - margin-top: -5px; - padding-bottom: 1px; + height: 23px; } #git-graph-container #rev-list .author img.ui.avatar { diff --git a/web_src/css/repo.css b/web_src/css/repo.css index e2627fda8f..35133b965f 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -865,43 +865,6 @@ td .commit-summary { .singular-commit .shabox .sha.label { margin: 0; - border: 1px solid var(--color-light-border); -} - -.singular-commit .shabox .sha.label.isSigned.isWarning { - border: 1px solid var(--color-red-badge); - background: var(--color-red-badge-bg); -} - -.singular-commit .shabox .sha.label.isSigned.isWarning:hover { - background: var(--color-red-badge-hover-bg) !important; -} - -.singular-commit .shabox .sha.label.isSigned.isVerified { - border: 1px solid var(--color-green-badge); - background: var(--color-green-badge-bg); -} - -.singular-commit .shabox .sha.label.isSigned.isVerified:hover { - background: var(--color-green-badge-hover-bg) !important; -} - -.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted { - border: 1px solid var(--color-yellow-badge); - background: var(--color-yellow-badge-bg); -} - -.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover { - background: var(--color-yellow-badge-hover-bg) !important; -} - -.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched { - border: 1px solid var(--color-orange-badge); - background: var(--color-orange-badge-bg); -} - -.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover { - background: var(--color-orange-badge-hover-bg) !important; } @media (min-width: 768px) { @@ -1272,189 +1235,100 @@ td .commit-summary { background-color: var(--color-light) !important; } -#activity-feed .sha.label, -.repository #commits-table td.sha .sha.label, -.repository #repo-files-table .sha.label, -.repository #repo-file-commit-box .sha.label, -.repository #rev-list .sha.label, -.repository .timeline-item.commits-list .singular-commit .sha.label { +.ui .sha.label { + font-family: var(--fonts-monospace); + font-size: 13px; + font-weight: var(--font-weight-normal); + margin: 0 6px; + padding: 0; + gap: 0; + flex-shrink: 0; +} + +.ui.ui .sha.label { border: 1px solid var(--color-light-border); } -#activity-feed .sha.label .ui.signature.avatar { - height: 13px; - margin-bottom: 0; - width: 13px; +.ui.primary.sha.label { + border: none !important; + background: var(--color-primary) !important; } -.repository #commits-table td.sha .sha.label .ui.signature.avatar, -.repository #repo-files-table .sha.label .ui.signature.avatar, -.repository #repo-file-commit-box .sha.label .ui.signature.avatar, -.repository #rev-list .sha.label .ui.signature.avatar, -.repository .timeline-item.commits-list .singular-commit .sha.label .ui.signature.avatar { +.sha.label .shortsha { + padding: 0.33rem 0.5rem; +} + +.sha.label .signature { + color: var(--color-text); + background: var(--color-light); + padding: 0.25rem 0.33rem; + border-left: 1px solid var(--color-light-border); +} + +.sha.label .signature-author { + display: flex; + gap: 0.25rem; +} + +.sha.label .signature-author .avatar { height: 16px; margin-bottom: 0; width: 16px; } -#activity-feed .sha.label .detail.icon, -.repository #commits-table td.sha .sha.label .detail.icon, -.repository #repo-files-table .sha.label .detail.icon, -.repository #repo-file-commit-box .sha.label .detail.icon, -.repository #rev-list .sha.label .detail.icon, -.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon { - background: var(--color-light); - margin: -6px -10px -4px 0; - padding: 5px 4px 5px 6px; - border-left: 1px solid var(--color-light-border); - border-top: 0; - border-right: 0; - border-bottom: 0; - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - -#activity-feed .sha.label .detail.icon img, -.repository #commits-table td.sha .sha.label .detail.icon img, -.repository #repo-files-table .sha.label .detail.icon img, -.repository #repo-file-commit-box .sha.label .detail.icon img, -.repository #rev-list .sha.label .detail.icon img, -.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon img { - margin-right: 0; -} - -#activity-feed .sha.label .detail.icon .svg, -.repository #commits-table td.sha .sha.label .detail.icon .svg, -.repository #repo-files-table .sha.label .detail.icon .svg, -.repository #repo-file-commit-box .sha.label .detail.icon .svg, -.repository #rev-list .sha.label .detail.icon .svg, -.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon .svg { - margin: 0 0.25em 0 0; -} - -#activity-feed .sha.label .detail.icon > div, -.repository #commits-table td.sha .sha.label .detail.icon > div, -.repository #repo-files-table .sha.label .detail.icon > div, -.repository #repo-file-commit-box .sha.label .detail.icon > div, -.repository #rev-list .sha.label .detail.icon > div, -.repository .timeline-item.commits-list .singular-commit .sha.label .detail.icon > div { - display: flex; - align-items: center; -} - -#activity-feed .sha.label.isSigned.isWarning, -.repository #commits-table td.sha .sha.label.isSigned.isWarning, -.repository #repo-files-table .sha.label.isSigned.isWarning, -.repository #repo-file-commit-box .sha.label.isSigned.isWarning, -.repository #rev-list .sha.label.isSigned.isWarning, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning { +.sha.label.isSigned.isWarning { border: 1px solid var(--color-red-badge); background: var(--color-red-badge-bg); } -#activity-feed .sha.label.isSigned.isWarning .detail.icon, -.repository #commits-table td.sha .sha.label.isSigned.isWarning .detail.icon, -.repository #repo-files-table .sha.label.isSigned.isWarning .detail.icon, -.repository #repo-file-commit-box .sha.label.isSigned.isWarning .detail.icon, -.repository #rev-list .sha.label.isSigned.isWarning .detail.icon, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning .detail.icon { +.sha.label.isSigned.isWarning .signature { border-left: 1px solid var(--color-red-badge); color: var(--color-red-badge); } -#activity-feed .sha.label.isSigned.isWarning:hover, -.repository #commits-table td.sha .sha.label.isSigned.isWarning:hover, -.repository #repo-files-table .sha.label.isSigned.isWarning:hover, -.repository #repo-file-commit-box .sha.label.isSigned.isWarning:hover, -.repository #rev-list .sha.label.isSigned.isWarning:hover, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isWarning:hover { +.sha.label.isSigned.isWarning:hover { background: var(--color-red-badge-hover-bg) !important; } -#activity-feed .sha.label.isSigned.isVerified, -.repository #commits-table td.sha .sha.label.isSigned.isVerified, -.repository #repo-files-table .sha.label.isSigned.isVerified, -.repository #repo-file-commit-box .sha.label.isSigned.isVerified, -.repository #rev-list .sha.label.isSigned.isVerified, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified { +.sha.label.isSigned.isVerified { border: 1px solid var(--color-green-badge); background: var(--color-green-badge-bg); } -#activity-feed .sha.label.isSigned.isVerified .detail.icon, -.repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon, -.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon, -.repository #repo-file-commit-box .sha.label.isSigned.isVerified .detail.icon, -.repository #rev-list .sha.label.isSigned.isVerified .detail.icon, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified .detail.icon { +.sha.label.isSigned.isVerified .signature { border-left: 1px solid var(--color-green-badge); color: var(--color-green-badge); } -#activity-feed .sha.label.isSigned.isVerified:hover, -.repository #commits-table td.sha .sha.label.isSigned.isVerified:hover, -.repository #repo-files-table .sha.label.isSigned.isVerified:hover, -.repository #repo-file-commit-box .sha.label.isSigned.isVerified:hover, -.repository #rev-list .sha.label.isSigned.isVerified:hover, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerified:hover { +.sha.label.isSigned.isVerified:hover { background: var(--color-green-badge-hover-bg) !important; } -#activity-feed .sha.label.isSigned.isVerifiedUntrusted, -.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted, -.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted, -.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted, -.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted { +.sha.label.isSigned.isVerifiedUntrusted { border: 1px solid var(--color-yellow-badge); background: var(--color-yellow-badge-bg); } -#activity-feed .sha.label.isSigned.isVerifiedUntrusted .detail.icon, -.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted .detail.icon, -.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted .detail.icon, -.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted .detail.icon, -.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted .detail.icon, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted .detail.icon { +.sha.label.isSigned.isVerifiedUntrusted .signature { border-left: 1px solid var(--color-yellow-badge); color: var(--color-yellow-badge); } -#activity-feed .sha.label.isSigned.isVerifiedUntrusted:hover, -.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUntrusted:hover, -.repository #repo-files-table .sha.label.isSigned.isVerifiedUntrusted:hover, -.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUntrusted:hover, -.repository #rev-list .sha.label.isSigned.isVerifiedUntrusted:hover, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUntrusted:hover { +.sha.label.isSigned.isVerifiedUntrusted:hover { background: var(--color-yellow-badge-hover-bg) !important; } -#activity-feed .sha.label.isSigned.isVerifiedUnmatched, -.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched, -.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched, -.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched, -.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched { +.sha.label.isSigned.isVerifiedUnmatched { border: 1px solid var(--color-orange-badge); background: var(--color-orange-badge-bg); } -#activity-feed .sha.label.isSigned.isVerifiedUnmatched .detail.icon, -.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched .detail.icon, -.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched .detail.icon, -.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched .detail.icon, -.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched .detail.icon, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched .detail.icon { +.sha.label.isSigned.isVerifiedUnmatched .signature { border-left: 1px solid var(--color-orange-badge); color: var(--color-orange-badge); } -#activity-feed .sha.label.isSigned.isVerifiedUnmatched:hover, -.repository #commits-table td.sha .sha.label.isSigned.isVerifiedUnmatched:hover, -.repository #repo-files-table .sha.label.isSigned.isVerifiedUnmatched:hover, -.repository #repo-file-commit-box .sha.label.isSigned.isVerifiedUnmatched:hover, -.repository #rev-list .sha.label.isSigned.isVerifiedUnmatched:hover, -.repository .timeline-item.commits-list .singular-commit .sha.label.isSigned.isVerifiedUnmatched:hover { +.sha.label.isSigned.isVerifiedUnmatched:hover { background: var(--color-orange-badge-hover-bg) !important; } From 8b93f41aaa7e7e32404db582ee4db1f0e4d57bc2 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 25 May 2025 13:31:53 +0200 Subject: [PATCH 35/94] fix(ui): ensure same width of usercards in grid (#6799) Followup to https://codeberg.org/forgejo/forgejo/pulls/4760 * some refactoring * move rules out of repo.css to a new module * simplify selectors by omitting .list: it is now only used to style the list itself, they're still precise enough in scope of .user-cards * apply wrap/ellipsis to cards' content. Done via CSS to avoid spamming gt-ellipsis in the template * prevent cards with long content from taking horizontal space from other cards * prevent such cards from causing horizontal overflow on mobile * prevent varying card height, it doesn't look good even with text wrapping Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6799 Reviewed-by: Otto Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- models/fixtures/follow.yml | 10 ++++++ models/fixtures/user.yml | 7 ++-- templates/repo/user_cards.tmpl | 2 +- tests/e2e/user-cards.test.e2e.ts | 29 ++++++++++++++++ web_src/css/index.css | 1 + web_src/css/modules/user-cards.css | 55 ++++++++++++++++++++++++++++++ web_src/css/repo.css | 47 ------------------------- 7 files changed, 100 insertions(+), 51 deletions(-) create mode 100644 tests/e2e/user-cards.test.e2e.ts create mode 100644 web_src/css/modules/user-cards.css diff --git a/models/fixtures/follow.yml b/models/fixtures/follow.yml index b8d35828bf..da3d4a60c1 100644 --- a/models/fixtures/follow.yml +++ b/models/fixtures/follow.yml @@ -17,3 +17,13 @@ id: 4 user_id: 31 follow_id: 33 + +- + id: 5 + user_id: 4 + follow_id: 8 + +- + id: 6 + user_id: 5 + follow_id: 8 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 52080b092f..82a1d28023 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -53,6 +53,7 @@ login_source: 0 login_name: user2 type: 0 + website: https://keyoxide.org/eb114f5e6c0dc2bcdd183550a4b61a2dc5923710 salt: ZogKvWdyEx max_repo_creation: -1 is_active: true @@ -143,7 +144,7 @@ avatar_email: user4@example.com use_custom_avatar: true num_followers: 0 - num_following: 1 + num_following: 2 num_stars: 0 num_repos: 0 num_teams: 0 @@ -181,7 +182,7 @@ avatar_email: user5@example.com use_custom_avatar: true num_followers: 0 - num_following: 0 + num_following: 1 num_stars: 0 num_repos: 1 num_teams: 0 @@ -294,7 +295,7 @@ avatar: "" avatar_email: user8@example.com use_custom_avatar: true - num_followers: 1 + num_followers: 3 num_following: 1 num_stars: 0 num_repos: 0 diff --git a/templates/repo/user_cards.tmpl b/templates/repo/user_cards.tmpl index fc5a30bd45..95871ee0dc 100644 --- a/templates/repo/user_cards.tmpl +++ b/templates/repo/user_cards.tmpl @@ -11,7 +11,7 @@ {{ctx.AvatarUtils.Avatar .}} -
    +

    {{.DisplayName}}

    diff --git a/tests/e2e/user-cards.test.e2e.ts b/tests/e2e/user-cards.test.e2e.ts new file mode 100644 index 0000000000..22c31965d6 --- /dev/null +++ b/tests/e2e/user-cards.test.e2e.ts @@ -0,0 +1,29 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +// @watch start +// templates/repo/user_cards.tmpl +// web_src/css/modules/user-cards.css +// @watch end + +import {expect} from '@playwright/test'; +import {test} from './utils_e2e.ts'; + +test('Usercards width', async ({page}) => { + await page.goto('/user8?tab=followers'); + + // Regardless of whether cards in a grid or flex mode, they should be ~same + // width. Verifying this relies on fixtures with users that have long website + // link or other content that could push the card width. + const widths = []; + const amount = 3; + + for (let i = 1; i <= amount; i++) { + const card = await page.locator(`.user-cards .card:nth-child(${i})`).boundingBox(); + widths.push(Math.round(card.width)); + } + + for (const width of widths) { + expect(width).toBe(widths[0]); + } +}); diff --git a/web_src/css/index.css b/web_src/css/index.css index 0a5a0180aa..88aa9bbf4a 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -27,6 +27,7 @@ @import "./modules/toast.css"; @import "./modules/svg.css"; @import "./modules/flexcontainer.css"; +@import "./modules/user-cards.css"; @import "./shared/flex-list.css"; @import "./shared/milestone.css"; diff --git a/web_src/css/modules/user-cards.css b/web_src/css/modules/user-cards.css new file mode 100644 index 0000000000..d89ea4588c --- /dev/null +++ b/web_src/css/modules/user-cards.css @@ -0,0 +1,55 @@ +.user-cards .list { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 15px; + margin: 0 0 10px; + padding: 0; +} + +@media (max-width: 767.98px) { + .user-cards .list { + grid-template-columns: repeat(1, 1fr); + } +} + +@media (max-width: 900px) { + .user.profile .user-cards .list { + grid-template-columns: repeat(1, 1fr); + } +} + +.user-cards .card { + display: flex; + flex-direction: row; + width: 100%; + margin: 0; + padding: 14px; + border-radius: 0.28571429rem; + border: 1px solid var(--color-secondary); + background: var(--color-box-body); +} + +.user-cards .card, +.user-cards .card .content, +.user-cards .card .name, +.user-cards .card .meta { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.user-cards .card .avatar { + width: 48px; + height: 48px; + margin-right: 14px; +} + +.user-cards .card .name { + margin-top: 0; + margin-bottom: 0; + font-weight: var(--font-weight-normal); +} + +.user-cards .card .meta { + margin-top: 5px; +} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 35133b965f..651923e60b 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2010,53 +2010,6 @@ details.repo-search-result summary::marker { padding: 0 10px; } -.user-cards .list { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 15px; - margin: 0 0 10px; - padding: 0; -} - -@media (max-width: 767.98px) { - .user-cards .list { - grid-template-columns: repeat(1, 1fr); - } -} - -@media (max-width: 900px) { - .user.profile .user-cards .list { - grid-template-columns: repeat(1, 1fr); - } -} - -.user-cards .list .card { - display: flex; - flex-direction: row; - width: 100%; - margin: 0; - padding: 14px; - border-radius: 0.28571429rem; - border: 1px solid var(--color-secondary); - background: var(--color-box-body); -} - -.user-cards .list .card .avatar { - width: 48px; - height: 48px; - margin-right: 14px; -} - -.user-cards .list .card .name { - margin-top: 0; - margin-bottom: 0; - font-weight: var(--font-weight-normal); -} - -.user-cards .list .card .meta { - margin-top: 5px; -} - #search-user-box .results .result .image { order: 0; margin-right: 12px; From d483dc674a64a245e1e3c38bcb45297e68efabae Mon Sep 17 00:00:00 2001 From: Danko Aleksejevs Date: Sun, 25 May 2025 19:17:03 +0200 Subject: [PATCH 36/94] Reimplement editor Tab handling with accessibility safeguards (#6813) The primary goal is to balance having the editor work as expected by developers (with Tab key affecting indentation) while also not impeding keyboard navigation. * Tab indents, Shift+Tab unindents, but only when that indent would be valid. E.g. moving existing list items down or up one level. * Indenting a selection always works. * When an "invalid" indent is attempted, nothing happens and a toast is shown with a hint to press again to leave the editor. * Attempting the same action again allows the textarea lose focus by allowing the browser's default key handler. * Pressing Esc also loses focus immediately. * No tab handling happens until the text editor has been interacted with (other than just having been focused). * Changing indentation in block quotes adds or removes quote levels instead. Screenshot of the toast being shown: https://codeberg.org/forgejo/forgejo/attachments/a6287d29-4ce0-4977-aae8-ef1aff2ac89f Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6813 Reviewed-by: Otto Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Danko Aleksejevs Co-committed-by: Danko Aleksejevs --- build/lint-locale/lint-locale.go | 2 +- options/locale_next/locale_en-US.json | 2 + release-notes/6813.md | 1 + templates/shared/combomarkdowneditor.tmpl | 2 +- tests/e2e/markdown-editor.test.e2e.ts | 143 ++++++++++++++++- .../js/features/comp/ComboMarkdownEditor.js | 147 ++++++++++++++---- web_src/js/modules/toast.js | 9 ++ web_src/js/svg.js | 2 + 8 files changed, 278 insertions(+), 30 deletions(-) create mode 100644 release-notes/6813.md diff --git a/build/lint-locale/lint-locale.go b/build/lint-locale/lint-locale.go index 0d80ffa4b0..dc4088c73c 100644 --- a/build/lint-locale/lint-locale.go +++ b/build/lint-locale/lint-locale.go @@ -52,7 +52,7 @@ func initBlueMondayPolicy() { policy.AllowAttrs("id").Matching(positionalPlaceholderRe).OnElements("code") // Allowed elements with no attributes. Must be a recognized tagname. - policy.AllowElements("strong", "br", "b", "strike", "code", "i") + policy.AllowElements("strong", "br", "b", "strike", "code", "i", "kbd") // TODO: Remove in `actions.workflow.dispatch.trigger_found`. policy.AllowNoAttrs().OnElements("c") diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 6305a8a8ed..3015be3ecd 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -90,5 +90,7 @@ "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Triggered because: %[1]s by: %[2]s", "discussion.locked": "This discussion has been locked. Commenting is limited to contributors.", + "editor.textarea.tab_hint": "Line already indented. Press Tab again or Escape to leave the editor.", + "editor.textarea.shift_tab_hint": "No indentation on this line. Press Shift + Tab again or Escape to leave the editor.", "meta.last_line": "Thank you for translating Forgejo! This line isn't seen by the users but it serves other purposes in the translation management. You can place a fun fact in the translation instead of translating it." } diff --git a/release-notes/6813.md b/release-notes/6813.md new file mode 100644 index 0000000000..bd9da8dc39 --- /dev/null +++ b/release-notes/6813.md @@ -0,0 +1 @@ +Reimplemented editor Tab key handling with accessibility safeguards. Balance having the editor work as expected by developers (with Tab key affecting indentation) while also not impeding keyboard navigation. diff --git a/templates/shared/combomarkdowneditor.tmpl b/templates/shared/combomarkdowneditor.tmpl index e9fbb67313..febca5ec7f 100644 --- a/templates/shared/combomarkdowneditor.tmpl +++ b/templates/shared/combomarkdowneditor.tmpl @@ -12,7 +12,7 @@ Template Attributes: * DisableAutosize: whether to disable automatic height resizing * EasyMDE: whether to display button for switching to legacy editor */}} -
    +
    {{if .MarkdownPreviewUrl}} diff --git a/tests/e2e/markdown-editor.test.e2e.ts b/tests/e2e/markdown-editor.test.e2e.ts index c69c9a7f0c..2b5f0d80a0 100644 --- a/tests/e2e/markdown-editor.test.e2e.ts +++ b/tests/e2e/markdown-editor.test.e2e.ts @@ -39,7 +39,7 @@ test('Markdown image preview behaviour', async ({page}, workerInfo) => { await save_visual(page); }); -test('Markdown indentation', async ({page}) => { +test('Markdown indentation via toolbar', async ({page}) => { const initText = `* first\n* second\n* third\n* last`; const response = await page.goto('/user2/repo1/issues/new'); @@ -50,7 +50,6 @@ test('Markdown indentation', async ({page}) => { const indent = page.locator('button[data-md-action="indent"]'); const unindent = page.locator('button[data-md-action="unindent"]'); await textarea.fill(initText); - await textarea.click(); // Tab handling is disabled until pointer event or input. // Indent, then unindent first line await textarea.focus(); @@ -109,6 +108,146 @@ test('Markdown indentation', async ({page}) => { await expect(textarea).toHaveValue(initText); }); +test('markdown indentation with Tab', async ({page}) => { + const initText = `* first\n* second\n* third\n* last`; + + const response = await page.goto('/user2/repo1/issues/new'); + expect(response?.status()).toBe(200); + + const textarea = page.locator('textarea[name=content]'); + const toast = page.locator('.toastify'); + const tab = ' '; + + await textarea.fill(initText); + + await textarea.click(); // Tab handling is disabled until pointer event or input. + + // Indent, then unindent first line + await textarea.focus(); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(0, 0)); + await textarea.press('Tab'); + await expect(textarea).toHaveValue(`${tab}* first\n* second\n* third\n* last`); + await textarea.press('Shift+Tab'); + await expect(textarea).toHaveValue(initText); + + // Attempt unindent again, ensure focus is not immediately lost and toast is shown, but then focus is lost on next attempt. + await expect(toast).toBeHidden(); // toast should not already be there + await textarea.press('Shift+Tab'); + await expect(textarea).toBeFocused(); + await expect(toast).toBeVisible(); + await textarea.press('Shift+Tab'); + await expect(textarea).not.toBeFocused(); + + // Indent lines 2-4 + await textarea.click(); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('\n') + 1, it.value.length)); + await textarea.press('Tab'); + await expect(textarea).toHaveValue(`* first\n${tab}* second\n${tab}* third\n${tab}* last`); + + // Indent second line while in whitespace, then unindent. + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf(' * third'), it.value.indexOf(' * third'))); + await textarea.press('Tab'); + await expect(textarea).toHaveValue(`* first\n${tab}* second\n${tab}${tab}* third\n${tab}* last`); + await textarea.press('Shift+Tab'); + await expect(textarea).toHaveValue(`* first\n${tab}* second\n${tab}* third\n${tab}* last`); + + // Select all and unindent, then lose focus. + await textarea.evaluate((it:HTMLTextAreaElement) => it.select()); + await textarea.press('Shift+Tab'); // Everything is unindented. + await expect(textarea).toHaveValue(initText); + await textarea.press('Shift+Tab'); // Valid, but nothing happens -> switch to "about to lose focus" state. + await expect(textarea).toBeFocused(); + await textarea.press('Shift+Tab'); + await expect(textarea).not.toBeFocused(); + + // Attempt the same with cursor within list element body. + await textarea.focus(); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(0, 0)); + await textarea.press('ArrowRight'); + await textarea.press('ArrowRight'); + await textarea.press('Tab'); + // Whole line should be indented. + await expect(textarea).toHaveValue(`${tab}* first\n* second\n* third\n* last`); + await textarea.press('Shift+Tab'); + + // Subsequently, select a chunk of 2nd and 3rd line and indent both, preserving the cursor position in relation to text + const line3 = `* first\n* second\n${tab}* third\n* last`; + const lines23 = `* first\n${tab}* second\n${tab}${tab}* third\n* last`; + await textarea.focus(); + await textarea.fill(line3); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('cond'), it.value.indexOf('hird'))); + await textarea.press('Tab'); + await expect(textarea).toHaveValue(lines23); + await expect(textarea).toHaveJSProperty('selectionStart', lines23.indexOf('cond')); + await expect(textarea).toHaveJSProperty('selectionEnd', lines23.indexOf('hird')); + + // Then unindent twice, erasing all indents. + await textarea.press('Shift+Tab'); + await expect(textarea).toHaveValue(line3); + await textarea.press('Shift+Tab'); + await expect(textarea).toHaveValue(initText); + + // Check that partial indents are cleared + await textarea.focus(); + await textarea.fill(initText); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('* second'), it.value.indexOf('* second'))); + await textarea.pressSequentially(' '); + await textarea.press('Shift+Tab'); + await expect(textarea).toHaveValue(initText); +}); + +test('markdown block quote indentation', async ({page}) => { + const initText = `> first\n> second\n> third\n> last`; + + const response = await page.goto('/user2/repo1/issues/new'); + expect(response?.status()).toBe(200); + + const textarea = page.locator('textarea[name=content]'); + const toast = page.locator('.toastify'); + + await textarea.fill(initText); + + await textarea.click(); // Tab handling is disabled until pointer event or input. + + // Indent, then unindent first line twice (quotes can quote quotes!) + await textarea.focus(); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(0, 0)); + await textarea.press('Tab'); + await expect(textarea).toHaveValue(`> > first\n> second\n> third\n> last`); + await textarea.press('Tab'); + await expect(textarea).toHaveValue(`> > > first\n> second\n> third\n> last`); + await textarea.press('Shift+Tab'); + await textarea.press('Shift+Tab'); + await expect(textarea).toHaveValue(initText); + + // Attempt unindent again. + await expect(toast).toBeHidden(); // toast should not already be there + await textarea.press('Shift+Tab'); + // Nothing happens - quote should not stop being a quote + await expect(textarea).toHaveValue(initText); + // Focus is not immediately lost and toast is shown, + await expect(textarea).toBeFocused(); + await expect(toast).toBeVisible(); + // Focus is lost on next attempt, + await textarea.press('Shift+Tab'); + await expect(textarea).not.toBeFocused(); + + // Indent lines 2-4 + await textarea.click(); + await textarea.evaluate((it:HTMLTextAreaElement) => it.setSelectionRange(it.value.indexOf('\n') + 1, it.value.length)); + await textarea.press('Tab'); + await expect(textarea).toHaveValue(`> first\n> > second\n> > third\n> > last`); + + // Select all and unindent, then lose focus. + await textarea.evaluate((it:HTMLTextAreaElement) => it.select()); + await textarea.press('Shift+Tab'); // Everything is unindented. + await expect(textarea).toHaveValue(initText); + await textarea.press('Shift+Tab'); // Valid, but nothing happens -> switch to "about to lose focus" state. + await expect(textarea).toBeFocused(); + await textarea.press('Shift+Tab'); + await expect(textarea).not.toBeFocused(); +}); + test('Markdown list continuation', async ({page}) => { const initText = `* first\n* second`; diff --git a/web_src/js/features/comp/ComboMarkdownEditor.js b/web_src/js/features/comp/ComboMarkdownEditor.js index 53c6b85728..21bc7e694f 100644 --- a/web_src/js/features/comp/ComboMarkdownEditor.js +++ b/web_src/js/features/comp/ComboMarkdownEditor.js @@ -2,13 +2,13 @@ import '@github/markdown-toolbar-element'; import '@github/text-expander-element'; import $ from 'jquery'; import {attachTribute} from '../tribute.js'; -import {hideElem, showElem, autosize, isElemVisible, replaceTextareaSelection} from '../../utils/dom.js'; +import {autosize, hideElem, isElemVisible, replaceTextareaSelection, showElem} from '../../utils/dom.js'; import {initEasyMDEPaste, initTextareaPaste} from './Paste.js'; import {handleGlobalEnterQuickSubmit} from './QuickSubmit.js'; import {renderPreviewPanelContent} from '../repo-editor.js'; import {easyMDEToolbarActions} from './EasyMDEToolbarActions.js'; import {initTextExpander} from './TextExpander.js'; -import {showErrorToast} from '../../modules/toast.js'; +import {showErrorToast, showHintToast} from '../../modules/toast.js'; import {POST} from '../../modules/fetch.js'; let elementIdCounter = 0; @@ -35,6 +35,9 @@ export function validateTextareaNonEmpty(textarea) { return true; } +// Matches the beginning of a line containing leading whitespace and possibly valid list or block quote prefix +const listPrefixRegex = /^\s*((\d+)[.)]\s|[-*+]\s{1,4}\[[ x]\]\s?|[-*+]\s|(>\s?)+)?/; + class ComboMarkdownEditor { constructor(container, options = {}) { container._giteaComboMarkdownEditor = this; @@ -88,24 +91,62 @@ class ComboMarkdownEditor { if (el.nodeName === 'BUTTON' && !el.getAttribute('type')) el.setAttribute('type', 'button'); } this.textareaMarkdownToolbar.querySelector('button[data-md-action="indent"]')?.addEventListener('click', () => { - this.indentSelection(false); + this.indentSelection(false, false); }); this.textareaMarkdownToolbar.querySelector('button[data-md-action="unindent"]')?.addEventListener('click', () => { - this.indentSelection(true); + this.indentSelection(true, false); }); this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-table"]')?.setAttribute('data-modal', `div[data-markdown-table-modal-id="${elementIdCounter}"]`); this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-link"]')?.setAttribute('data-modal', `div[data-markdown-link-modal-id="${elementIdCounter}"]`); + // Track whether any actual input or pointer action was made after focusing, and only intercept Tab presses after that. + this.tabEnabled = false; + // This tracks whether last Tab action was ignored, and if it immediately happens *again*, lose focus. + this.ignoredTabAction = false; + this.ignoredTabToast = null; + + this.textarea.addEventListener('focus', () => { + this.tabEnabled = false; + this.ignoredTabAction = false; + }); + this.textarea.addEventListener('pointerup', () => { + // Assume if a pointer is used then Tab handling is a bit less of an issue. + this.tabEnabled = true; + }); this.textarea.addEventListener('keydown', (e) => { if (e.shiftKey) { e.target._shiftDown = true; } - if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey && !e.altKey) { - // Prevent special line break handling if currently a text expander popup is open - if (this.textarea.hasAttribute('aria-expanded')) return; + + // Prevent special keyboard handling if currently a text expander popup is open + if (this.textarea.hasAttribute('aria-expanded')) return; + + const noModifiers = !e.shiftKey && !e.ctrlKey && !e.altKey; + if (e.key === 'Escape') { + // Explicitly lose focus and reenable tab navigation. + e.target.blur(); + this.tabEnabled = false; + } else if (e.key === 'Tab' && this.tabEnabled && !e.altKey && !e.ctrlKey) { + if (this.indentSelection(e.shiftKey, true)) { + this.options?.onContentChanged?.(this, e); + e.preventDefault(); + this.activateTabHandling(); + } else if (!this.ignoredTabAction) { + e.preventDefault(); + this.ignoredTabAction = true; + this.ignoredTabToast?.hideToast(); + this.ignoredTabToast = showHintToast( + this.container.dataset[e.shiftKey ? 'shiftTabHint' : 'tabHint'], + {gravity: 'bottom', useHtmlBody: true}, + ); + this.ignoredTabToast.toastElement.role = 'alert'; + } + } else if (e.key === 'Enter' && noModifiers) { if (!this.breakLine()) return; // Nothing changed, let the default handler work. this.options?.onContentChanged?.(this, e); e.preventDefault(); + } else if (noModifiers) { + this.activateTabHandling(); } }); this.textarea.addEventListener('keyup', (e) => { @@ -142,6 +183,15 @@ class ComboMarkdownEditor { } } + activateTabHandling() { + this.tabEnabled = true; + this.ignoredTabAction = false; + if (this.ignoredTabToast) { + this.ignoredTabToast.hideToast(); + this.ignoredTabToast = null; + } + } + setupDropzone() { const dropzoneParentContainer = this.container.getAttribute('data-dropzone-parent-container'); if (dropzoneParentContainer) { @@ -403,13 +453,15 @@ class ComboMarkdownEditor { } } - indentSelection(unindent) { + // Indent all lines that are included in the selection, partially or whole, while preserving the original selection at the end. + indentSelection(unindent, validOnly) { // Indent with 4 spaces, unindent 4 spaces or fewer or a lost tab. const indentPrefix = ' '; - const unindentRegex = /^( {1,4}|\t)/; + const unindentRegex = /^( {1,4}|\t|> {0,4})/; + const indentLevel = / {4}|\t|> /g; - // Indent all lines that are included in the selection, partially or whole, while preserving the original selection at the end. - const lines = this.textarea.value.split('\n'); + const value = this.textarea.value; + const lines = value.split('\n'); const changedLines = []; // The current selection or cursor position. const [start, end] = [this.textarea.selectionStart, this.textarea.selectionEnd]; @@ -419,31 +471,66 @@ class ComboMarkdownEditor { let [newStart, newEnd] = [start, end]; // The start and end position of the current line (where end points to the newline or EOF) let [lineStart, lineEnd] = [0, 0]; + // Index of the first line included in the selection (or containing the cursor) + let firstLineIdx = 0; - for (const line of lines) { + // Find all the lines in selection beforehand so we know the full set before we start changing. + const linePositions = []; + for (const [i, line] of lines.entries()) { lineEnd = lineStart + line.length + 1; if (lineEnd <= start) { lineStart = lineEnd; continue; } + linePositions.push([lineStart, line]); + if (start >= lineStart && start < lineEnd) { + firstLineIdx = i; + editStart = lineStart; + } + editEnd = lineEnd - 1; + if (lineEnd >= end) break; + lineStart = lineEnd; + } - const updated = unindent ? line.replace(unindentRegex, '') : indentPrefix + line; + // Block quotes need to be nested/unnested instead of whitespace added/removed. However, only do this if the *whole* selection is in a quote. + const isQuote = linePositions.every(([_, line]) => line[0] === '>'); + + const line = lines[firstLineIdx]; + // If there's no indent to remove, do nothing + if (unindent && start === end && !unindentRegex.test(line)) { + return false; + } + + // If there is no selection and this is an ambiguous command (Tab handling), only (un)indent if already in a code/list. + if (!unindent && validOnly && start === end) { + // Check there's any indentation or prefix at all. + const match = line.match(listPrefixRegex); + if (!match || !match[0].length) return false; + // Check that the line isn't already indented in relation to parent. + const levels = line.match(indentLevel)?.length ?? 0; + const parentLevels = !firstLineIdx ? 0 : lines[firstLineIdx - 1].match(indentLevel)?.length ?? 0; + // Quotes can *begin* multiple levels in, so just allow whatever for now. + if (levels - parentLevels > 0 && !isQuote) return false; + } + + // Apply indentation changes to lines. + for (const [i, [lineStart, line]] of linePositions.entries()) { + const updated = isQuote ? + (unindent ? line.replace(/^>\s{0,4}>/, '>') : `> ${line}`) : + (unindent ? line.replace(unindentRegex, '') : indentPrefix + line); changedLines.push(updated); const move = updated.length - line.length; - - if (start >= lineStart && start < lineEnd) { - editStart = lineStart; - newStart = Math.max(start + move, lineStart); - } - + if (i === 0) newStart = Math.max(start + move, lineStart); newEnd += move; - editEnd = lineEnd - 1; - lineStart = lineEnd; - if (lineStart > end) break; } // Update changed lines whole. const text = changedLines.join('\n'); + if (text === value.slice(editStart, editEnd)) { + // Nothing changed, likely due to Shift+Tab when no indents are left. + return false; + } + this.textarea.focus(); this.textarea.setSelectionRange(editStart, editEnd); if (!document.execCommand('insertText', false, text)) { @@ -454,6 +541,8 @@ class ComboMarkdownEditor { // Set selection to (effectively) be the same as before. this.textarea.setSelectionRange(newStart, Math.max(newStart, newEnd)); + + return true; } breakLine() { @@ -470,7 +559,7 @@ class ComboMarkdownEditor { const lineEnd = nextLF === -1 ? value.length : nextLF; const line = value.slice(lineStart, lineEnd); // Match any whitespace at the start + any repeatable prefix + exactly one space after. - const prefix = line.match(/^\s*((\d+)[.)]\s|[-*+]\s{1,4}\[[ x]\]\s?|[-*+]\s|(>\s?)+)?/); + const prefix = line.match(listPrefixRegex); // Defer to browser if we can't do anything more useful, or if the cursor is inside the prefix. if (!prefix) return false; @@ -489,14 +578,20 @@ class ComboMarkdownEditor { } // Insert newline + prefix. - let text = `\n${prefix[0]}`; + let text = `${prefix[0]}`; // Increment a number if present. (perhaps detecting repeating 1. and not doing that then would be a good idea) const num = text.match(/\d+/); if (num) text = text.replace(num[0], Number(num[0]) + 1); text = text.replace('[x]', '[ ]'); - if (!document.execCommand('insertText', false, text)) { - this.textarea.setRangeText(text); + // Split the newline and prefix addition in two, so that it's two separate undo entries in Firefox + // Chrome seems to bundle everything together more aggressively, even with prior text input. + if (document.execCommand('insertText', false, '\n')) { + setTimeout(() => { + document.execCommand('insertText', false, text); + }, 1); + } else { + this.textarea.setRangeText(`\n${text}`); } return true; diff --git a/web_src/js/modules/toast.js b/web_src/js/modules/toast.js index d12d203718..177800d4fb 100644 --- a/web_src/js/modules/toast.js +++ b/web_src/js/modules/toast.js @@ -3,6 +3,11 @@ import {svg} from '../svg.js'; import Toastify from 'toastify-js'; // don't use "async import", because when network error occurs, the "async import" also fails and nothing is shown const levels = { + hint: { + icon: 'octicon-light-bulb', + background: 'var(--color-black-light)', + duration: 2500, + }, info: { icon: 'octicon-check', background: 'var(--color-green)', @@ -42,6 +47,10 @@ function showToast(message, level, {gravity, position, duration, useHtmlBody, .. return toast; } +export function showHintToast(message, opts) { + return showToast(message, 'hint', opts); +} + export function showInfoToast(message, opts) { return showToast(message, 'info', opts); } diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 3134279c84..13f26348c2 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -42,6 +42,7 @@ import octiconIssueClosed from '../../public/assets/img/svg/octicon-issue-closed import octiconIssueOpened from '../../public/assets/img/svg/octicon-issue-opened.svg'; import octiconItalic from '../../public/assets/img/svg/octicon-italic.svg'; import octiconKebabHorizontal from '../../public/assets/img/svg/octicon-kebab-horizontal.svg'; +import octiconLightBulb from '../../public/assets/img/svg/octicon-light-bulb.svg'; import octiconLink from '../../public/assets/img/svg/octicon-link.svg'; import octiconListOrdered from '../../public/assets/img/svg/octicon-list-ordered.svg'; import octiconListUnordered from '../../public/assets/img/svg/octicon-list-unordered.svg'; @@ -117,6 +118,7 @@ const svgs = { 'octicon-issue-opened': octiconIssueOpened, 'octicon-italic': octiconItalic, 'octicon-kebab-horizontal': octiconKebabHorizontal, + 'octicon-light-bulb': octiconLightBulb, 'octicon-link': octiconLink, 'octicon-list-ordered': octiconListOrdered, 'octicon-list-unordered': octiconListUnordered, From 8d5d3fc47cd3771b0ff68f8786a448b80211b733 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 26 May 2025 07:53:24 +0200 Subject: [PATCH 37/94] Lock file maintenance (forgejo) (#7961) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM, only on Monday ( * 0-3 * * 1 ) (UTC), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7961 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 286 ++++++++++++++--------------- web_src/fomantic/package-lock.json | 148 +++++++-------- 2 files changed, 217 insertions(+), 217 deletions(-) diff --git a/package-lock.json b/package-lock.json index b2bf666164..8009daff0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2211,9 +2211,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz", - "integrity": "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz", + "integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==", "cpu": [ "arm" ], @@ -2225,9 +2225,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz", - "integrity": "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz", + "integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==", "cpu": [ "arm64" ], @@ -2239,9 +2239,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz", - "integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz", + "integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==", "cpu": [ "arm64" ], @@ -2253,9 +2253,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz", - "integrity": "sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz", + "integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==", "cpu": [ "x64" ], @@ -2267,9 +2267,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz", - "integrity": "sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz", + "integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==", "cpu": [ "arm64" ], @@ -2281,9 +2281,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz", - "integrity": "sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz", + "integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==", "cpu": [ "x64" ], @@ -2295,9 +2295,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz", - "integrity": "sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz", + "integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==", "cpu": [ "arm" ], @@ -2309,9 +2309,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz", - "integrity": "sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz", + "integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==", "cpu": [ "arm" ], @@ -2323,9 +2323,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz", - "integrity": "sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz", + "integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==", "cpu": [ "arm64" ], @@ -2337,9 +2337,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz", - "integrity": "sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz", + "integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==", "cpu": [ "arm64" ], @@ -2351,9 +2351,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz", - "integrity": "sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz", + "integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==", "cpu": [ "loong64" ], @@ -2365,9 +2365,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz", - "integrity": "sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz", + "integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==", "cpu": [ "ppc64" ], @@ -2379,9 +2379,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz", - "integrity": "sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz", + "integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==", "cpu": [ "riscv64" ], @@ -2393,9 +2393,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz", - "integrity": "sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz", + "integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==", "cpu": [ "riscv64" ], @@ -2407,9 +2407,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz", - "integrity": "sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz", + "integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==", "cpu": [ "s390x" ], @@ -2421,9 +2421,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz", - "integrity": "sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", + "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", "cpu": [ "x64" ], @@ -2435,9 +2435,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz", - "integrity": "sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", + "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", "cpu": [ "x64" ], @@ -2449,9 +2449,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz", - "integrity": "sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz", + "integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==", "cpu": [ "arm64" ], @@ -2463,9 +2463,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz", - "integrity": "sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz", + "integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==", "cpu": [ "ia32" ], @@ -2477,9 +2477,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz", - "integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz", + "integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==", "cpu": [ "x64" ], @@ -3429,9 +3429,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz", - "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==", + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -6596,9 +6596,9 @@ } }, "node_modules/dompurify": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz", - "integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -6699,9 +6699,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.155", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", - "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==", + "version": "1.5.157", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.157.tgz", + "integrity": "sha512-/0ybgsQd1muo8QlnuTpKwtl0oX5YMlUGbm8xyqgDU00motRkKFFbUJySAQBWcY79rVqNLWIWa87BGVGClwAB2w==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -6775,9 +6775,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "version": "1.23.10", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.10.tgz", + "integrity": "sha512-MtUbM072wlJNyeYAe0mhzrD+M6DIJa96CZAOBBrhDbgKnB4MApIKefcyAB1eOdYn8cUNZgvwBvEzdoAYsxgEIw==", "dev": true, "license": "MIT", "dependencies": { @@ -6785,18 +6785,18 @@ "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -6812,13 +6812,13 @@ "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", @@ -6831,7 +6831,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -7339,6 +7339,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/eslint-plugin-sonarjs/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-plugin-toml": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/eslint-plugin-toml/-/eslint-plugin-toml-0.12.0.tgz", @@ -8080,9 +8093,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", - "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" @@ -10216,9 +10229,9 @@ "license": "MIT" }, "node_modules/mermaid/node_modules/marked": { - "version": "15.0.11", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.11.tgz", - "integrity": "sha512-1BEXAU2euRCG3xwgLVT1y0xbJEld1XOrmRJpUwRCcy7rxhSCwMrmEu9LXoPhHSCJG41V7YcQ2mjKRr5BA3ITIA==", + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -12703,9 +12716,9 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -12724,18 +12737,18 @@ } }, "node_modules/seroval": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.1.tgz", - "integrity": "sha512-F+T9EQPdLzgdewgxnBh4mSc+vde+EOkU6dC9BDuu/bfGb+UyUlqM6t8znFCTPQSuai/ZcfFg0gu79h+bVW2O0w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", + "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/seroval-plugins": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.1.tgz", - "integrity": "sha512-dOlUoiI3fgZbQIcj6By+l865pzeWdP3XCSLdI3xlKnjCk5983yLWPsXytFOUI0BUZKG9qwqbj78n9yVcVwUqaQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.2.tgz", + "integrity": "sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==", "license": "MIT", "engines": { "node": ">=10" @@ -12847,19 +12860,6 @@ "@img/sharp-win32-x64": "0.34.2" } }, - "node_modules/sharp/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -13964,9 +13964,9 @@ } }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "license": "MIT", "engines": { "node": ">=6" @@ -14139,9 +14139,9 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14848,9 +14848,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz", - "integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", + "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", "dev": true, "license": "MIT", "dependencies": { @@ -14864,26 +14864,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.41.0", - "@rollup/rollup-android-arm64": "4.41.0", - "@rollup/rollup-darwin-arm64": "4.41.0", - "@rollup/rollup-darwin-x64": "4.41.0", - "@rollup/rollup-freebsd-arm64": "4.41.0", - "@rollup/rollup-freebsd-x64": "4.41.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.41.0", - "@rollup/rollup-linux-arm-musleabihf": "4.41.0", - "@rollup/rollup-linux-arm64-gnu": "4.41.0", - "@rollup/rollup-linux-arm64-musl": "4.41.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.41.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0", - "@rollup/rollup-linux-riscv64-gnu": "4.41.0", - "@rollup/rollup-linux-riscv64-musl": "4.41.0", - "@rollup/rollup-linux-s390x-gnu": "4.41.0", - "@rollup/rollup-linux-x64-gnu": "4.41.0", - "@rollup/rollup-linux-x64-musl": "4.41.0", - "@rollup/rollup-win32-arm64-msvc": "4.41.0", - "@rollup/rollup-win32-ia32-msvc": "4.41.0", - "@rollup/rollup-win32-x64-msvc": "4.41.0", + "@rollup/rollup-android-arm-eabi": "4.41.1", + "@rollup/rollup-android-arm64": "4.41.1", + "@rollup/rollup-darwin-arm64": "4.41.1", + "@rollup/rollup-darwin-x64": "4.41.1", + "@rollup/rollup-freebsd-arm64": "4.41.1", + "@rollup/rollup-freebsd-x64": "4.41.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", + "@rollup/rollup-linux-arm-musleabihf": "4.41.1", + "@rollup/rollup-linux-arm64-gnu": "4.41.1", + "@rollup/rollup-linux-arm64-musl": "4.41.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-musl": "4.41.1", + "@rollup/rollup-linux-s390x-gnu": "4.41.1", + "@rollup/rollup-linux-x64-gnu": "4.41.1", + "@rollup/rollup-linux-x64-musl": "4.41.1", + "@rollup/rollup-win32-arm64-msvc": "4.41.1", + "@rollup/rollup-win32-ia32-msvc": "4.41.1", + "@rollup/rollup-win32-x64-msvc": "4.41.1", "fsevents": "~2.3.2" } }, @@ -15124,9 +15124,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", @@ -15297,9 +15297,9 @@ } }, "node_modules/webpack/node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.0.tgz", + "integrity": "sha512-77R0RDmJfj9dyv5p3bM5pOHa+X8/ZkO9c7kpDstigkC4nIDobadsfSGCwB4bKhMVxqAok8tajaoR8rirM7+VFQ==", "license": "MIT", "engines": { "node": ">=10.13.0" diff --git a/web_src/fomantic/package-lock.json b/web_src/fomantic/package-lock.json index 7e8240d826..cad06cc525 100644 --- a/web_src/fomantic/package-lock.json +++ b/web_src/fomantic/package-lock.json @@ -132,38 +132,38 @@ } }, "node_modules/@octokit/core": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", - "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz", + "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==", "license": "MIT", "peer": true, "dependencies": { - "@octokit/auth-token": "^5.0.0", - "@octokit/graphql": "^8.2.2", - "@octokit/request": "^9.2.3", - "@octokit/request-error": "^6.1.8", + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.1", + "@octokit/request": "^10.0.2", + "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", - "before-after-hook": "^3.0.2", + "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/core/node_modules/@octokit/auth-token": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", - "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", "license": "MIT", "peer": true, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/core/node_modules/@octokit/endpoint": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", - "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", + "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", "license": "MIT", "peer": true, "dependencies": { @@ -171,60 +171,60 @@ "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { - "version": "25.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", - "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", "license": "MIT", "peer": true }, "node_modules/@octokit/core/node_modules/@octokit/request": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz", - "integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.2.tgz", + "integrity": "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==", "license": "MIT", "peer": true, "dependencies": { - "@octokit/endpoint": "^10.1.4", - "@octokit/request-error": "^6.1.8", + "@octokit/endpoint": "^11.0.0", + "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", - "fast-content-type-parse": "^2.0.0", + "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/core/node_modules/@octokit/request-error": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", - "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz", + "integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==", "license": "MIT", "peer": true, "dependencies": { "@octokit/types": "^14.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/core/node_modules/@octokit/types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", - "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "license": "MIT", "peer": true, "dependencies": { - "@octokit/openapi-types": "^25.0.0" + "@octokit/openapi-types": "^25.1.0" } }, "node_modules/@octokit/core/node_modules/before-after-hook": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", - "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", "license": "Apache-2.0", "peer": true }, @@ -253,24 +253,24 @@ "license": "ISC" }, "node_modules/@octokit/graphql": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", - "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz", + "integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==", "license": "MIT", "peer": true, "dependencies": { - "@octokit/request": "^9.2.3", + "@octokit/request": "^10.0.2", "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/graphql/node_modules/@octokit/endpoint": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", - "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", + "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", "license": "MIT", "peer": true, "dependencies": { @@ -278,54 +278,54 @@ "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { - "version": "25.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", - "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", "license": "MIT", "peer": true }, "node_modules/@octokit/graphql/node_modules/@octokit/request": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz", - "integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.2.tgz", + "integrity": "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==", "license": "MIT", "peer": true, "dependencies": { - "@octokit/endpoint": "^10.1.4", - "@octokit/request-error": "^6.1.8", + "@octokit/endpoint": "^11.0.0", + "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", - "fast-content-type-parse": "^2.0.0", + "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/graphql/node_modules/@octokit/request-error": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", - "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz", + "integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==", "license": "MIT", "peer": true, "dependencies": { "@octokit/types": "^14.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/graphql/node_modules/@octokit/types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", - "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "license": "MIT", "peer": true, "dependencies": { - "@octokit/openapi-types": "^25.0.0" + "@octokit/openapi-types": "^25.1.0" } }, "node_modules/@octokit/graphql/node_modules/universal-user-agent": { @@ -494,9 +494,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.15.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz", - "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==", + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -2005,9 +2005,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.155", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", - "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==", + "version": "1.5.157", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.157.tgz", + "integrity": "sha512-/0ybgsQd1muo8QlnuTpKwtl0oX5YMlUGbm8xyqgDU00motRkKFFbUJySAQBWcY79rVqNLWIWa87BGVGClwAB2w==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -2333,9 +2333,9 @@ } }, "node_modules/fast-content-type-parse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", - "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", "funding": [ { "type": "github", From d4bccedf8ddad91354b8fde2ec9347d6698f38c5 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 26 May 2025 10:17:13 +0200 Subject: [PATCH 38/94] Update renovate to v40.31.0 (forgejo) (#7959) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index 9c98ba0dc6..1765f58a4e 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:40.26.0 + image: data.forgejo.org/renovate/renovate:40.31.0 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index 2b6cc4a543..6a1c19a23a 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.32.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.1 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@40.26.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@40.31.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From 40d678d4b952d874ba1da935511317f0b16501ec Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Mon, 26 May 2025 14:12:01 +0000 Subject: [PATCH 39/94] i18n: update of translations from Codeberg Translate Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Dirk Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Infernus Co-authored-by: Juno Takano Co-authored-by: Kita Ikuyo Co-authored-by: Miguel P.L Co-authored-by: Outbreak2096 Co-authored-by: SomeTr Co-authored-by: Wuzzy Co-authored-by: artnay Co-authored-by: hugoalh Co-authored-by: huskee Co-authored-by: justbispo Co-authored-by: kwoot Co-authored-by: milimarg Co-authored-by: nekoedges Co-authored-by: oscarotero Co-authored-by: smlxdesign Co-authored-by: tacaly Co-authored-by: xtex Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/da/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/de/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fi/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fil/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fr/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/gl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/lv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nds/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nl/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_BR/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_PT/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ru/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/zh_Hans/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/cs/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/el/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/es/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/fi/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/he/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/sv/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/ Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/zh_Hant/ Translation: Forgejo/forgejo Translation: Forgejo/forgejo-next --- options/locale/locale_cs-CZ.ini | 4 +- options/locale/locale_el-GR.ini | 20 +- options/locale/locale_es-ES.ini | 3 + options/locale/locale_fi-FI.ini | 1305 +++++++++++++++++-------- options/locale/locale_he.ini | 8 +- options/locale/locale_sv-SE.ini | 1 + options/locale/locale_uk-UA.ini | 1 + options/locale/locale_zh-TW.ini | 48 +- options/locale_next/locale_cs-CZ.json | 28 +- options/locale_next/locale_da.json | 22 +- options/locale_next/locale_de-DE.json | 28 +- options/locale_next/locale_fi-FI.json | 4 +- options/locale_next/locale_fil.json | 21 +- options/locale_next/locale_fr-FR.json | 26 +- options/locale_next/locale_gl.json | 40 +- options/locale_next/locale_lv-LV.json | 28 +- options/locale_next/locale_nds.json | 30 +- options/locale_next/locale_nl-NL.json | 58 +- options/locale_next/locale_pt-BR.json | 28 +- options/locale_next/locale_pt-PT.json | 30 +- options/locale_next/locale_ru-RU.json | 30 +- options/locale_next/locale_uk-UA.json | 30 +- options/locale_next/locale_zh-CN.json | 28 +- 23 files changed, 1344 insertions(+), 477 deletions(-) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 04c4da2f17..27baf20117 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -118,7 +118,7 @@ go_back=Zpět never=Nikdy unknown=Neznámý -rss_feed=RSS kanál +rss_feed=Kanál RSS pin=Připnout unpin=Odepnout @@ -3568,7 +3568,7 @@ notices.type=Typ notices.type_1=Repozitář notices.type_2=Úloha notices.desc=Popis -notices.op=Akce +notices.op=Op. notices.delete_success=Systémové upozornění bylo smazáno. dashboard.sync_repo_branches = Synchronizovat vynechané větve z dat Gitu do databáze monitor.queue.activeworkers = Aktivní workery diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 4b1f19e295..29085aebf1 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -112,7 +112,7 @@ preview=Προεπισκόπηση loading=Φόρτωση… error=Σφάλμα -error404=Η σελίδα που προσπαθείτε να φτάσετε είτε δεν υπάρχει είτε δεν είστε εξουσιοδοτημένοι για να την δείτε. +error404=Η σελίδα που προσπαθείτε να φτάσετε είτε δεν υπάρχει, έχει αφαιρεθεί είτε δεν είστε εξουσιοδοτημένοι για να την δείτε. go_back=Επιστροφή never=Ποτέ @@ -3941,29 +3941,29 @@ code_search_unavailable = Η αναζήτηση κώδικα δεν είναι keyword_search_unavailable = Η αναζήτηση με την χρήση λέξεων-κλειδιών δεν είναι επί του παρόντος διαθέσιμη. Παρακαλώ επικοινωνήστε με τον διαχειριστή σας. runner_kind = Αναζήτηση runner... code_search_by_git_grep = Για την αναζήτηση κώδικα, χρησιμοποιείται η εντολή «git grep». Ίσως να παρουσιαστούν καλύτερα αποτελέσματα, αν ο διαχειριστής σας ενεργοποιήσει ένα ευρετήριο για αποθετήρια («Repository Indexer»). -package_kind = Αναζήτηση πακέτων... +package_kind = Αναζήτηση πακέτων… project_kind = Αναζήτηση έργων... -branch_kind = Αναζήτηση κλάδων... +branch_kind = Αναζήτηση κλάδων… commit_kind = Αναζήτηση commit... no_results = Δεν βρέθηκαν κατάλληλα αποτελέσματα. -search = Αναζήτηση... +search = Αναζήτηση… type_tooltip = Είδος αναζήτησης fuzzy = Στο περίπου fuzzy_tooltip = Να συμπεριληφθούν αποτελέσματα που μοιάζουν με τον όρο αναζήτησης match = Ακριβής match_tooltip = Να συμπεριληφθούν αποτελέσματα που ταιριάζουν με τον όρο αναζήτησης -repo_kind = Αναζήτηση αποθετηρίων... -user_kind = Αναζήτηση χρηστών... -org_kind = Αναζήτηση οργανισμών... -team_kind = Αναζήτηση ομαδών... -code_kind = Αναζήτηση κώδικα... +repo_kind = Αναζήτηση αποθετηρίων… +user_kind = Αναζήτηση χρηστών… +org_kind = Αναζήτηση οργανισμών… +team_kind = Αναζήτηση ομαδών… +code_kind = Αναζήτηση κώδικα… exact_tooltip = Να συμπεριληφθούν μόνο αποτελέσματα που ταιριάζουν με τον όρο αναζήτησης issue_kind = Αναζήτηση ζητημάτων... pull_kind = Αναζήτηση pull... exact = Ακριβής milestone_kind = Αναζήτηση ορόσημων... union = Ένωση -union_tooltip = Να συμπεριληφθούν αποτελέσματα που περιέχουν οποιαδήποτε από τις λέξεις που έχουν εισαχθεί και διαχωριστεί με κενό +union_tooltip = Να συμπεριληφθούν αποτελέσματα που περιέχουν οποιαδήποτε από τις λέξεις χωρισμένες με κενό regexp = Κανονική Έκφραση regexp_tooltip = Ερμηνεία του όρου αναζήτησης ως κανονική έκφραση diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 63be50f3ce..f912409cc9 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -2886,6 +2886,7 @@ issues.filter_no_results_placeholder = Intenta ajustar tus filtros de búsqueda. pulls.delete_after_merge.head_branch.is_default = La rama actual que desea eliminar es la rama por defecto y no se puede eliminar. summary_card_alt = Tarjeta de resumen del repositorio %s settings.pull_mirror_sync_quota_exceeded = Cuota excedida, no se empujan los cambios. +archive.nocomment = No es posible hacer comentarios porque el repositorio está archivado. [graphs] component_loading = Cargando %s… @@ -3945,6 +3946,8 @@ runs.no_workflows = Aún no hay flujos de trabajo. workflow.dispatch.success = La ejecución del flujo de trabajo se ha solicitado correctamente. variables.not_found = No se ha encontrado la variable. workflow.dispatch.input_required = Se requiere valor para la entrada "%s". +workflow.dispatch.trigger_found = Este flujo de trabajo tiene un disparador de eventos workflow_dispatch. +workflow.dispatch.warn_input_limit = Sólo se muestran las primeras %d entradas. [projects] type-1.display_name=Proyecto individual diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 7fb667f6e2..af42ba24aa 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -34,9 +34,9 @@ twofa_scratch=Kaksivaiheinen kertakäyttöinen koodi passcode=Tunnuskoodi webauthn_insert_key=Aseta turva-avaimesi -webauthn_sign_in=Paina turva-avaimesi painiketta. Jos turva-avaimessasi ei ole painiketta, irroita se ja aseta uudelleen. +webauthn_sign_in=Paina turva-avaimesi painiketta. Jos turva-avaimessasi ei ole painiketta, irrota se ja aseta uudelleen. webauthn_press_button=Paina turva-avaimesi painiketta… -webauthn_use_twofa=Käytä kaksivaihesta vahvistusta puhelimestasi +webauthn_use_twofa=Käytä kaksivaihesta todennusta puhelimestasi webauthn_error=Turva-avainta ei voitu lukea. webauthn_unsupported_browser=Selaimesi ei tällä hetkellä tue WebAuthnia. webauthn_error_unknown=Tuntematon virhe. Yritä uudelleen. @@ -44,16 +44,16 @@ webauthn_error_insecure=`WebAuthn tukee vain suojattuja yhteyksiä. Testaukseen webauthn_error_unable_to_process=Palvelin ei pystynyt käsittelemään pyyntöä. webauthn_error_duplicated=Turva-avainta ei ole sallittu tässä pyynnössä. Varmista, ettei avainta ole jo rekisteröity. webauthn_error_empty=Sinun täytyy asettaa nimi tälle avaimelle. -webauthn_error_timeout=Aikakatkaisu saavutettu ennenkuin avaintasi on voitu lukea. Lataa tämä sivu uudelleen ja yritä uudelleen. +webauthn_error_timeout=Aikakatkaisu ennen kuin avaintasi voitiin lukea. Lataa tämä sivu uudelleen ja yritä uudelleen. webauthn_reload=Päivitä -repository=Repo +repository=Tietovarasto organization=Organisaatio mirror=Peili new_repo=Uusi repo new_migrate=Uusi migraatio new_mirror=Uusi peili -new_fork=Uusi tietovarastohaarukka +new_fork=Uusi tietovarastoforkki new_org=Uusi organisaatio new_project=Uusi projekti manage_org=Ylläpidä organisaatioita @@ -68,7 +68,7 @@ all=Kaikki sources=Lähteet mirrors=Peilit collaborative=Yhteistyössä -forks=Haarat +forks=Forkit activities=Toimet pull_requests=Vetopyynnöt @@ -98,7 +98,7 @@ preview=Esikatselu loading=Ladataan… error=Virhe -error404=Sivu, jolle yrität päästä, joko ei ole olemassa, on poistettu tai sinulla ei ole oikeutta tarkastella sitä. +error404=Sivu, jolle yrität päästä, ei joko ole olemassa, on poistettu tai sinulla ei ole oikeutta nähdä sitä. never=Ei koskaan @@ -108,13 +108,13 @@ rss_feed=RSS-syöte archived=Arkistoidut -concept_code_repository=Repo +concept_code_repository=Tietovarasto concept_user_organization=Organisaatio name=Nimi -enable_javascript = Tämä sivu vaatii Javascriptin. +enable_javascript = Tämä sivu vaatii JavaScriptin. new_project_column = Uusi sarake retry = Yritä uudelleen copy_type_unsupported = Tätä tiedostotyyppiä ei voi kopioida @@ -152,19 +152,19 @@ new_org.title = Uusi organisaatio new_org.link = Uusi organisaatio new_repo.link = Uusi tietovarasto new_migrate.link = Uusi migraatio -rerun_all = Uudelleensuorita kaikki työt -artifacts = Artifaktit -confirm_delete_artifact = Haluatko varmasti poistaa artifaktin "%s"? +rerun_all = Suorita uudelleen kaikki työt +artifacts = Artefaktit +confirm_delete_artifact = Haluatko varmasti poistaa artefaktin "%s"? new_migrate.title = Uusi migraatio test = Testi concept_system_global = Yleisesti pätevä sign_in_with_provider = Kirjaudu %s-tilillä filter.is_fork = Forkit -filter.is_mirror = Peilattu +filter.is_mirror = Peilit filter.is_template = Mallipohjat -filter.not_fork = Ei forkkeja +filter.not_fork = Ei forkit filter.not_template = Ei mallipohjat -filter.not_mirror = Ei peilattu +filter.not_mirror = Ei peilit copy_path = Kopioi polku concept_user_individual = Yksittäinen käyttäjä @@ -180,7 +180,7 @@ more = Enemmän number_of_contributions_in_the_last_12_months = %s kontribuutiota viimeisen vuoden aikana contributions_zero = Ei kontribuutioita contributions_one = kontribuutio -contributions_few = panokset +contributions_few = kontribuutiota contributions_format = {contributions} {day}. {month} {year} [editor] @@ -197,7 +197,7 @@ buttons.list.ordered.tooltip = Lisää numeroitu lista buttons.switch_to_legacy.tooltip = Käytä vanhentunutta tekstieditoria buttons.indent.tooltip = Sisennä yhden tason verran buttons.quote.tooltip = Aseta lainaustekstiksi -buttons.enable_monospace_font = Ota tasalevyinen kirjasin käyttöön +buttons.enable_monospace_font = Ota tasalevyinen fontti käyttöön buttons.ref.tooltip = Viittaa ongelmaa tai vetopyyntöä buttons.new_table.tooltip = Lisää taulukko table_modal.header = Lisää taulukko @@ -205,7 +205,7 @@ table_modal.placeholder.header = Otsikko table_modal.placeholder.content = Sisältö table_modal.label.rows = Rivit table_modal.label.columns = Sarakkeet -buttons.unindent.tooltip = Kumoa sisäkkäisyyden yhden tason verran +buttons.unindent.tooltip = Vähennä sisennystä yhden tason verran link_modal.header = Lisää linkki link_modal.url = Osoite link_modal.description = Kuvaus @@ -232,8 +232,8 @@ platform_desc=Forgejo on mahdollista suorittaa vapaissa käyttöjärjestelmissä lightweight=Kevyt lightweight_desc=Forgejolla on vähäiset vähimmäisvaatimukset, joten se toimii jopa halvassa Raspberry Pi:ssä. Säästä koneesi energiaa! license=Avoin lähdekoodi -license_desc=Mene osoitteeseen Forgejo! Liity mukaan tekemään projektista entistäkin parempi. Älä ujostele avustamista! -install_desc = Yksinkertaisesti suorita alustasi binääritiedosto, lähetä se Dockerin kanssa, tai pakkaa se. +license_desc=Mene ja lataa Forgejo! Liity tekemään projektista entistäkin parempi. Älä ujostele avustamista! +install_desc = Suorita alustallesi sopiva binääritiedosto, kontita se, tai hanki se paketoituna. [install] install=Asennus @@ -251,22 +251,22 @@ ssl_mode=SSL path=Polku sqlite_helper=SQLite3-tietokannan tiedostopolku.
    Syötä absoluuttinen polku, jos ajat Forgejoa palveluna. reinstall_error=Yrität asentaa olemassa olevaan Forgejo-tietokantaan -reinstall_confirm_message=Asentaminen uudelleen olemassa olevalla Forgejo-tietokannalla voi aiheuttaa useita ongelmia. Useimmissa tapauksissa sinun pitäisi käyttää olemassa olevia "app.ini" asetuksia Forgejon käyttöön. Jos tiedät mitä teet, vahvista seuraavat seikat: -reinstall_confirm_check_1=Tiedot, jotka on salattu SECRET_KEY:llä app.ini:ssä saatetaan menettää: käyttäjät eivät ehkä voi kirjautua sisään 2FA/OTP:lla ja peilit eivät välttämättä toimi oikein. Ruksaamalla tämän vahvistat, että nykyinen app.ini -tiedosto sisältää oikean SECRET_KEY:n. -reinstall_confirm_check_2=Repot ja asetukset saattaa olla tarpeen uudelleensynkronoida. Valitsemalla tämän vahvistat, että uudelleensynkronoit repojen koukut ja authorized_keys -tiedoston manuaalisesti. Varmistat, että repon ja peilin asetukset ovat oikeat. -reinstall_confirm_check_3=Vahvistat, että olet täysin varma siitä, että tämä Forgejo toimii oikealla app.ini sijainnilla ja että olet varma, että sinun täytyy asentaa uudelleen. Vahvistat, että tunnustat edellä mainitut riskit. +reinstall_confirm_message=Asentaminen uudelleen olemassa olevalla Forgejo-tietokannalla voi aiheuttaa useita ongelmia. Useimmissa tapauksissa sinun pitäisi käyttää olemassa olevia "app.ini"-asetuksia Forgejon suorittamiseksi. Jos tiedät mitä teet, vahvista seuraavat seikat: +reinstall_confirm_check_1=Tiedot, jotka on salattu SECRET_KEY:llä app.ini:ssä saatetaan menettää: käyttäjät eivät ehkä voi kirjautua sisään 2FA/OTP:lla ja peilit eivät välttämättä toimi oikein. Ruksaamalla tämän vahvistat, että nykyinen app.ini-tiedosto sisältää oikean SECRET_KEY:n. +reinstall_confirm_check_2=Tietovarastot ja asetukset saattaa olla tarpeen synkronoida uudelleen. Valitsemalla tämän vahvistat, että synkronoit uudelleen tietovarastojen koukut ja authorized_keys-tiedoston manuaalisesti. Varmistat, että tietovaraston ja peilin asetukset ovat oikeat. +reinstall_confirm_check_3=Vahvistat, että olet täysin varma siitä, että tämä Forgejo toimii oikealla app.ini-sijainnilla ja että olet varma, että sinun täytyy asentaa uudelleen. Vahvistat, että tunnustat edellä mainitut riskit. err_empty_db_path=SQLite3-tietokannan polku ei voi olla tyhjä. no_admin_and_disable_registration=Et voi kytkeä rekisteröintiä pois luomatta sitä ennen ylläpitotiliä. err_empty_admin_password=Ylläpitäjän salasana ei voi olla tyhjä. err_empty_admin_email=Ylläpitäjän sähköpostiosoite ei voi olla tyhjä. -err_admin_name_is_reserved=`Ylläpitäjän käyttäjätunnus on virheellinen; käyttäjätunnus on varattu` +err_admin_name_is_reserved=Ylläpitäjän käyttäjätunnus on virheellinen; käyttäjätunnus on varattu err_admin_name_is_invalid=Ylläpitäjän käyttäjätunnus on virheellinen general_title=Yleiset asetukset -app_name=Ilmentymän otsikko -app_name_helper=Syötä ilmentymän nimi tähän. Se näytetään kaikilla sivuilla. +app_name=Instanssin otsikko +app_name_helper=Syötä instanssin nimi tähän. Se näytetään kaikilla sivuilla. repo_path=Tietovaraston juuren polku -repo_path_helper=Muualla olevat git-repositoriot tullaan tallentamaan tähän kansioon. +repo_path_helper=Muualla olevat git-tietovarastot tullaan tallentamaan tähän kansioon. lfs_path=Git LFS -juuripolku lfs_path_helper=Git LFS:n ylläpitämät tiedostot tullaan tallentamaan tähän hakemistoon. Jätä tyhjäksi kytkeäksesi toiminnon pois. run_user=Aja käyttäjänä @@ -276,7 +276,7 @@ ssh_port_helper=Porttinumero, jossa SSH-palvelimesi kuuntelee. Jätä tyhjäksi http_port=HTTP-kuunteluportti http_port_helper=Forgejo-verkkopalvelimen käyttämä porttinumero. app_url=Juuriosoite -app_url_helper=Juuriosoite HTTP(S)-klooniosoitteille ja sähköpostimuistutuksille. +app_url_helper=Juuriosoite HTTP(S)-klooniosoitteille ja sähköposti-ilmoituksille. log_root_path=Lokitiedostojen polku log_root_path_helper=Lokitiedostot kirjoitetaan tähän kansioon. @@ -285,7 +285,7 @@ email_title=Sähköpostiasetukset smtp_addr=SMTP-isäntä smtp_port=SMTP-portti smtp_from=Lähetä sähköpostit osoitteella -smtp_from_helper=Sähköpostiosoite, jota Forgejo käyttää. Kirjoita osoite ”nimi” -muodossa. +smtp_from_helper=Sähköpostiosoite, jota Forgejo käyttää. Kirjoita pelkkä sähköpostiosoite tai "Nimi” -muodossa. mailer_user=SMTP-käyttäjätunnus mailer_password=SMTP-salasana register_confirm=Vaadi sähköpostinvahvistus rekisteröinnin edellytykseksi @@ -294,12 +294,12 @@ server_service_title=Palvelimen ja kolmansien osapuolten palveluiden asetukset offline_mode=Ota paikallinen tila käyttöön offline_mode.description=Poista kolmannen osapuolen sisällönjakeluverkot ja tarjoa kaikki resurssit paikallisesti. disable_gravatar=Poista Gravatar käytöstä -disable_gravatar.description=Poista Gravatar- tai muiden kolmansien osapuolien avatar-lähteet käytöstä. Oletuskuvia käytetään käyttäjien avatareissa, elleivät he uloslataa omaa avatariaan ilmentymään. +disable_gravatar.description=Poista Gravatar- tai muiden kolmansien osapuolien avatar-lähteet käytöstä. Oletuskuvia käytetään käyttäjien avatareissa, elleivät käyttäjät lähetä omaa avatariaan Forgejo-instanssiin. federated_avatar_lookup=Käytä federoituja profiilikuvia federated_avatar_lookup.description=Käytä Libravatar-palvelua profiilikuvien hakemiseen. disable_registration=Poista itserekisteröinti käytöstä -disable_registration.description=Vain järjestelmänvalvojat voivat luoda uusia käyttäjiä. On suositeltavaa pitää rekisteröinti suljettuna mikäli kyseessä ei ole julkinen instanssi jota varten tarvitsee hallinnoida suuria määriä roskapostikäyttäjiä. -allow_only_external_registration.description=Käyttäjät voivat luoda uusia käyttäjiä vain erikseen konfiguroituja ulkoisia palveluja käyttäen. +disable_registration.description=Vain ylläpitäjät voivat luoda uusia käyttäjiä. On suositeltavaa pitää rekisteröinti suljettuna, jos kyseessä ei ole julkinen instanssi, ja et olet valmis hallinnoimaan suuria määriä mahdollisia bottikäyttäjiä. +allow_only_external_registration.description=Käyttäjät voivat luoda uusia tilejä vain erikseen määritettyjä ulkoisia palveluja käyttäen. openid_signin=Ota OpenID-kirjautuminen käyttöön openid_signin.description=Salli OpenID:n kautta kirjautuminen. openid_signup=Ota OpenID-itserekisteröinti käyttöön @@ -307,9 +307,9 @@ openid_signup.description=Salli OpenID:n kautta rekisteröinti mikäli itserekis enable_captcha=Ota käyttöön CAPTCHA rekisteröityessä enable_captcha.description=Vaadi CAPTCHA rekisteröinnin yhteydessä. require_sign_in_view=Vaadi sisäänkirjautuminen sisällön näkemiseksi -admin_setting.description=Järjestelmänvalvojan tilin luominen on valinnaista. Ensimmäisestä rekisteröityneestä käyttäjästä tulee automaattisesti järjestelmänvalvoja. -admin_title=Järjestelmänvalvojan tilin asetukset -admin_name=Järjestelmänvalvojan käyttäjänimi +admin_setting.description=Ylläpitotilin luominen on valinnaista. Ensimmäisestä rekisteröityneestä käyttäjästä tulee automaattisesti ylläpitäjä. +admin_title=Ylläpitotilin asetukset +admin_name=Ylläpitäjän käyttäjänimi admin_password=Salasana confirm_password=Varmista salasana admin_email=Sähköpostiosoite @@ -328,44 +328,44 @@ default_enable_timetracking.description=Salli ajanseuranta-ominaisuuden käyttö no_reply_address=Piilotetun sähköpostin toimialue no_reply_address_helper=Verkkotunnuksen nimi käyttäjille, joilla on piilotettu sähköpostiosoite. Esimerkiksi käyttäjätunnus 'joe' kirjataan Git-palveluun nimellä 'joe@noreply.example.org' jos piilotetun sähköpostiosoitteen arvoksi on asetettu 'noreply.example.org'. password_algorithm=Salasanan hajautusalgoritmi -enable_update_checker_helper_forgejo = Se tarkistaa tietyin väliajoin uusia Forgejo-versioita tutkimalla sen TXT DNS record -tietoja osoitteesta release.forgejo.org . -invalid_admin_setting = Järjestelmänvalvojatilin asetukset eivät kelpaa: %v +enable_update_checker_helper_forgejo = Se tarkistaa väliajoin uusia Forgejo-versioita tutkimalla TXT DNS -tietueen osoitteesta release.forgejo.org . +invalid_admin_setting = Ylläpitotilin asetukset eivät kelpaa: %v env_config_keys = Ympäristökonfiguraatio run_user_helper = Käyttöjärjestelmätason käyttäjänimi, jona Forgejo ajetaan. Huomaa, että kyseinen käyttäjän on oltava pääsy tietovaraston juuripolkuun. -env_config_keys_prompt = Seuraavat ympäristömuuttujat sisällytetään myös asetustiedostoonne: +env_config_keys_prompt = Seuraavat ympäristömuuttujat sisällytetään myös asetustiedostoon: secret_key_failed = Salausavaimen generointi epäonnistui: %v default_allow_create_organization.description = Salli organisaatioiden luonti uusille käyttäjille oletuksena. Järjestelmänvalvojan tarvitsee antaa lupa luoda organisaatioita mikäli tämä asetus on pois päältä. config_location_hint = Tallennettujen asetusten sijainti: invalid_db_table = Tietokantataulu "%s" ei kelpaa: %v -invalid_password_algorithm = Salasananhajautusalgoritmi ei kelpaa -password_algorithm_helper = Aseta salasananhajautusalgoritmi. Eri algoritmeilla on erilaisia vaatimuksia ja vahvuuksia - argon2-algoritmi on erityisen turvallinen mutta vaatii paljon muistia ja voi näin ollen olla pienille järjestelmille soveltumaton. +invalid_password_algorithm = Salasanan hajautusalgoritmi on virheellinen +password_algorithm_helper = Aseta salasanan hajautusalgoritmi. Eri algoritmeilla on erilaisia vaatimuksia ja vahvuuksia. Argon2-algoritmi on erityisen turvallinen, mutta vaatii paljon muistia ja voi siten olla pienille järjestelmille soveltumaton. db_schema_helper = Jätä tyhjäksi käyttääksesi oletusarvoa ("public"). run_user_not_match = Tämänhetkinen käyttäjänimi ei täsmää tiettynä käyttäjänä ajettavan käyttäjänimen kanssa: %s -> %s invalid_log_root_path = Lokitiedoston polku ei kelpaa: %v require_sign_in_view.description = Rajoita sisältö vain kirjautuneille. Vieraat pääsevät vain autentikaatiosivuille. allow_only_external_registration = Salli rekisteröinti vain ulkoisia palveluja käyttäen default_allow_create_organization = Salli organisaatioiden luonti oletuksena -allow_dots_in_usernames = Salli pisteiden käyttö käyttäjänimissä. Ei vaikuta olemassaoleviin käyttäjiin. +allow_dots_in_usernames = Salli pisteiden käyttö käyttäjänimissä. Ei vaikuta olemassa oleviin käyttäjiin. enable_update_checker = Ota päivitystentarkistus käyttöön -app_slogan = Ilmentymän tunnuslause -app_slogan_helper = Syötä ilmentymän tunnuslause tähän. Jätä tyhjäksi poistaaksesi käytöstä. +app_slogan = Instanssin tunnuslause +app_slogan_helper = Syötä instanssin tunnuslause tähän. Jätä tyhjäksi poistaaksesi käytöstä. domain_helper = Palvelimen toimialue tai isäntänimi. -smtp_from_invalid = "Lähetä sähköpostit osoitteella" -osoite on epäkelvollinen -err_admin_name_pattern_not_allowed = Järjestelmänvalvojan käyttäjänimi on epäkelvollinen, se vastaa varattua kaavaa +smtp_from_invalid = "Lähetä sähköpostit osoitteella"-osoite on epäkelvollinen +err_admin_name_pattern_not_allowed = Ylläpitäjän käyttäjänimi on epäkelpo, se vastaa varattua kaavaa [home] uname_holder=Käyttäjätunnus tai sähköpostiosoite password_holder=Salasana switch_dashboard_context=Vaihda kojelaudan kontekstia -my_repos=Repot +my_repos=Tietovarastot show_more_repos=Näytä lisää repoja… collaborative_repos=Yhteistyö repot -my_orgs=Organisaationi +my_orgs=Organisaatiot my_mirrors=Peilini view_home=Näytä %s search_repos=Etsi repo… filter=Muut suodattimet -filter_by_team_repositories=Suodata tiimin repojen mukaan +filter_by_team_repositories=Suodata tiimin tietovarastojen mukaan feed_of=`Syöte "%s"` show_archived=Arkistoidut @@ -378,10 +378,10 @@ show_both_private_public=Näytetään sekä julkiset että yksityiset show_only_private=Näytetään vain yksityiset show_only_public=Näytetään vain julkiset -issues.in_your_repos=Repoissasi +issues.in_your_repos=Tietovarastoissasi [explore] -repos=Repot +repos=Tietovarastot users=Käyttäjät organizations=Organisaatiot search=Hae @@ -395,38 +395,38 @@ code_last_indexed_at=Viimeksi indeksoitu %s stars_one = %d tähti stars_few = %d tähteä relevant_repositories = Vain asiaankuuluvat tietovarastot näytetään, näytä suodattamattomat tulokset. -forks_one = %d haarukka -forks_few = %d haarukkaa +forks_one = %d forkki +forks_few = %d forkkia go_to = Siirry -relevant_repositories_tooltip = Tietovarastot, jotka ovat haarukoita tai joilla ei ole aihetta, kuvaketta tai kuvausta, piilotetaan. +relevant_repositories_tooltip = Tietovarastot, jotka ovat forkkeja tai joilla ei ole aihetta, kuvaketta tai kuvausta, piilotetaan. [auth] create_new_account=Rekisteröi tili register_helper_msg=On jo tili? Kirjaudu sisään nyt! social_register_helper_msg=Onko sinulla jo tili? Linkitä se nyt! -disable_register_prompt=Rekisteröinti on estetty. Ota yhteys ylläpitäjääsi. +disable_register_prompt=Rekisteröinti on estetty. Ota yhteys sivuston ylläpitäjään. disable_register_mail=Sähköpostivahvistus rekisteröinnille on estetty. remember_me=Muista tämä laite forgot_password_title=Unohtuiko salasana forgot_password=Unohtuiko salasana? sign_up_now=Tarvitsetko tilin? Rekisteröidy nyt. -confirmation_mail_sent_prompt=Uusi varmistussähköposti on lähetetty osoitteeseen %s. Tarkista sähköpostisi ja seuraa saamaasi linkkiä seuraavan %s aikana saadaksesi rekisteröintiprosessin valmiiksi. Mikäli annettu sähköpostiosoite on väärin, voit kirjautua sisään ja pyytää uutta varmistussähköpostia toiseen osoitteeseen. +confirmation_mail_sent_prompt=Uusi varmistussähköposti on lähetetty osoitteeseen %s. Tarkista sähköpostisi ja seuraa saamaasi linkkiä seuraavan %s aikana viimeistelläksesi rekisteröinnin. Mikäli annettu sähköpostiosoite on väärin, voit kirjautua sisään ja pyytää uutta varmistussähköpostia toiseen osoitteeseen. must_change_password=Vaihda salasanasi allow_password_change=Vaadi käyttäjää vaihtamaan salasanansa (suositeltava) reset_password_mail_sent_prompt=Varmistussähköposti on lähetetty osoitteeseen %s. Tarkista sähköpostisi ja seuraa annettua linkkiä seuraavan %s aikana saadaksesi tilin palauttamisen valmiiksi. active_your_account=Aktivoi tilisi account_activated=Tili on aktivoitu prohibit_login=Tili on jäädytetty -resent_limit_prompt=Olet jo tilannut aktivointisähköpostin hetki sitten. Ole hyvä ja odota 3 minuuttia ja yritä sitten uudelleen. -has_unconfirmed_mail=Hei %s, sinulla on varmistamaton sähköposti osoite (%s). Jos et ole saanut varmistus sähköpostia tai tarvitset uudelleenlähetyksen, ole hyvä ja klikkaa allaolevaa painiketta. -resend_mail=Klikkaa tästä uudelleenlähettääksesi aktivointi sähköpostisi +resent_limit_prompt=Olet jo tilannut aktivointisähköpostin äskettäin. Odota kolme minuuttia ja yritä sitten uudelleen. +has_unconfirmed_mail=Hei %s, sinulla on vahvistamaton sähköpostiosoite (%s). Jos et ole saanut vahvistussähköpostia tai tarvitset uudelleenlähetyksen, ole hyvä ja napsauta alla olevaa painiketta. +resend_mail=Napsauta tästä lähettääksesi aktivointisähköpostin uudelleen email_not_associate=Tätä sähköpostiosoitetta ei ole liitetty mihinkään tiliin. send_reset_mail=Lähetä palautussähköposti reset_password=Tilin palautus -invalid_code=Vahvistusavain on virheellinen tai vanhentunut. +invalid_code=Vahvistuskoodi on virheellinen tai vanhentunut. reset_password_helper=Palauta käyttäjätili password_too_short=Salasanan pituus ei voi olla vähemmän kuin %d merkkiä. -non_local_account=Ei-lokaalit käyttäjät eivät voi päivittää salasanojaan Forgejon web-käyttöliittymän kautta. +non_local_account=Muut kuin lokaalit käyttäjät eivät voi päivittää salasanojaan Forgejo-selainkäyttöliittymän kautta. verify=Vahvista scratch_code=Kertakäyttökoodi use_scratch_code=Käytä kertakäyttökoodia @@ -442,24 +442,24 @@ oauth_signin_tab=Linkitä olemassa olevaan tiliin oauth_signin_title=Kirjaudu sisään valtuuttaaksesi linkitetyn tilin oauth_signin_submit=Yhdistä tiliin oauth.signin.error.access_denied=Valtuutuspyyntö on evätty. -openid_connect_submit=Connect -openid_connect_title=Yhdistä olemassaolevaan tiliin +openid_connect_submit=Yhdistä +openid_connect_title=Yhdistä olemassa olevaan tiliin openid_connect_desc=Valittu OpenID-osoite on tuntematon. Liitä se uuteen tiliin täällä. openid_register_title=Luo uusi tili openid_register_desc=Valittu OpenID-osoite on tuntematon. Liitä se uuteen tiliin täällä. email_domain_blacklisted=Et voi rekisteröityä sähköpostiosoittellasi. authorize_application=Valtuuta sovellus authorize_redirect_notice=Sinut uudelleen ohjataan osoitteeseen %s jos valtuutat tämän sovelluksen. -authorize_application_created_by=Tämän sovelluksen on luonnut %s. +authorize_application_created_by=Tämän sovelluksen on luonut %s. authorize_application_description=Jos myönnät valtuuden, se pystyy pääsemään kaikkiin tilitietoihisi ja kirjoittamaan niihin, mukaan lukien yksityiset tietovarastot ja organisaatiot. authorize_title=Valtuutatko "%s" pääsemään tilillesi? -authorization_failed=Käyttöoikeuden varmistus epäonnistui +authorization_failed=Valtuuttaminen epäonnistui sspi_auth_failed=SSPI todennus epäonnistui sign_up_successful = Käyttäjätili luotiin onnistuneesti. Tervetuloa! hint_login = Onko sinulla jo käyttäjätili? Kirjaudu sisään! hint_register = Tarvitsetko käyttäjätilin? Rekisteröidy nyt. sign_up_button = Rekisteröidy nyt. -manual_activation_only = Ota yhteyttä järjestelmänvalvojaanne viimeistelläksesi aktivoinnin. +manual_activation_only = Ota yhteys sivuston ylläpitoon viimeistelläksesi aktivoinnin. change_unconfirmed_email_error = Sähköpostiosoitteen vaihtaminen ei onnistu: %v invalid_password = Salasanasi ei vastaa tilin luomisen yhteydessä käytettyä salasanaa. back_to_sign_in = Takaisin kirjautumiseen @@ -471,15 +471,15 @@ change_unconfirmed_email_summary = Vaihda sähköpostiosoite, johon aktivointis reset_password_wrong_user = Olet kirjautuneena tilillä %s, mutta tilin palautuslinkki on tarkoitettu tilille %s last_admin = Et voi poistaa viimeistä ylläpitäjää. Ylläpitäjiä tulee olla vähintään yksi. password_pwned = Valitsemasi salasana on varastettujen salasanojen listalla, eli se on paljastanut jossain julkisessa tietovuodossa. Kokeile asettaa eri salasana, ja jos käytät samaa salasanaa muissa palveluissa, vaihda kyseinen salasana. -use_onetime_code = Käytä kertakäyttöiskoodia -unauthorized_credentials = Valtuustiedot ovat epäkelvolliset tai umpeutuneet. Yritä komentoa uudelleen tai katso lisätietoja kohdasta %s +use_onetime_code = Käytä kertakäyttökoodia +unauthorized_credentials = Kirjautumistiedot ovat virheelliset tai vanhentuneet. Yritä suorittaa komento uudelleen tai katso %s saadaksesi lisätietoja oauth.signin.error.temporarily_unavailable = Valtuus epäonnistui, koska todennuspalvelin ei ole tällä hetkellä käytettävissä. Yritä uudelleen myöhemmin. disable_forgot_password_mail = Tilin palautus ei ole käytössä, koska sähköpostia ei ole määritetty. Ota yhteys sivuston ylläpitoon. password_pwned_err = Pyyntöä HaveIBeenPwned-palveluun ei voitu suorittaa authorization_failed_desc = Valtuus epäonnistui, koska havaitsimme virheellisen pyynnön. Ota yhteys sen sovelluksen ylläpitäjään, jota yritit valtuuttaa. -oauth.signin.error = Valtuutuspyynnön käsittelyssä tapahtui virhe. Jos tämä virhe jatkuu, ota yhteyttä sivuston järjestelmänvalvojaan. +oauth.signin.error = Valtuuspyynnön käsittelyssä tapahtui virhe. Jos virhe toistuu, ota yhteys sivuston ylläpitoon. disable_forgot_password_mail_admin = Tilin palautus on käytössä vain, jos sähköposti on määritetty. Aseta sähköposti, jotta tilin palauttaminen on mahdollista ottaa käyttöön. -prohibit_login_desc = Tilisi käyttö ilmentymän kanssa on estetty. Ota yhteyttä ilmentymän järjestelmänvalvojaan saadaksesi pääsyn takaisin. +prohibit_login_desc = Tilisi käyttö instanssin kanssa on estetty. Ota yhteyttä instanssin ylläpitoon saadaksesi pääsyn takaisin. [mail] view_it_on=Näytä %s @@ -500,8 +500,8 @@ register_success=Rekisteröinti onnistui issue.x_mentioned_you=@%s mainitsi sinut: -issue.action.push_1=@%[1]s työnsi %[3]d commitin kohteeseen %[2]s -issue.action.push_n=@%[1]s työnsi %[3]d committia kohteeseen %[2]s +issue.action.push_1=@%[1]s työnsi %[3]d kommitin kohteeseen %[2]s +issue.action.push_n=@%[1]s työnsi %[3]d kommittia kohteeseen %[2]s issue.action.reject=@%[1]s pyysi muutoksia tässä vetopyynnössä. release.title=Otsikko: %s @@ -540,25 +540,25 @@ admin.new_user.text = Napsauta tästä hallitaksesi tätä käy repo.collaborator.added.text = Sinut on lisätty avustajaksi tietovarastoon: primary_mail_change.text_1 = Tilisi ensisijaiseksi sähköpostiosoitteeksi asetettiin %[1]s. Se tarkoittaa, että tämä sähköpostiosoite ei enää vastaanota tilisi ilmoituksia sähköpostitse. team_invite.text_1 = %[1]s on kutsunut sinut liittymään tiimiin %[2]s organisaatiossa %[3]s. -issue_assigned.pull = @%[1]s osoitti sinulle vetopyynnön %[2]stietovarastossa %[3]s. -issue_assigned.issue = @%[1]s osoitti sinulle vianlipun %[2]s tietovarastossa %[3]s. +issue_assigned.pull = @%[1]s osoitti sinulle vetopyynnön %[2]s tietovarastossa %[3]s. +issue_assigned.issue = @%[1]s osoitti sinulle ongelman %[2]s tietovarastossa %[3]s. register_notify.text_1 = tämä on %s:n rekistöröitymisen vahvistussähköposti! -reset_password.text = jos tämä oli sinun toimestasi, ole hyvä ja klikkaa oheista linkkiä palauttaaksesi tilisi %s sisällä: -totp_disabled.no_2fa = Muita kaksivaiheisen tunnistautumisen menetelmiä ei ole konfiguroituna, joten et tarvitse kaksivaiheista tunnistautumista kirjautuaaksesi tilillesi. +reset_password.text = jos se olit sinä, napsauta oheista linkkiä palauttaaksesi tilisi %s sisällä: +totp_disabled.no_2fa = Muita kaksivaiheisen todennuksen menetelmiä ei ole määritetty, joten et enää tarvitse kaksivaiheista todennusta kirjautuaksesi tilillesi. totp_enrolled.subject = Olet aktivoinut TOTP:in kaksivaiheisen todennuksen menetelmäksi -totp_enrolled.text_1.no_webauthn = Otit TOTP:n käyttöön tilillesi. Tämä tarkoittaa, että kirjauduttaessa sisään tilillesi sinun täytyy käyttää TOTP menetelmää kaksivaiheisena tunnistautumisena. -team_invite.text_3 = Huomaa: Tämä kutsu on tarkoitettu %[1]s:lle. Jos et odottanut tätä kutsua, voit jättää tämän sähköpostin huomiotta. -removed_security_key.no_2fa = Yhtään kaksivaiheisen tunnistautumisen menetelmää ei ole määritelty, joten tilillesi ei enää tarvitse kirjautua kaksivaiheisella tunnistautumisella. -totp_enrolled.text_1.has_webauthn = Otit TOTP:n käyttöön tilillesi. Tämä tarkoittaa, että kirjauduttaessa sisään tilillesi voit käyttää TOTP menetelmää kaksivaiheisena tunnistautumisena tai mitä tahansa turva-avaimiasi. -repo.collaborator.added.subject = %s lisäsi sinut %s yhteistyökumppaniksi -release.new.text = @%[1]s julkaistu %[2]s %[3]s:ssa -issue.action.review_dismissed = @%[1]s hylkäsi %[2]s:n viimeisimmän arvion tälle vetopyynnölle. +totp_enrolled.text_1.no_webauthn = Otit TOTP:n käyttöön tilillesi. Tämä tarkoittaa, että kirjautuessasi tilillesi sinun täytyy käyttää TOTP-menetelmää kaksivaiheisena todennuksena. +team_invite.text_3 = Huomaa: Tämä kutsu on tarkoitettu käyttäjälle %[1]s. Jos et odottanut tätä kutsua, voit jättää tämän sähköpostin huomiotta. +removed_security_key.no_2fa = Yhtäkään kaksivaiheisen todennuksen menetelmää ei ole määritelty, joten tilillesi ei enää tarvitse kirjautua kaksivaiheisella todennuksella. +totp_enrolled.text_1.has_webauthn = Otit TOTP:n käyttöön tilillesi. Tämä tarkoittaa, että kirjautuessasi tilillesi voit käyttää TOTP-menetelmää kaksivaiheisena tunnistautumisena tai mitä tahansa turva-avaintasi. +repo.collaborator.added.subject = %s lisäsi sinut tietovaraston %s avustajaksi +release.new.text = @%[1]s julkaisi %[2]s projektissa %[3]s +issue.action.review_dismissed = @%[1]s hylkäsi viimeisimmän katselmoinnin taholta %[2]s tälle vetopyynnölle. release.new.subject = %s %s:ssa julkaistu issue.in_tree_path = %s:ssa: -issue.action.force_push = %[1]s pakkotyönnetty %[2]s %[3]s:sta %[4]s:hen. -issue.action.merge = @%[1]s yhdistetty #%[2]d %[3]s:hen. -repo.transfer.subject_to = %s haluaa siirtää tietovaraston "%s" %s:hen -repo.transfer.body = Hyväksy tai hylkää se käymällä %s:lla tai jätä se huomiotta. +issue.action.force_push = %[1]s pakkotyönsi %[2]s lähteestä %[3]s kohteeseen %[4]s:hen. +issue.action.merge = @%[1]s yhdisti kohteen #%[2]d kohteeseen %[3]s. +repo.transfer.subject_to = %s haluaa siirtää tietovaraston "%s" taholle %s +repo.transfer.body = Hyväksy tai hylkää se käymällä %s tai jätä se huomiotta. @@ -566,13 +566,13 @@ repo.transfer.body = Hyväksy tai hylkää se käymällä %s:lla tai jätä se h yes=Kyllä no=Ei cancel=Peruuta -modify=Päivitys +modify=Päivitä confirm = Vahvista [form] UserName=Käyttäjätunnus -RepoName=Repon nimi -Email=Sähköposti osoite +RepoName=Tietovaraston nimi +Email=Sähköpostiosoite Password=Salasana Retype=Vahvista salasana SSHTitle=SSH avain nimi @@ -582,9 +582,9 @@ AuthName=Luvan nimi AdminEmail=Ylläpitäjän sähköposti NewBranchName=Uuden haaran nimi -CommitSummary=Commitin yhteenveto -CommitMessage=Commitin viesti -CommitChoice=Commitin valinta +CommitSummary=Kommitin yhteenveto +CommitMessage=Kommitointiviesti +CommitChoice=Kommitin valinta TreeName=Tiedostopolku Content=Sisältö @@ -597,13 +597,13 @@ min_size_error=` täytyy sisältää vähintään %s merkkiä.` max_size_error=` täytyy sisältää enintään %s merkkiä.` email_error=` ei ole kelvollinen sähköpostiosoite.` unknown_error=Tuntematon virhe: -captcha_incorrect=CAPTCHA koodi on virheellinen. +captcha_incorrect=CAPTCHA-koodi on virheellinen. password_not_match=Salasanat eivät täsmää. lang_select_error=Valitse kieli listalta. username_been_taken=Käyttäjätunnus on jo varattu. -repo_name_been_taken=Repon nimi on jo käytössä. -repository_force_private=Pakotettu yksityisyys käytössä: yksityisiä repoja ei voida muuttaa julkisiksi. +repo_name_been_taken=Tietovaraston nimi on jo käytössä. +repository_force_private=Pakotettu yksityisyys käytössä: yksityisiä tietovarastoja ei voida muuttaa julkisiksi. org_name_been_taken=Organisaation nimi on jo käytössä. team_name_been_taken=Tiimin nimi on jo varattu. email_been_used=Sähköpostiosoite on jo käytössä. @@ -624,7 +624,7 @@ invalid_gpg_key=GPG-avaintasi ei voi vahvistaa: %s auth_failed=Todennus epäonnistui: %v -target_branch_not_exist=Kohde branchia ei ole olemassa. +target_branch_not_exist=Kohdehaaraa ei ole olemassa. Pronouns = Pronomini FullName = Koko nimi Description = Kuvaus @@ -634,7 +634,7 @@ Location = Sijainti To = Haaran nimi still_own_repo = Tilisi omistaa yhden tai useamman tietovaraston; poista tai siirrä ne ensin. organization_leave_success = Olet poistunut organisaatiosta %s. -enterred_invalid_repo_name = Syöttämäsi tietovaraston nimi on epäkelvollinen. +enterred_invalid_repo_name = Syöttämäsi tietovaraston nimi on epäkelpo. openid_been_used = OpenID-osoite "%s" on jo käytetty. password_complexity = Salasana ei täytä monimutkaisuusvaatimuksia: still_has_org = Tilisi on jäsen yhdessä tai useamassa organisaatiossa, poistu niistä ensin. @@ -654,11 +654,20 @@ regex_pattern_error = `regex-kuvio on epäkelvollinen: %s.` username_error_no_dots = `voi sisältää vain aakkosnumeerisia merkkejä ("0–9", "a–z", "A–Z"), yhdysviivaa ("-") ja alaviivaa ("_"). Se ei voi alkaa tai päättyä muihin kuin aakkosnumeerisiin merkkeihin, ja peräkkäiset muut kuin aakkosnumeeriset merkit ovat myös kiellettyjä.` PayloadUrl = Hyötykuorman URL-osoite repository_files_already_exist.adopt = Tälle tietovarastolle on jo olemassa tiedostoja, ja ne voidaan vain omaksua. +repository_files_already_exist = Tässä tietovarastossa on jo tiedostoja. Ota yhteys järjestelmän ylläpitäjään. +last_org_owner = Et voi poistaa viimeistä käyttäjää "owners"-tiimistä. Organisaatiolla tulee olla vähintään yksi omistaja. +required_prefix = Syötteen tulee alkaa "%s" +repository_files_already_exist.delete = Tässä tietovarastossa on jo tiedostoja. Poista ne. +duplicate_invite_to_team = Käyttäjä oli jo kutsuttu tiimin jäseneksi. +org_still_own_repo = Organisaatio omistaa yhden tai useamman tietovaraston. Poista tai siirrä ne ensin. +org_still_own_packages = Organisaatio omistaa yhden tai useamman paketin. Poista ne ensin. +team_no_units_error = Salli pääsy vähintään yhteen tietovaraston osioon. +repository_files_already_exist.adopt_or_delete = Tässä tietovarastossa on jo tiedostoja. Omaksu ne itsellesi tai poista ne. [user] change_avatar=Vaihda profiilikuvasi… -repositories=Repot +repositories=Tietovarastot activity=Julkinen toiminta followers_few=%d seuraajaa starred=Tähdelliset tietovarastot @@ -692,6 +701,11 @@ form.name_reserved = Käyttäjätunnus "%s" on varattu. form.name_pattern_not_allowed = Kaava "%s" ei ole sallittu käyttäjätunnuksessa. public_activity.visibility_hint.admin_private = Aktiivisuus on näkyvissä sinulle, koska olet ylläpitäjä, mutta käyttäjä haluaa pitää aktiivisuutensa yksityisenä. public_activity.visibility_hint.self_private_profile = Aktiivisuutesi on näkyvissä vain sinulle ja instanssin ylläpitäjille, koska profiilisi on yksityinen. Määritä. +watched = Tarkaillut tietovarastot +block_user.detail_2 = Tämä käyttäjä ei voi olla vuorovaikutuksessa omistamiesi tietovarastojen kanssa, tai luomiesi ongelmien ja kommenttien kanssa. +block_user.detail_3 = Ette voi lisätä toisianne tietovaraston avustajiksi. +block_user.detail_1 = Lopetatte toistenne seuraamisen, ettekä pysty enää seurata toisianne. +public_activity.visibility_hint.admin_public = Tämä aktiviteetti on näkyvissä kaikille, mutta ylläpitäjänä voit nähdä myös vuorovaikutukset yksityisissä tiloissa. [settings] @@ -705,7 +719,7 @@ ssh_gpg_keys=SSH-/GPG-avaimet social=Sosiaaliset tilit applications=Sovellukset orgs=Organisaatiot -repos=Repot +repos=Tietovarastot delete=Poista tili twofa=Kaksivaiheinen todennus (TOTP) account_link=Linkitetyt tilit @@ -738,8 +752,8 @@ comment_type_group_time_tracking=Ajanseuranta comment_type_group_deadline=Määräaika comment_type_group_dependency=Riippuvuus comment_type_group_lock=Lukituksen tila -comment_type_group_review_request=Arviointipyyntö -comment_type_group_pull_request_push=Lisätyt commitit +comment_type_group_review_request=Katselmointipyyntö +comment_type_group_pull_request_push=Lisätyt kommitit comment_type_group_project=Projekti saved_successfully=Asetuksesi tallennettiin onnistuneesti. privacy=Yksityisyys @@ -757,7 +771,7 @@ update_avatar_success=Profiilikuva on päivitetty. update_password=Päivitä salasana old_password=Nykyinen salasana new_password=Uusi salasana -password_incorrect=Nykyinen salasanasi on virheellinen. +password_incorrect=Nykyinen salasana on virheellinen. password_change_disabled=Ei-lokaalit käyttäjät eivät voi päivittää salasanojaan Forgejon web-käyttöliittymän kautta. emails=Sähköposti osoitteet @@ -768,12 +782,12 @@ theme_desc=Tätä teemaa käytetään verkkosivuston käyttöliittymässä, kun primary=Ensisijainen activated=Aktivoitu requires_activation=Vaatii aktivoinnin -primary_email=Tee ensisijainen +primary_email=Aseta ensisijaiseksi activate_email=Lähetä aktivointi activations_pending=Odottaa aktivointia delete_email=Poista email_deletion=Poista sähköpostiosoite -email_deletion_desc=Sähköpostiosoite ja siihen liittyvät tiedot poistetaan tililtäsi. Kyseisen sähköpostiosoitteen sisältävät commitit pysyvät muuttumattomia. Jatketaanko? +email_deletion_desc=Sähköpostiosoite ja siihen liittyvät tiedot poistetaan tililtäsi. Kyseisen sähköpostiosoitteen sisältävät kommitit pysyvät muuttumattomia. Jatketaanko? email_deletion_success=Sähköpostiosoite on poistettu. theme_update_success=Teemasi on päivitetty. theme_update_error=Valittua teemaa ei löydy. @@ -787,48 +801,48 @@ add_email_success=Uusi sähköpostiosoite on lisätty. email_preference_set_success=Sähköpostin asetukset on asetettu onnistuneesti. add_openid_success=Uusi OpenID-osoite on lisätty. keep_email_private=Piilota sähköpostiosoite -openid_desc=OpenID mahdollistaa todentamisen delegoinnin ulkopuoliselle palvelun tarjoajalle. +openid_desc=OpenID mahdollistaa todentamisen delegoinnin ulkopuoliselle palveluntarjoajalle. manage_ssh_keys=Hallitse SSH-avaimia manage_gpg_keys=Hallitse GPG-avaimia add_key=Lisää avain -ssh_desc=Nämä julkiset SSH-avaimet on liitetty tiliisi. Vastaavat yksityiset avaimet antavat täyden pääsyn tietovarastoihisi. Vahvistettuja SSH-avaimia voidaan käyttää SSH-allekirjoitettujen Git-sitoumusten varmentamiseen. -gpg_desc=Nämä julkiset GPG-avaimet liitetään tiliisi ja niitä käytetään sitoumuksesi vahvistamiseen. Pidä yksityiset avaimesi turvassa, koska niiden avulla voit allekirjoittaa sitoumuksia henkilöllisyytesi kanssa. +ssh_desc=Nämä julkiset SSH-avaimet on liitetty tiliisi. Vastaavat yksityiset avaimet antavat täyden pääsyn tietovarastoihisi. Vahvistettuja SSH-avaimia voidaan käyttää SSH-allekirjoitettujen Git-kommittien varmentamiseen. +gpg_desc=Nämä julkiset GPG-avaimet liitetään tiliisi ja niitä käytetään tekemiesi kommittien vahvistamiseen. Pidä yksityiset avaimesi turvassa, koska niiden avulla voit allekirjoittaa kommitteja henkilöllisyytesi kanssa. ssh_helper=Tarvitsetko apua? Tutustu GitHubin oppaaseen omien SSH-avainten luonnista tai yleisistä ongelmista, joita voit kohdata SSH:n kanssa. gpg_helper=Tarvitsetko apua? Katso GitHubin opas GPG:stä. add_new_key=Lisää SSH avain add_new_gpg_key=Lisää GPG-avain key_content_ssh_placeholder=Alkaa sanoilla "ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "sk-ecdsa-sha2-nistp256@openssh.com" tai "sk-ssh-ed25519@openssh.com" key_content_gpg_placeholder=Alkaa sanoilla "-----BEGIN PGP PUBLIC KEY BLOCK-----" -ssh_key_name_used=Samanniminen SSH avain on jo olemassa tililläsi. -gpg_key_id_used=Julkinen GPG-avain samalla tunnuksella on jo olemassa. -gpg_no_key_email_found=Tämä GPG-avain ei vastaa mitään tiliisi liitettyä aktivoitua sähköpostiosoitetta. Se voidaan silti lisätä, jos allekirjoitat annetun pääsymerkin. +ssh_key_name_used=SSH-avain samalla nimellä on jo olemassa tililläsi. +gpg_key_id_used=Julkinen GPG-avain samalla ID-tunnisteella on jo olemassa. +gpg_no_key_email_found=Tämä GPG-avain ei vastaa mitään tiliisi liitettyä aktivoitua sähköpostiosoitetta. Se voidaan silti lisätä, jos allekirjoitat annetun pääsypoletin. gpg_key_verified=Vahvistettu avain -gpg_key_verified_long=Avain on vahvistettu pääsymerkillä ja sitä voidaan käyttää todentamaan commitit, jotka vastaavat tämän käyttäjän aktivoituja sähköpostiosoitteita tämän avaimen kaikkien vastaavien identiteettien lisäksi. +gpg_key_verified_long=Avain on vahvistettu poletilla ja sitä voidaan käyttää vahvistamaan kommitit, jotka vastaavat tämän käyttäjän aktivoituja sähköpostiosoitteita tämän avaimen kaikkien vastaavien identiteettien lisäksi. gpg_key_verify=Vahvista -gpg_token_required=Sinun täytyy antaa allekirjoitus alla olevalle pääsymerkille -gpg_token=Pääsymerkki +gpg_token_required=Sinun täytyy antaa allekirjoitus alla olevalle poletille +gpg_token=Poletti gpg_token_help=Voit luoda allekirjoituksen käyttäen: gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=Panssaroitu GPG-allekirjoitus key_signature_gpg_placeholder=Alkaa sanoilla "-----BEGIN PGP SIGNATURE-----" ssh_key_verified=Vahvistettu avain -ssh_key_verified_long=Avain on vahvistettu pääsymerkillä ja sitä voidaan käyttää todentamaan commitit, jotka vastaavat tämän käyttäjän aktivoituja sähköpostiosoitteita. +ssh_key_verified_long=Avain on vahvistettu poletilla ja sitä voidaan käyttää vahvistamaan kommitit, jotka vastaavat tämän käyttäjän aktivoituja sähköpostiosoitteita. ssh_key_verify=Vahvista -ssh_token_required=Sinun täytyy antaa allekirjoitus alla olevalle pääsymerkille -ssh_token=Pääsymerkki +ssh_token_required=Sinun täytyy antaa allekirjoitus alla olevalle poletille +ssh_token=Poletti ssh_token_help=Voit luoda allekirjoituksen käyttäen: ssh_token_signature=Panssaroitu SSH-allekirjoitus key_signature_ssh_placeholder=Alkaa sanoilla "-----BEGIN SSH SIGNATURE-----" subkeys=Aliavaimet -key_id=Avain ID +key_id=Avain-ID key_name=Avaimen nimi key_content=Sisältö principal_content=Sisältö delete_key=Poista ssh_key_deletion=Poista SSH-avain gpg_key_deletion=Poista GPG-avain -gpg_key_deletion_desc=GPG-avaimen poistaminen peruuttaa sillä allekirjoitettujen committien vahvistukset. Jatketaanko? +gpg_key_deletion_desc=GPG-avaimen poistaminen peruuttaa sillä allekirjoitettujen kommittien vahvistukset. Jatketaanko? gpg_key_deletion_success=GPG-avain on poistettu. valid_forever=Voimassa ikuisesti last_used=Käytetty viimeksi @@ -840,16 +854,16 @@ hide_openid=Piilota profiilista ssh_disabled=SSH on pois käytöstä manage_social=Hallitse liitettyjä sosiaalisia tilejä -manage_access_token=Hallitse pääsymerkkejä -generate_new_token=Luo uusi pääsymerkki -token_name=Pääsymerkin nimi -generate_token=Luo pääsymerkki -generate_token_success=Uusi pääsymerkkisi on nyt luotu. Kopioi se nyt, koska sitä ei näytetä enää uudelleen. +manage_access_token=Pääsypoletit +generate_new_token=Luo uusi poletti +token_name=Poletin nimi +generate_token=Luo pääsypoletti +generate_token_success=Uusi pääsypolettisi on nyt luotu. Kopioi se nyt, koska sitä ei näytetä enää uudelleen. delete_token=Poista -access_token_deletion=Poista pääsymerkki +access_token_deletion=Poista pääsypoletti access_token_deletion_cancel_action=Peruuta access_token_deletion_confirm_action=Poista -permission_read=Luettu +permission_read=Lue edit_oauth2_application=Muokkaa OAuth2-sovellusta remove_oauth2_application=Poista OAuth2-sovellus @@ -859,18 +873,18 @@ create_oauth2_application_button=Luo sovellus oauth2_application_name=Sovelluksen nimi save_application=Tallenna oauth2_regenerate_secret=Luo salaisuus uudelleen -oauth2_regenerate_secret_hint=Kadotitko secretin? +oauth2_regenerate_secret_hint=Kadotitko salaisuuden? oauth2_application_edit=Muokkaa twofa_desc=Kaksivaiheinen todennus parantaa tilisi turvallisuutta. -twofa_is_enrolled=Tilisi käyttää kaksivaiheista vahvistusta. -twofa_not_enrolled=Tilisi ei tällä hetkellä käytä kaksivaiheista vahvistusta. -twofa_enroll=Ilmoittaudu kaksivaiheiseen todennuksen käyttöön +twofa_is_enrolled=Tilisi käyttää kaksivaiheista todennusta. +twofa_not_enrolled=Tilisi ei tällä hetkellä käytä kaksivaiheista todennusta. +twofa_enroll=Ota kaksivaiheinen todennus käyttöön twofa_disabled=Kaksivaiheinen todennus on otettu pois käytöstä. -scan_this_image=Skannaa tämä kuva tunnistautumissovelluksellasi: +scan_this_image=Skannaa tämä kuva todennussovelluksellasi: or_enter_secret=Tai kirjoita salainen avain: %s -twofa_enrolled=Tiliisi on otettu käyttöön kaksivaiheinen vahvistus. Ota palautustunnus (%s) talteen turvalliseen paikkaan, sillä se näytetään vain kerran! +twofa_enrolled=Tiliisi on otettu käyttöön kaksivaiheinen todennus. Ota kertakäyttöinen palautusavain (%s) talteen turvalliseen paikkaan, sillä se näytetään vain kerran! webauthn_nickname=Nimimerkki @@ -878,7 +892,7 @@ manage_account_links=Yhdistetyt tilit manage_account_links_desc=Nämä ulkoiset tilit on linkitetty Forgejo-tiliisi. link_account=Yhdistä tili remove_account_link=Poista yhdistetty tili -remove_account_link_desc=Linkitetyn tilin poistaminen peruuttaa pääsyn Forgejo-tiliisi linkitetyn tili kautta. Jatketaanko? +remove_account_link_desc=Linkitetyn tilin poistaminen peruuttaa pääsyn Forgejo-tiliisi linkitetyn tilin kautta. Jatketaanko? remove_account_link_success=Linkitetty tili on poistettu. @@ -929,7 +943,7 @@ webauthn_key_loss_warning = Jos menetät turva-avaimesi, menetät pääsyn tilil keep_activity_private.description = Julkinen toimintasi näkyy vain sinulle ja instanssin ylläpitäjille. email_desc = Ensisijaista sähköpostiosoitettasi käytetään ilmoituksiin, salasanan palautukseen ja jos sähköpostiosoite ei ole piilotettu, web-pohjaisiin Git-toimenpiteisiin. tokens_desc = Nämä poletit mahdollistavat pääsyn tilillesi Forgejon rajapintaa vasten. -keep_email_private_popup = Sähköpostiosoitettasi ei näytetä profiilissasi, eikä sitä käytetä oletuksena verkkokäyttöliittymän kautta tehtävissä sitoumuksissa, kuten tiedostojen ulospäinlatauksissa, muokkauksissa ja yhdistämissitoumuksissa. Sen sijaan voit käyttää erityistä osoitetta %s sitoumuksien linkittämiseen tiliisi. Tämä vaihtoehto ei vaikuta olemassa oleviin sitoumuksiin. +keep_email_private_popup = Sähköpostiosoitettasi ei näytetä profiilissasi, eikä sitä käytetä oletuksena verkkokäyttöliittymän kautta tehtävissä kommiteissa, kuten tiedostojen lähetyksissä, muokkauksissa ja yhdistämiskommiteissa. Sen sijaan voit käyttää erityistä osoitetta %s kommittien linkittämiseen tiliisi. Tämä vaihtoehto ei vaikuta olemassa oleviin kommitteihin. added_on = Lisätty %s additional_repo_units_hint = Ehdota tietovaraston lisäyksiköiden käyttöönottoa revoke_oauth2_grant_success = Pääsy mitätöity. @@ -942,7 +956,7 @@ twofa_recovery_tip = Jos menetät laitteesi, voit palauttaa pääsyn tiliisi kä hooks.desc = Lisää web-koukkuja, jotka aktivoituvat kaikissa omistamissasi tietovarastoissa. revoke_key = Mitätöi permissions_list = Käyttöoikeudet: -at_least_one_permission = Pääsymerkin luominen vaatii vähintään yhden käyttöoikeuden +at_least_one_permission = Pääsypoletin luominen vaatii vähintään yhden käyttöoikeuden select_permissions = Valitse käyttöoikeudet twofa_disable_note = Voit poistaa kaksivaiheisen todennuksen käytöstä, jos tarve. authorized_oauth2_applications_description = Olet myöntänyt pääsyn henkilökohtaiseen Forgejo-tiliisi näille kolmannen osapuolen sovelluksille. Jos et enää käytä sovellusta, mitätöi sen pääsy tilillesi. @@ -981,7 +995,7 @@ add_email_confirmation_sent = Vahvistusviesti on lähetetty osoitteeseen "%s". V pronouns_custom_label = Mukautetut pronominit openid_deletion_desc = Tämän OpenID-osoitteen poistaminen tililtäsi estää kirjautumisen sitä käyttäen. Jatketaanko? generate_token_name_duplicate = Nimeä %s on jo käytetty sovelluksen nimenä. Käytä eri nimeä. -ssh_signonly = SSH on tällä hetkellä poistettu käytöstä, joten näitä avaimia käytetään vain sitoumusten allekirjoituksen vahvistamiseen. +ssh_signonly = SSH on tällä hetkellä poistettu käytöstä, joten näitä avaimia käytetään vain kommittien allekirjoituksen vahvistamiseen. oauth2_applications_desc = OAuth2-sovellukset mahdollistavat käyttämäsi kolmannen osapuolen sovelluksen todentaa turvallisesti käyttäjiä tähän Forgejo-instanssiin. quota.sizes.assets.attachments.all = Liitteet quota.applies_to_user = Seuraavia kiintiösääntöjä sovelletaan tiliisi @@ -998,22 +1012,36 @@ quota.sizes.repos.private = Yksityiset tietovarastot quota.sizes.git.all = Git-sisältö quota.sizes.assets.packages.all = Paketit quota.sizes.wiki = Wiki +update_hints_success = Vihjeet on päivitetty. +twofa_scratch_token_regenerate = Luo uudelleen kertakäyttöinen palautusavain +token_state_desc = Tätä polettia on käytetty viimeisen 7 päivän aikana +quota.sizes.assets.artifacts = Artefaktit +update_user_avatar_success = Käyttäjän profiilikuva on päivitetty. +access_token_regeneration = Luo uudelleen pääsypoletti +regenerate_token = Luo uudelleen +keep_pronouns_private = Näytä pronominit vain tunnistautuneille käyttäjille +keep_pronouns_private.description = Tämä piilottaa pronominisi käyttäjiltä, jotka eivät ole kirjautuneet sisään. +comment_type_group_issue_ref = Ongelmaviittaus +twofa_scratch_token_regenerated = Kertakäyttöinen palautusavaimesi on nyt %s. Talleta se turvalliseen sijaintiin, koska sitä ei näytetä uudelleen. +change_username_redirect_prompt.with_cooldown.few = Vanha käyttäjätunnus on kenen tahansa saatavilla %[1]d päivän suojaamisjakson jälkeen. Voit palauttaa käyttäjätunnuksen itsellesi suojaamisjakson aikana. +additional_repo_units_hint_description = Näytä "Ota lisää käyttöön"-vihje tietovarastoissa, missä kaikki saatavilla olevat yksiköt eivät ole käytössä. +change_username_redirect_prompt.with_cooldown.one = Vanha käyttäjätunnus on kenen tahansa saatavilla %[1]d päivän suojaamisjakson jälkeen. Voit palauttaa käyttäjätunnuksen itsellesi suojaamisjakson aikana. [repo] owner=Omistaja -owner_helper=Jotkin organisaatiot eivät välttämättä näy pudotusvalikossa, koska repojen maksimimäärää on rajoitettu. +owner_helper=Jotkin organisaatiot eivät välttämättä näy pudotusvalikossa, koska tietovarastojen enimmäismäärää on rajoitettu. repo_name=Tietovaraston nimi -repo_name_helper=Hyvä repon nimi on lyhyt, mieleenpainuva ja yksilöllinen. -repo_size=Repon koko -template=Malli -template_select=Valitse malli -template_helper=Tee reposta mallipohja +repo_name_helper=Hyvä tietovaraston nimi on lyhyt, mieleenpainuva ja yksilöllinen. +repo_size=Tietovaraston koko +template=Mallipohja +template_select=Valitse mallipohja +template_helper=Tee tietovarastosta mallipohja visibility=Näkyvyys visibility_description=Vain omistaja tai organisaation jäsenet, jos heillä on oikeudet, voivat nähdä sen. -visibility_helper_forced=Sivuston ylläpitäjä pakottaa uudet repot olemaan yksityisiä. -fork_repo=Luo tietovaraston haarukka +visibility_helper_forced=Sivuston ylläpitäjä pakottaa uudet tietovarastot olemaan yksityisiä. +fork_repo=Forkkaa tietovarasto fork_from=Forkkaa lähteestä -fork_visibility_helper=Forkatun repon näkyvyyttä ei voi muuttaa. +fork_visibility_helper=Forkatun tietovaraston näkyvyyttä ei voi muuttaa. clone_in_vsc=Kloonaa VS Codessa download_zip=Lataa ZIP download_tar=Lataa TAR.GZ @@ -1021,7 +1049,7 @@ repo_desc=Kuvaus repo_lang=Kieli repo_gitignore_helper=Valitse .gitignore-mallit issue_labels=Tunnisteet -issue_labels_helper=Valitse nimiöjoukko +issue_labels_helper=Valitse tunnistejoukko license=Lisenssi license_helper=Valitse lisenssitiedosto readme=README @@ -1031,14 +1059,14 @@ default_branch=Oletushaara mirror_prune=Karsi watchers=Tarkkailijat stargazers=Tähtiharrastajat -forks=Haarat +forks=Forkit delete_preexisting_label=Poista desc.private=Yksityinen desc.public=Julkinen -desc.template=Malli +desc.template=Mallipohja desc.internal=Sisäinen desc.archived=Arkistoidut @@ -1046,28 +1074,28 @@ template.git_hooks=Git-koukut template.webhooks=Webkoukut template.topics=Aiheet template.avatar=Profiilikuva -template.issue_labels=Viannimikkeet +template.issue_labels=Ongelmatunnisteet -migrate_items=Siirrettävät kohteet +migrate_items=Migraation kohteet migrate_items_wiki=Wiki migrate_items_milestones=Merkkipaalut migrate_items_labels=Tunnisteet migrate_items_issues=Ongelmat migrate_items_pullrequests=Vetopyynnöt migrate_items_releases=Julkaisut -migrate_repo=Siirrä tietovarasto +migrate_repo=Suorita tietovaraston migraatio migrate.clone_address=Migraatio/kloonaus URL-osoitteesta -migrate.github_token_desc=Voit laittaa yhden tai useamman pääsymerkin pilkulla erotellen tähän nopeuttaaksesi migraatiota GitHub APIn vauhtirajojen takia. VAROITUS: Tämän ominaisuuden väärinkäyttö voi rikkoa palveluntarjoajan ehtoja ja johtaa tilin estämiseen. -migrate.permission_denied=Sinun ei sallita tuovan paikallisia repoja. -migrate.failed=Siirto epäonnistui: %v -migrate.migrate_items_options=Lisäkohteiden siirtämistä varten vaaditaan pääsypoletti -migrate.migrating=Tuodaan kohteesta %s … -migrate.migrating_failed=Tuonti kohteesta %s epäonnistui. -migrate.migrating_git=Git-tietojen siirtäminen +migrate.github_token_desc=Voit laittaa yhden tai useamman pääsypoletin pilkulla erotellen tähän nopeuttaaksesi migraatiota GitHubin rajapinnan tahtirajojen takia. VAROITUS: Tämän ominaisuuden väärinkäyttö voi rikkoa palveluntarjoajan ehtoja ja johtaa tilin estämiseen. +migrate.permission_denied=Paikallisten tietovarastojen tuominen ei ole sallittua. +migrate.failed=Migraatio epäonnistui: %v +migrate.migrate_items_options=Lisäkohteiden migraatiota varten vaaditaan pääsypoletti +migrate.migrating=Suoritetaan migraatio lähteestä %s … +migrate.migrating_failed=Migraatio lähteestä %s epäonnistui. +migrate.migrating_git=Suoritetaan Git-datan migraatiota -mirror_from=peilaus alkaen +mirror_from=peili kohteelle forked_from=forkattu lähteestä unwatch=Lopeta tarkkailu watch=Tarkkaile @@ -1077,10 +1105,10 @@ download_archive=Lataa tietovarasto no_desc=Ei kuvausta quick_guide=Pikaopas -clone_this_repo=Kloonaa tämä repo +clone_this_repo=Kloonaa tämä tietovarasto code=Koodi -code.desc=Pääsy lähdekoodiin, tiedostoihin, committeihin ja haaroihin. +code.desc=Pääsy lähdekoodiin, tiedostoihin, kommitteihin ja haaroihin. branch=Haara tree=Puu filter_branch_and_tag=Suodata haara tai tagi @@ -1093,8 +1121,8 @@ packages=Paketit labels=Tunnisteet milestones=Merkkipaalut -commits=Commitit -commit=Commit +commits=Kommitit +commit=Kommitti releases=Julkaisut tag=Tagi released_this=julkaisi tämän @@ -1105,7 +1133,7 @@ file_permalink=Pysyvä linkki video_not_supported_in_browser=Selaimesi ei tue HTML5:n video-tagia. audio_not_supported_in_browser=Selaimesi ei tue HTML5:n audio-tagia. -blame=Selitys +blame=Blame download_file=Lataa tiedosto normal_view=Normaali näkymä line=rivi @@ -1121,20 +1149,20 @@ editor.delete_this_file=Poista tiedosto editor.name_your_file=Nimeä tiedostosi… editor.filename_help=Lisää hakemisto kirjoittamalla sen nimi ja perään kauttaviiva ("/"). Poista hakemisto kirjoittamalla askelpalautin syöttökentän alkuun. editor.or=tai -editor.cancel_lower=Peru -editor.commit_signed_changes=Sitoudu allekirjoitetut muutokset -editor.commit_changes=Sitoudu muutokset +editor.cancel_lower=Peruuta +editor.commit_signed_changes=Kommitoi allekirjoitetut muutokset +editor.commit_changes=Kommitoi muutokset editor.add_tmpl=Lisää "<%s>" -editor.commit_directly_to_this_branch=Sitoudu suoraan %[1]s -haaraan. -editor.create_new_branch=Luo uusi haara tälle commitille ja aloita vetopyyntö. -editor.create_new_branch_np=Luo uusi haara tälle commitille. +editor.commit_directly_to_this_branch=Kommitoi suoraan haaraan %[1]s. +editor.create_new_branch=Luo uusi haara tälle kommitille ja aloita vetopyyntö. +editor.create_new_branch_np=Luo uusi haara tälle kommitille. editor.cancel=Peruuta editor.filename_cannot_be_empty=Tiedostonimi ei voi olla tyhjä. editor.no_changes_to_show=Ei muutoksia näytettäväksi. editor.add_subdir=Lisää hakemisto… -editor.require_signed_commit=Haara vaatii vahvistetun commitin +editor.require_signed_commit=Haara vaatii allekirjoitetun kommitin -commits.commits=Commitit +commits.commits=Kommitit commits.nothing_to_compare=Nämä haarat vastaavat toisiaan. commits.find=Haku commits.search_all=Kaikki haarat @@ -1188,7 +1216,7 @@ issues.new.clear_assignees=Tyhjennä käsittelijä issues.new.no_assignees=Ei käsittelijää issues.choose.open_external_link=Avaa issues.choose.blank=Oletus -issues.no_ref=Haaran/tagia ei määritelty +issues.no_ref=Haaraa/tagia ei määritelty issues.create=Luo ongelma issues.new_label=Uusi tunniste issues.new_label_placeholder=Tunnisteen nimi @@ -1209,7 +1237,7 @@ issues.filter_label_exclude=`Käytä alt + klikkaus/rivinvaih issues.filter_label_no_select=Kaikki tunnisteet issues.filter_milestone=Merkkipaalu issues.filter_project=Projekti -issues.filter_assignee=Osoitettu +issues.filter_assignee=Käsittelijä issues.filter_poster=Tekijä issues.filter_type=Tyyppi issues.filter_type.all_issues=Kaikki ongelmat @@ -1241,7 +1269,7 @@ issues.previous=Edellinen issues.next=Seuraava issues.open_title=Avoinna issues.closed_title=Suljettu -issues.draft_title=Työversio +issues.draft_title=Luonnos issues.num_comments=%d kommenttia issues.commented_at=`kommentoi %s` issues.delete_comment_confirm=Haluatko varmasti poistaa tämän kommentin? @@ -1256,7 +1284,7 @@ issues.reopen_comment_issue=Kommentoi ja avaa uudelleen issues.create_comment=Kommentoi issues.closed_at=`sulki tämän ongelman %[2]s` issues.reopened_at=`uudelleenavasi tämän ongelman %[2]s` -issues.commit_ref_at=`viittasi tähän ongelmaan commitissa %[2]s` +issues.commit_ref_at=`viittasi tähän ongelmaan kommitissa %[2]s` issues.author=Tekijä issues.role.owner=Omistaja issues.role.member=Jäsen @@ -1267,7 +1295,7 @@ issues.label_title=Tunnisteen nimi issues.label_description=Kuvaus issues.label_color=Tunnisteen väri issues.label_count=%d tunnistetta -issues.label_open_issues=%d avointa ongelmaa +issues.label_open_issues=%d avointa ongelmaa/vetopyyntöä issues.label_edit=Muokkaa issues.label_delete=Poista issues.label_modify=Muokkaa tunnistetta @@ -1284,7 +1312,7 @@ issues.unlock=Avaa keskustelu issues.unlock_comment=avasi tämän keskustelun lukituksen %s issues.lock_confirm=Lukitse issues.unlock_confirm=Avaa -issues.lock.notice_1=- Muut käyttäjät eivät voi lisätä uusia kommentteja tähän vianlippuun. +issues.lock.notice_1=- Muut käyttäjät eivät voi lisätä uusia kommentteja tähän ongelmaan. issues.lock.notice_3=- Voit aina myöhemmin avata tämän ongelman lukituksesta. issues.unlock.notice_2=- Voit aina myöhemmin lukita tämän ongelman uudelleen. issues.lock.reason=Lukitsemisen syy @@ -1307,18 +1335,18 @@ issues.add_time_minutes=Minuuttia issues.add_time_sum_to_small=Aikaa ei syötetty. issues.time_spent_from_all_authors=`Käytetty kokonaisaika: %s` issues.due_date=Määräpäivä -issues.push_commit_1=lisäsi %d sitoumuksen %s -issues.push_commits_n=lisäsi %d sitoumusta %s +issues.push_commit_1=lisäsi %d kommitin %s +issues.push_commits_n=lisäsi %d kommittia %s issues.due_date_form=vvvv-kk-pp issues.due_date_form_edit=Muokkaa issues.due_date_form_remove=Poista issues.due_date_not_set=Määräpäivää ei ole asetettu. issues.due_date_overdue=Myöhässä issues.dependency.title=Riippuvuudet -issues.dependency.issue_no_dependencies=Riippuvuuksia ei asetettu. -issues.dependency.pr_no_dependencies=Riippuvuuksia ei asetettu. +issues.dependency.issue_no_dependencies=Riippuvuuksia ei ole asetettu. +issues.dependency.pr_no_dependencies=Riippuvuuksia ei ole asetettu. issues.dependency.add=Lisää riippuvuus… -issues.dependency.cancel=Peru +issues.dependency.cancel=Peruuta issues.dependency.remove=Poista issues.dependency.remove_info=Poistä tämä riippuvuus issues.review.self.approval=Et voi hyväksyä omia vetopyyntöjä. @@ -1335,11 +1363,11 @@ issues.content_history.created=luotu pulls.new=Uusi vetopyyntö -pulls.compare_changes=Uusi pull-pyyntö +pulls.compare_changes=Uusi vetopyyntö pulls.has_viewed_file=Katsottu pulls.viewed_files_label=%[1]d / %[2]d tiedostoa katsottu pulls.compare_compare=vedä kohteesta -pulls.filter_branch=Suodata branch +pulls.filter_branch=Suodata haara pulls.no_results=Tuloksia ei löytynyt. pulls.nothing_to_compare=Nämä haarat vastaavat toisiaan. Ei ole tarvetta luoda vetopyyntöä. pulls.nothing_to_compare_and_allow_empty_pr=Nämä haarat vastaavat toisiaan. Vetopyyntö tulee olemaan tyhjä. @@ -1348,13 +1376,13 @@ pulls.create=Luo vetopyyntö pulls.title_desc_few=haluaa yhdistää %[1]d committia lähteestä %[2]s kohteeseen %[3]s pulls.merged_title_desc_few=yhdistetty %[1]d committia lähteestä %[2]s kohteeseen %[3]s %[4]s pulls.tab_conversation=Keskustelu -pulls.tab_commits=Commitit +pulls.tab_commits=Kommitit pulls.tab_files=Muuttuneet tiedostot pulls.merged=Yhdistetty pulls.title_wip_desc=`Aloita otsikko sanalla %s estääksesi vetopyynnön yhdistämisen vahingossa.` -pulls.add_prefix=Lisää %s etuliite -pulls.remove_prefix=Poista %s etuliite -pulls.can_auto_merge_desc=Tämä pull-pyyntö voidaan yhdistää automaattisesti. +pulls.add_prefix=Lisää etuliite %s +pulls.remove_prefix=Poista etuliite %s +pulls.can_auto_merge_desc=Tämä vetopyyntö voidaan yhdistää automaattisesti. @@ -1387,7 +1415,7 @@ wiki.welcome=Tervetuloa Wikiin. wiki.welcome_desc=Wikissä voit kirjoittaa ja jakaa dokumentaatiota käyttäjien kesken. wiki.create_first_page=Luo ensimmäinen sivu wiki.page=Sivu -wiki.filter_page=Suodatin sivu +wiki.filter_page=Suodata sivu wiki.new_page=Sivu wiki.default_commit_message=Kirjoita muistiinpano tästä päivityksestä (valinnainen). wiki.save_page=Tallenna sivu @@ -1416,7 +1444,7 @@ activity.active_issues_count_1=%d aktiivinen ongelma activity.active_issues_count_n=%d aktiivista ongelmaa activity.closed_issues_count_1=suljettu ongelma activity.closed_issues_count_n=suljettua ongelmaa -activity.title.issues_created_by=%s luonnut %s +activity.title.issues_created_by=%s luonut %s activity.closed_issue_label=Suljettu activity.new_issues_count_1=Uusi ongelma activity.new_issues_count_n=uutta ongelmaa @@ -1432,14 +1460,14 @@ activity.git_stats_and_deletions=ja activity.git_stats_deletion_1=%d poisto activity.git_stats_deletion_n=%d poistoa -contributors.contribution_type.commits=Sitoumukset +contributors.contribution_type.commits=Kommitit search=Haku search.match=Osuma search.code_no_results=Hakuehtoasi vastaavaa lähdekoodia ei löytynyt. settings=Asetukset -settings.options=Repo +settings.options=Tietovarasto settings.collaboration.admin=Ylläpitäjä settings.collaboration.write=Kirjoita settings.collaboration.read=Lue @@ -1450,31 +1478,31 @@ settings.githooks=Git-koukut settings.basic_settings=Perusasetukset settings.mirror_settings=Peilauksen asetukset -settings.site=Nettisivu +settings.site=Verkkosivusto settings.update_settings=Tallenna asetukset settings.advanced_settings=Lisäasetukset settings.use_internal_wiki=Käytä sisäänrakennettua wikiä settings.use_external_wiki=Käytä ulkoista wikiä settings.external_wiki_url=Ulkoisen wikin URL-osoite -settings.external_wiki_url_desc=Wiki-välilehden klikkaus ohjaa vierailijat ulkoisen wiki-URL-osoitteeseen. -settings.tracker_url_format=Ulkoisen vianseurannan URL-muoto +settings.external_wiki_url_desc=Wiki-välilehden napsauttaminen ohjaa vierailijat ulkoiseen wiki-URL-osoitteeseen. +settings.tracker_url_format=Ulkoisen ongelmanseurannan URL-muoto settings.tracker_issue_style.numeric=Numeerinen settings.tracker_issue_style.alphanumeric=Aakkosnumeerinen -settings.enable_timetracker=Ota ajan seuranta käyttöön +settings.enable_timetracker=Ota ajanseuranta käyttöön settings.danger_zone=Vaaravyöhyke -settings.new_owner_has_same_repo=Uudella omistajalla on jo samanniminen repo. Ole hyvä ja valitse toinen nimi. +settings.new_owner_has_same_repo=Uudella omistajalla on jo samanniminen tietovarasto. Valitse toinen nimi. settings.transfer.title=Siirrä omistajuus settings.transfer_form_title=Syötä repon nimi vahvistuksena: -settings.transfer_notices_3=- Jos arkisto on yksityinen ja se siirretään yksittäiselle käyttäjälle, tämä toiminto varmistaa, että käyttäjällä on ainakin lukuoikeudet (ja muuttaa käyttöoikeuksia tarvittaessa). +settings.transfer_notices_3=- Jos tietovarasto on yksityinen ja se siirretään yksittäiselle käyttäjälle, tämä toiminto varmistaa, että käyttäjällä on ainakin lukuoikeudet (ja muuttaa käyttöoikeuksia tarvittaessa). settings.transfer_owner=Uusi omistaja settings.wiki_delete=Poista wikidata -settings.wiki_delete_desc=Repon wikin data poistaminen on pysyvä eikä voi peruuttaa. +settings.wiki_delete_desc=Tietovaraston wikin data poistaminen on pysyvä, eikä sitä voi perua. settings.confirm_wiki_delete=Poista wikidata -settings.wiki_deletion_success=Repon wiki data on poistettu. +settings.wiki_deletion_success=Tietovaraston wikidata on poistettu. settings.delete=Poista tämä tietovarasto -settings.delete_desc=Repon poistaminen on pysyvä eikä voi peruuttaa. -settings.delete_notices_1=- Tätä toimintoa EI VOI peruuttaa myöhemmin. -settings.update_settings_success=Repon asetukset on päivitetty. +settings.delete_desc=Tietovaraston poistaminen on pysyvä, eikä sitä voi perua. +settings.delete_notices_1=- Tätä toimintoa EI VOI perua myöhemmin. +settings.update_settings_success=Tietovaraston asetukset on päivitetty. settings.delete_collaborator=Poista settings.search_user_placeholder=Etsi käyttäjä… settings.teams=Tiimit @@ -1484,18 +1512,18 @@ settings.webhook_deletion=Poista webkoukku settings.webhook.test_delivery=Testitoimitus settings.webhook.request=Pyyntö settings.webhook.response=Vastaus -settings.webhook.headers=Otsikot +settings.webhook.headers=Otsakkeet settings.webhook.payload=Sisältö settings.webhook.body=Sisältö settings.githook_edit_desc=Jos koukku ei ole käytössä, esitellään esimerkkisisältö. Sisällön jättäminen tyhjäksi arvoksi poistaa tämän koukun käytöstä. settings.githook_name=Koukun nimi settings.githook_content=Koukun sisältö settings.update_githook=Päivitä koukku -settings.payload_url=Kohde URL +settings.payload_url=Kohde-URL settings.http_method=HTTP-menetelmä -settings.secret=Salaus +settings.secret=Salaisuus settings.slack_username=Käyttäjätunnus -settings.slack_icon_url=Kuvakkeen URL +settings.slack_icon_url=Kuvakkeen URL-osoite settings.discord_username=Käyttäjätunnus settings.event_desc=Laukaisu päällä: settings.event_send_everything=Kaikki tapahtumat @@ -1506,29 +1534,29 @@ settings.event_create_desc=Haara tai tagi luotu. settings.event_delete=Poista settings.event_delete_desc=Haara tai tagi poistettu. settings.event_wiki=Wiki -settings.event_release_desc=Julkaisu julkaistu, päivitetty tai poistettu varastosta. +settings.event_release_desc=Julkaisu julkaistu, päivitetty tai poistettu tietovarastosta. settings.event_push=Työnnä -settings.event_push_desc=Git push repoon. -settings.event_repository=Repo -settings.event_repository_desc=Repo luotu tai poistettu. +settings.event_push_desc=Git-työntö tietovarastoon. +settings.event_repository=Tietovarasto +settings.event_repository_desc=Tietovarasto luotu tai poistettu. settings.event_header_issue=Ongelmien tapahtumat settings.event_issues=Muokkaus settings.event_issues_desc=Ongelma avattu, suljettu, avattu uudelleen tai muokattu. settings.event_issue_assign=Toimeksianto settings.event_issue_assign_desc=Ongelma osoitettu tai osoitus poistettu. -settings.event_issue_label_desc=Viannimikkeet lisätty tai poistettu. +settings.event_issue_label_desc=Ongelmatunnisteet lisätty tai poistettu. settings.event_issue_milestone_desc=Merkkipaalu lisätty, poistettu tai muokattu. settings.event_issue_comment_desc=Ongelman kommentti luotu, muokattu tai poistettu. settings.event_header_pull_request=Vetopyyntöjen tapahtumat settings.event_pull_request=Muokkaus -settings.event_package_desc=Paketti on luotu tai poistettu repossa. +settings.event_package_desc=Paketti on luotu tai poistettu tietovarastossa. settings.active_helper=Tiedot käynnistetyistä tapahtumista lähetetään tähän webkoukun URL-osoitteeseen. settings.add_hook_success=Uusi webkoukku on lisätty. settings.update_webhook=Päivitä webkoukku settings.delete_webhook=Poista webkoukku settings.recent_deliveries=Viimeisimmät toimitukset settings.hook_type=Koukun tyyppi -settings.slack_token=Pääsymerkki +settings.slack_token=Poletti settings.slack_domain=Verkkotunnus settings.slack_channel=Kanava settings.add_web_hook_desc=Integroi %s repoon. @@ -1543,36 +1571,36 @@ settings.web_hook_name_matrix=Matrix settings.web_hook_name_feishu_only =Feishu settings.web_hook_name_larksuite_only =Lark Suite settings.web_hook_name_packagist=Packagist -settings.deploy_keys=Julkaisuavaimet -settings.add_deploy_key=Lisää julkaisuavain -settings.deploy_key_desc=Julkaisuavaimilla on vain-luku oikeudet repoon. -settings.is_writable_info=Salli tämän julkaisuavaimen puskea repoon. -settings.no_deploy_keys=Julkaisuavaimia ei ole käytössä vielä. +settings.deploy_keys=Toimitusavaimet +settings.add_deploy_key=Lisää toimitusavain +settings.deploy_key_desc=Toimitusavaimilla on pelkkä lukuoikeus tietovarastoon. +settings.is_writable_info=Salli tämän toimitusavaimen työntää tietovarastoon. +settings.no_deploy_keys=Toimitusavaimia ei ole käytössä vielä. settings.title=Otsikko settings.deploy_key_content=Sisältö -settings.key_been_used=Julkaisuavain identtisellä sisällöllä on jo käytössä. -settings.key_name_used=Julkaisuavain samalla nimellä on jo olemassa. -settings.deploy_key_deletion=Poista julkaisuavain -settings.deploy_key_deletion_desc=Julkaisuavaimen poistaminen kumoaa sen pääsyn tähän repoon. Jatketaanko? -settings.deploy_key_deletion_success=Julkaisuavain on poistettu. +settings.key_been_used=Toimitusavain identtisellä sisällöllä on jo käytössä. +settings.key_name_used=Toimitusavain samalla nimellä on jo olemassa. +settings.deploy_key_deletion=Poista toimitusavain +settings.deploy_key_deletion_desc=Toimitusavaimen poistaminen kumoaa sen pääsyn tähän tietovarastoon. Jatketaanko? +settings.deploy_key_deletion_success=Toimitusavain on poistettu. settings.branches=Haarat settings.protected_branch=Haaran suojaus settings.branch_protection=Haaran "%s" suojaussäännöt settings.protect_this_branch=Ota haaran suojaus käyttöön -settings.protect_whitelist_deploy_keys=Lisää julkaisuavaimet sallittujen listalle mahdollistaaksesi repohin kirjoituksen. -settings.protect_whitelist_users=Sallitut käyttäjät suhteessa työntämiseen +settings.protect_whitelist_deploy_keys=Lisää toimitusavaimet sallittujen listalle mahdollistaaksesi tietovarastoihin kirjoituksen. +settings.protect_whitelist_users=Työntämiseen oikeutettujen käyttäjien lista settings.protect_whitelist_search_users=Etsi käyttäjiä… settings.protect_merge_whitelist_committers_desc=Salli vain listaan merkittyjen käyttäjien ja tiimien yhdistää vetopyynnöt tähän haaraan. -settings.protect_merge_whitelist_users=Sallitut käyttäjät suhteessa yhdistämiseen +settings.protect_merge_whitelist_users=Yhdistämiseen oikeutettujen käyttäjien lista settings.protect_required_approvals=Vaadittavat hyväksynnät -settings.protect_approvals_whitelist_users=Sallittujen tarkastajien lista +settings.protect_approvals_whitelist_users=Sallittujen katselmoijien lista settings.choose_branch=Valitse haara… settings.no_protected_branch=Suojattuja haaroja ei ole. settings.edit_protected_branch=Muokkaa -settings.protected_branch_required_approvals_min=Vaadittavat hyväksynnät ei voi olla negatiivinen. +settings.protected_branch_required_approvals_min=Vaadittavat hyväksynnät eivät voi olla negatiivisia. settings.tags=Tagit -settings.tags.protection=Tagien suojaaminen -settings.tags.protection.pattern=Tagin kuvio +settings.tags.protection=Tagien suojaus +settings.tags.protection.pattern=Tagin kaava settings.tags.protection.allowed=Sallitut settings.tags.protection.allowed.users=Sallitut käyttäjät settings.tags.protection.allowed.teams=Sallitut tiimit @@ -1585,13 +1613,13 @@ settings.archive.button=Arkistoi tietovarasto settings.archive.header=Arkistoi tämä tietovarasto settings.archive.tagsettings_unavailable=Tagi-asetukset eivät ole käytettävissä arkistoiduissa tietovarastoissa. settings.lfs=LFS -settings.lfs_filelist=LFS-tiedostot tallennettu tähän repoon -settings.lfs_no_lfs_files=LFS-tiedostoja ei ole tallennettu tähän repoon. -settings.lfs_findcommits=Etsi commitit -settings.lfs_lfs_file_no_commits=Tälle LFS-tiedostolle ei löytynyt sitoumuksia +settings.lfs_filelist=Tähän tietovarastoon tallennetut LFS-tiedostot +settings.lfs_no_lfs_files=LFS-tiedostoja ei ole tallennettu tähän tietovarastoon. +settings.lfs_findcommits=Etsi kommitteja +settings.lfs_lfs_file_no_commits=Tälle LFS-tiedostolle ei löytynyt kommitteja settings.lfs_noattribute=Tällä polulla ei ole lukittavaa attribuuttia oletushaarassa settings.lfs_delete=Poista LFS-tiedosto OID:lla %s -settings.lfs_delete_warning=LFS-tiedoston poistaminen saattaa aiheuttaa "objektia ei ole olemassa" -virheitä uloskirjauksessa. Oletko varma? +settings.lfs_delete_warning=LFS-tiedoston poistaminen saattaa aiheuttaa "objektia ei ole olemassa"-virheitä uloskuittauksessa. Oletko varma? settings.lfs_findpointerfiles=Etsi osoitintiedostoja settings.lfs_locks=Lukot settings.lfs_invalid_locking_path=Virheellinen polku: %s @@ -1601,7 +1629,7 @@ settings.lfs_lock_path=Lukittavan tiedostopolku… settings.lfs_locks_no_locks=Ei lukkoja settings.lfs_lock_file_no_exist=Lukittua tiedostoa ei ole olemassa oletushaarassa settings.lfs_force_unlock=Pakota lukituksen avaus -settings.lfs_pointers.found=Löytyi %d blob osoitinta - %d yhdistettyö, %d yhdistämätöntä (%d puuttuu varastosta) +settings.lfs_pointers.found=Löytyi %d blob-osoitinta - %d assosioitu, %d assosioitumaton (%d puuttuu varastosta) settings.lfs_pointers.sha=Blob-tiiviste settings.lfs_pointers.oid=OID settings.lfs_pointers.inRepo=Tietovarastossa @@ -1610,8 +1638,8 @@ settings.lfs_pointers.accessible=Saatavilla käyttäjälle diff.browse_source=Selaa lähdekoodia diff.parent=vanhempi -diff.commit=commit -diff.git-notes=Muistiinpanot +diff.commit=kommitti +diff.git-notes=Huomautukset diff.options_button=Vertailun asetukset diff.show_split_view=Jaettu näkymä diff.show_unified_view=Yhdistetty näkymä @@ -1620,7 +1648,7 @@ diff.whitespace_show_everything=Näytä kaikki muutokset diff.whitespace_ignore_all_whitespace=Ohita tyhjämerkit rivejä verratessa diff.whitespace_ignore_amount_changes=Ohita tyhjämerkin määrän muutokset diff.whitespace_ignore_at_eol=Ohita muutokset rivin lopun tyhjämerkeissä -diff.stats_desc=%d muutettua tiedostoa jossa %d lisäystä ja %d poistoa +diff.stats_desc=%d muutettua tiedostoa joissa %d lisäystä ja %d poistoa diff.bin=BIN diff.view_file=Näytä tiedosto diff.file_image_width=Leveys @@ -1629,7 +1657,7 @@ diff.file_byte_size=Koko diff.comment.markdown_info=Muotoilu Markdownilla on tuettu. diff.comment.add_single_comment=Lisää yksittäinen kommentti diff.comment.add_review_comment=Lisää kommentti -diff.comment.start_review=Aloita tarkistus +diff.comment.start_review=Aloita katselmointi diff.comment.reply=Vastaa diff.review.header=Lähetä katselmointi diff.review.placeholder=Katselmoinnin kommentti @@ -1640,14 +1668,14 @@ diff.review.reject=Pyydä muutoksia release.releases=Julkaisut release.tags=Tagit release.new_release=Uusi julkaisu -release.draft=Työversio +release.draft=Luonnos release.prerelease=Esijulkaisu release.stable=Vakaa release.edit=Muokkaa release.source_code=Lähdekoodi release.new_subheader=Julkaisut organisoivat projektien versioita. release.edit_subheader=Julkaisut organisoivat projektien versioita. -release.tag_name=Taginimi +release.tag_name=Tagin nimi release.target=Kohde release.tag_helper=Valitse olemassa oleva tagi tai luo uusi tagi. release.prerelease_desc=Merkitse esijulkaisuksi @@ -1659,7 +1687,7 @@ release.edit_release=Päivitä julkaisu release.delete_release=Poista julkaisu release.delete_tag=Poista tagi release.deletion=Poista julkaisu -release.deletion_tag_desc=Poistetaanko tämä tagi reposta? Repon sisältö ja historia pysyvät muuttumattomina. Jatketaanko? +release.deletion_tag_desc=Poistetaan tämä tagi tietovarastosta. Tietovaraston sisältö ja historia pysyvät muuttumattomina. Jatketaanko? release.deletion_tag_success=Tagi on poistettu. release.tag_name_invalid=Tagin nimi ei ole kelvollinen. release.downloads=Lataukset @@ -1675,23 +1703,23 @@ topic.done=Valmis already_forked = Olet jo forkannut %s fork_to_different_account = Forkkaa toiselle tilille release.compare = Vertaa -release.ahead.commits = %d sitoumusta +release.ahead.commits = %d kommittia all_branches = Kaikki haarat n_tag_few = %s tagia -settings.event_fork_desc = Tietovaraston haarukka luotu. +settings.event_fork_desc = Tietovarasto forkattu. actions = Toiminnat -fork_guest_user = Kirjaudu sisään luodaksesi tämän tietovaraston haarukan. -fork_from_self = Et voi luoda omistamasi tietovaraston haarukkaa. +fork_guest_user = Kirjaudu sisään forkataksesi tämän tietovaraston. +fork_from_self = Et voi forkata omistamaasi tietovarastoa. visibility_fork_helper = (Tämän muuttaminen vaikuttaa kaikkien forkkien näkyvyyteen.) fork = Forkkaa -activity.git_stats_commit_n = %d sitoumusta +activity.git_stats_commit_n = %d kommittia commits.search_branch = Tämä haara n_branch_few = %s haaraa -pulls.show_all_commits = Näytä kaikki sitoumukset +pulls.show_all_commits = Näytä kaikki kommitit commit_graph.select = Valitse haarat -activity.navbar.recent_commits = Viimeaikaiset sitoumukset +activity.navbar.recent_commits = Viimeaikaiset kommitit settings.branches.add_new_rule = Lisää uusi sääntö -n_commit_few = %s sitoumusta +n_commit_few = %s kommittia issues.force_push_compare = Vertaa commits.desc = Selaa lähdekoodin muutoshistoriaa. clone_helper = Tarvitseko apua kloonauksen kanssa? Siirry tukisivulle. @@ -1739,7 +1767,7 @@ settings.event_issue_comment = Kommentit diff.download_patch = Lataa patch-tiedosto issues.filter_milestone_none = Ei merkkipaaluja issues.filter_milestone_open = Avoimet merkkipaalut -new_repo_helper = Tietovarasto sisältää kaikki projektitiedostot, mukaan lukien tarkistushistoria. Järjestätkö jo sellaisen muualla? Siirrä tietovarasto. +new_repo_helper = Tietovarasto sisältää kaikki projektitiedostot, mukaan lukien versiohistorian. Onko sinulla tietovarasto jo muualla? Siirrä tietovarasto. use_template = Käytä tätä mallipohjaa star_guest_user = Kirjaudu sisään lisätäksesi tähden tähän tietovarastoon. watch_guest_user = Kirjaudu sisään tarkkaillaksesi tätä tietovarastoa. @@ -1770,7 +1798,7 @@ settings.protected_branch.delete_rule = Poista sääntö settings.archive.success = Tietovarasto arkistoitiin onnistuneesti. diff.comment.placeholder = Jätä kommentti release.message = Kuvaile tätä julkaisua -branch.delete_desc = Haaran poistaminen on pysyvä toimenpide. Vaikka poistettu haara voi jäädä olemaan lyhyeksi ajaksi, ennen kuin todellisesti poistetaan, poistoa EI VOI perua useimmiten. Jatketaanko? +branch.delete_desc = Haaran poistaminen on pysyvä toimenpide. Vaikka poistettu haara voi jäädä olemaan lyhyeksi ajaksi, ennen kuin se todellisesti poistetaan, poistoa EI VOI perua useimmiten. Jatketaanko? branch.protected_deletion_failed = Haara "%s" on suojattu. Sitä ei voi poistaa. open_with_editor = Avaa sovelluksella %s download_bundle = Lataa BUNDLE @@ -1785,7 +1813,7 @@ issues.new.closed_projects = Suljetut projektit settings.event_issue_milestone = Merkkipaalut branch.branch_already_exists = Haara "%s" on jo olemassa tässä tietovarastossa. projects.card_type.images_and_text = Kuvat ja teksti -default_branch_helper = Oletushaara on kantahaara vetopyyntöjä ja koodisitoumuksia varten. +default_branch_helper = Oletushaara on kantahaara vetopyyntöjä ja koodikommitteja varten. author_search_tooltip = Näyttää enintään 30 käyttäjää migrate_options_mirror_helper = Tästä tietovarastosta tulee peili commit_graph.color = Väri @@ -1804,7 +1832,7 @@ issues.dependency.pr_remove_text = Riippuvuus poistetaan tästä vetopyynnöstä release.download_count_few = %s latausta diff.data_not_available = Diff-sisältö ei ole saatavilla diff.image.side_by_side = Rinnakkain -release.ahead.target = projektiin %s tämän julkaisun jälkeen +release.ahead.target = haaraan %s tämän julkaisun jälkeen issues.close = Sulje ongelma issues.no_content = Ei kuvausta. pulls.reject_count_1 = %d muutospyyntö @@ -1818,7 +1846,7 @@ find_file.no_matching = Vastaavaa tiedostoa ei löytynyt editor.file_delete_success = Tiedosto "%s" on poistettu. settings.transfer.button = Siirrä omistajuus settings.slack_color = Väri -release.tag_name_already_exist = Julkaisu tällä taginimellä on jo olemassa. +release.tag_name_already_exist = Julkaisu tällä tagin nimellä on jo olemassa. pulls.allow_edits_from_maintainers_err = Päivittäminen epäonnistui stars = Tähdet editor.branch_already_exists = Haara "%s" on jo olemassa tässä tietovarastossa. @@ -1832,12 +1860,12 @@ wiki.back_to_wiki = Takaisin wikisivulle wiki.delete_page_notice_1 = Wikisivun "%s" poistamista ei voi perua. Jatketaanko? activity.merged_prs_count_1 = Yhdistetty vetopyyntö activity.merged_prs_count_n = Yhdistettyä vetopyyntöä -activity.opened_prs_count_1 = ehdotettu vetopyyntö -activity.opened_prs_count_n = ehdotettua vetopyyntöä +activity.opened_prs_count_1 = Ehdotettu vetopyyntö +activity.opened_prs_count_n = Ehdotettua vetopyyntöä activity.title.user_1 = %d käyttäjä activity.title.prs_n = %d vetopyyntöä settings.sourcehut_builds.secrets = Salaisuudet -commit_graph = Sitoumuskaavio +commit_graph = Kommittikaavio visibility_helper = Tee tietovarastosta yksityinen pulls.approve_count_1 = %d hyväksyntä settings.confirm_delete = Poista tietovarasto @@ -1852,7 +1880,7 @@ pull.deleted_branch = (poistettu):%s settings.transfer.rejected = Tietovaraston siirto hylättiin. settings.transfer.modal.title = Siirrä omistajuus settings.event_pull_request_sync = Synkronoitu -editor.commit_empty_file_text = Tiedosto, jonka olet aikeissa sitoa, on tyhjä. Edetäänkö? +editor.commit_empty_file_text = Tiedosto, jonka olet aikeissa kommitoida, on tyhjä. Jatketaanko? diff.load = Lataa diff branch.create_branch_operation = Luo haara activity.title.releases_n = %d julkaisua @@ -1873,7 +1901,7 @@ issues.filter_label_select_no_label = Ei tunnistetta projects.column.set_default = Aseta oletukseksi projects.edit_success = Projekti "%s" on päivitetty. desc.sha256 = SHA256 -n_commit_one = %s sitoumus +n_commit_one = %s kommitti transfer.accept = Hyväksy siirto transfer.reject = Hylkää siirto default_branch_label = oletus @@ -1886,7 +1914,7 @@ need_auth = Valtuutus migrate_options = Migraatioasetukset projects.create_success = Projekti "%s" on luotu. projects.description = Kuvaus (valinnainen) -editor.commit_empty_file_header = Sitoudu tyhjä tiedosto +editor.commit_empty_file_header = Kommitoi tyhjä tiedosto editor.branch_does_not_exist = Haaraa "%s" ei ole olemassa tässä tietovarastossa. editor.delete = Poista %s editor.patching = Paikkaus: @@ -1954,18 +1982,18 @@ stored_lfs = Talletettu Git LFS:llä activity.git_stats_author_1 = %d tekijä issues.choose.blank_about = Luo ongelma oletusarvoisesta mallipohjasta. pulls.made_using_agit = AGit -editor.cannot_edit_lfs_files = LFS-tiedostoja ei voi muokata web-käyttöliittymässä. +editor.cannot_edit_lfs_files = LFS-tiedostoja ei voi muokata selainkäyttöliittymässä. pulls.cmd_instruction_hint = Näytä komentoriviohjeet settings.wiki_globally_editable = Salli kenen tahansa muokata wikiä pulls.rebase_conflict_summary = Virheviesti wiki.search = Etsi wikistä -activity.commit = Sitoutumistoiminta -editor.cannot_edit_non_text_files = Binääritiedostoja ei voi muokata web-käyttöliittymässä. +activity.commit = Kommittitoiminta +editor.cannot_edit_non_text_files = Binääritiedostoja ei voi muokata selainkäyttöliittymässä. projects.template.desc_helper = Valitse projektin mallipohja aloittaaksesi -commit.contained_in_default_branch = Tämä sitoumus on osa oletushaaraa +commit.contained_in_default_branch = Tämä kommitti on osa oletushaaraa activity.git_stats_exclude_merges = Poissulkien yhdistämiset -activity.no_git_activity = Tällä ajanjaksolla ei ole ollut sitoutumistoimintaa. -activity.git_stats_commit_1 = %d sitoumus +activity.no_git_activity = Tällä ajanjaksolla ei ole ollut kommitointitoimintaa. +activity.git_stats_commit_1 = %d kommitin activity.git_stats_push_to_all_branches = kaikkiin haaroihin. settings.graphql_url = GraphQL:n URL-osoite branch.create_new_branch = Luo haara haarasta: @@ -1984,7 +2012,7 @@ migrate.forgejo.description = Tee migraatio codeberg.orgista tai muista Forgejo- migrate.gitbucket.description = Tee migraatio GitBucket-instansseista. migrate.onedev.description = Tee migraatio code.onedev.io:sta tai muista OneDev-instansseista. migrate.codebase.description = Tee migraatio codebasehq.comista. -migrate.git.description = Siirrä tietovarasto mistä tahansa Git-palvelusta. +migrate.git.description = Suorita tietovaraston migraatio mistä tahansa Git-palvelusta. migrate.gitlab.description = Tee migraatio gitlab.comista tai muista GitLab-instansseista. migrate.gitea.description = Tee migraatio gitea.comista tai muista Gitea-instansseista. repo_gitignore_helper_desc = Valitse mitä tiedostoja ei seurata yleisimpien kielten mallipohjista. Tyypilliset artefaktit, joita eri kielten koostamistyökalut tuottavat, lisätään .gitignore-tiedostoon oletusarvoisesti. @@ -1993,36 +2021,36 @@ license_helper_desc = Lisenssi määrää, mitä muut voivat ja eivät voi tehd milestones.filter_sort.earliest_due_data = Lähin määräpäivä issues.filter_type.reviewed_by_you = Katselmoitu toimestasi settings.units.overview = Yleisnäkymä -settings.remove_team_success = Joukkueen pääsy tietovarastoon on poistettu. +settings.remove_team_success = Tiimin pääsy tietovarastoon on poistettu. migrate.cancel_migrating_confirm = Haluatko perua tämän migraation? settings.units.units = Yksiköt settings.update_settings_no_unit = Tietovaraston tulisi sallia edes jonkinlainen vuorovaikutus. settings.units.add_more = Ota lisää käyttöön -settings.add_team_success = Joukkueella on nyt pääsy tietovarastoon. -settings.use_external_issue_tracker = Käytä ulkoista ongelmienseurantaa -settings.transfer_started = Tämä tietovarasto on merkitty siirrettäväksi ja se odottaa vahvistusta "%s":lta -signing.wont_sign.pubkey = Sitoumusta ei allekirjoiteta, koska sinulla ei ole julkista avainta liitetty tiliisi. +settings.add_team_success = Tiimillä on nyt pääsy tietovarastoon. +settings.use_external_issue_tracker = Käytä ulkoista ongelmanseurantaa +settings.transfer_started = Tämä tietovarasto on merkitty siirrettäväksi ja se odottaa vahvistusta käyttäjältä "%s" +signing.wont_sign.pubkey = Kommittia ei allekirjoiteta, koska sinulla ei ole julkista avainta liitetty tiliisi. settings.transfer_succeed = Tietovarasto on siirretty. activity.git_stats_on_default_branch = Haarassa %s, settings.tracker_issue_style.regexp = Säännöllinen lauseke wiki.reserved_page = Wikisivun nimi "%s" on varattu. pulls.recently_pushed_new_branches = Työnsit haaraan %[1]s %[2]s -signing.will_sign = Tämä sitoumus allekirjoitetaan avaimella "%s". -signing.wont_sign.never = Sitoumukset eivät ole koskaan allekirjoitettuja. +signing.will_sign = Tämä kommitti allekirjoitetaan avaimella "%s". +signing.wont_sign.never = Kommitit eivät ole koskaan allekirjoitettuja. settings.mirror_settings.direction = Suunta settings.mirror_settings.push_mirror.remote_url = Git-etätietovaraston URL-osoite -settings.issues_desc = Ota tietovaraston vianseuranta käyttöön -settings.use_internal_issue_tracker = Käytä sisäänrakennettua ongelmienseurantaa -settings.external_tracker_url = Ulkoisen ongelmienseurannan URL-osoite -settings.transfer_abort_success = Tietovaraston siirto %s:hen peruttiin. +settings.issues_desc = Ota tietovaraston ongelmanseuranta käyttöön +settings.use_internal_issue_tracker = Käytä sisäänrakennettua ongelmanseurantaa +settings.external_tracker_url = Ulkoisen ongelmanseurannan URL-osoite +settings.transfer_abort_success = Tietovaraston siirto käyttäjälle %s peruttiin. settings.transfer_quota_exceeded = Uusi omistaja (%s) on ylittänyt kiintiön. Tietovarastoa ei ole siirretty. settings.projects_desc = Ota tietovarastoprojektit käyttöön settings.releases_desc = Ota tietovaraston julkaisut käyttöön settings.packages_desc = Ota tietovarastopakettien rekisteri käyttöön activity.git_stats_push_to_branch = haaraan %s ja wiki.wiki_page_revisions = Sivun versiot -settings.wiki_desc = Ota tietovaraston Wiki käyttöön -signing.wont_sign.always = Sitoumukset ovat aina allekirjoitettuja. +settings.wiki_desc = Ota tietovaraston wiki käyttöön +signing.wont_sign.always = Kommitit ovat aina allekirjoitettuja. milestones.edit_subheader = Merkkipaalut järjestävät ongelmia ja seuraavat edistymistä. view_git_blame = Näytä git blame editor.push_rejected = Tämä muutos hylättiin palvelimen toimesta. Tarkista Git-koukut. @@ -2034,26 +2062,26 @@ releases.desc = Seuraa projektin versioita ja latauksia. settings.protect_patterns = Kaavat branch.new_branch_from = Luo uusi haara kohteesta "%s" settings.matrix.message_type = Viestin tyyppi -diff.committed_by = Sitoumuksen toimijana +diff.committed_by = kommitoinut invisible_runes_line = `Tällä rivillä on näkymättömiä Unicode-merkkejä` -editor.fork_before_edit = Sinun on luotava tämän tietovaraston haarukka voidaksesi tehdä tai ehdottaa muutoksia tähän tiedostoon. +editor.fork_before_edit = Sinun täytyy forkata tämä tietovarasto tehdäksesi tai ehdottaaksesi muutoksia tähän tiedostoon. editor.file_deleting_no_longer_exists = Poistettavaa tiedostoa "%s" ei enää ole tässä tietovarastossa. editor.add_tmpl.filename = tiedostonimi editor.fail_to_apply_patch = Ei voitu toteuttaa paikkaa "%s" editor.propose_file_change = Ehdota tiedostomuutosta -editor.new_branch_name = Nimeä uusi haara tätä sitoumusta varten +editor.new_branch_name = Nimeä uusi haara tätä kommittia varten editor.new_branch_name_desc = Uuden haaran nimi… editor.file_editing_no_longer_exists = Muokattavaa tiedostoa "%s" ei enää ole tässä tietovarastossa. -editor.cannot_commit_to_protected_branch = Suojattuun haaraan "%s" ei voi sitoutua. +editor.cannot_commit_to_protected_branch = Suojattuun haaraan "%s" ei voi kommitoida. issues.remove_request_review = Poista katselmointipyyntö issues.remove_request_review_block = Katselmointipyyntöä ei voi poistaa -pulls.require_signed_wont_sign = Haara vaatii allekirjoitettuja sitoumuksia, mutta tätä yhdistämistä ei allekirjoiteta +pulls.require_signed_wont_sign = Haara vaatii allekirjoitettuja kommitteja, mutta tätä yhdistämistä ei allekirjoiteta pulls.push_rejected_summary = Koko hylkäysviesti settings.unarchive.button = Kumoa tietovaraston arkistointi release.type_attachment = Liite tag.create_tag_from = Luo uusi tagi kohteesta"%s" topic.count_prompt = Voit valita korkeintaan 25 aihetta -settings.require_signed_commits = Vaadi allekirjoitettuja sitoumuksia +settings.require_signed_commits = Vaadi allekirjoitetut kommitit editor.push_rejected_summary = Koko hylkäysviesti: release.title = Julkaisun nimi release.tag_helper_existing = Olemassa oleva tagi. @@ -2065,7 +2093,7 @@ editor.must_have_write_access = Sinulla täytyy olla kirjoitusoikeus tehdäksesi issues.re_request_review = Pyydä katselmointia uudelleen pulls.status_checks_details = Yksityiskohdat release.title_empty = Nimi ei voi olla tyhjä. -archive.title = Tämä tietovarasto on arkistoitu. Voit tarkastella sen tiedostoja ja kloonata sen, mutta et voi tehdä muutoksia sen tilaan, kuten lähettää tai luoda uusia vikalippuja, vetopyyntöjä tai kommentteja. +archive.title = Tämä tietovarasto on arkistoitu. Voit tarkastella sen tiedostoja ja kloonata sen, mutta et voi tehdä muutoksia sen tilaan, kuten tehdä työntöjä tai luoda uusia ongelmia, vetopyyntöjä tai kommentteja. reactions_more = ja %d lisää mirror_address = Kloonaa URL-osoitteesta migrate_items_merge_requests = Yhdistämispyynnöt @@ -2077,9 +2105,9 @@ settings.discord_icon_url.exceeds_max_length = Kuvakkeen URL-osoite voi sisält settings.event_wiki_desc = Wiki-sivu luotu, nimetty uudelleen, muokattu tai poistettu. settings.event_pull_request_desc = Vetopyyntö avattu, suljettu, avattu uudelleen tai muokattu. settings.protect_branch_name_pattern = Suojatun haaran nimen kaava -issues.dependency.add_error_dep_not_same_repo = Molempien vikojen tulee olla samassa tietovarastossa. +issues.dependency.add_error_dep_not_same_repo = Molempien ongelmien tulee olla samassa tietovarastossa. settings.event_release = Julkaisu -pulls.merge_pull_request = Luo yhdistämissitoumus +pulls.merge_pull_request = Luo yhdistämiskommitti settings.pull_mirror_sync_quota_exceeded = Kiintiö ylitetty, ei vedetä muutoksia. settings.wiki_rename_branch_main_notices_1 = Tätä toimintoa EI VOI perua. settings.webhook.test_delivery_desc_disabled = Aktivoi webkoukku testataksesi sitä tekaistulla tapahtumalla. @@ -2092,31 +2120,31 @@ pulls.switch_comparison_type = Vaihda vertailutyyppiä settings.hooks_desc = Webkoukut tekevät automaattisesti HTTP POST -pyyntöjä palvelimelle, kun jotkin Forgejo-tapahtumat käynnistyvät. Lue lisää webkoukkujen oppaasta. issues.num_participants_one = %d osallistuja issues.reference_link = Viittaus: %s -settings.transfer_desc = Siirrä tämä tietovarasto käyttäjälle tai organisaatiolle, johon sinulla on hallintaoikeudet. +settings.transfer_desc = Siirrä tämä tietovarasto käyttäjälle tai organisaatiolle, johon sinulla on ylläpito-oikeudet. settings.add_collaborator = Lisää avustaja -settings.mirror_settings.push_mirror.none = Push-peilejä ei ole määritetty -settings.collaborator_deletion_desc = Yhteistyöhenkilön poistaminen peruuttaa hänen pääsynsä tähän tietovarastoon. Jatketaanko? -settings.archive.text = Tietovaraston arkistointi tekee siitä kokonaan vain-lukuisen. Se piilotetaan kojelaudalta. Kukaan –et edes sinä– ei voi tehdä uusia sitoumuksia tai avata vianlippuja tai vetopyyntöjä. -settings.mirror_settings.docs = Määritä tietovarastosi synkronoimaan sitoumukset, tagit ja haarat automaattisesti toisen tietovaraston kanssa. -settings.add_collaborator_duplicate = Yhteistyöhenkilö on jo lisätty tähän tietovarastoon. -settings.add_collaborator_blocked_them = Yhteistyöhenkilöä ei voida lisätä, koska hän on estänyt tietovaraston omistajan. -settings.add_collaborator_blocked_our = Yhteistyöhenkilöä ei voi lisätä, koska tietovaraston omistaja on estänyt hänet. -settings.default_branch_desc = Valitse oletustietovaraston haara vetopyyntöjä ja koodin sitoumuksia varten: +settings.mirror_settings.push_mirror.none = Työntöpeilejä ei ole määritetty +settings.collaborator_deletion_desc = Avustajan poistaminen peruuttaa hänen pääsynsä tähän tietovarastoon. Jatketaanko? +settings.archive.text = Tietovaraston arkistointi asettaa sen pelkkään lukutilaan. Se piilotetaan kojelaudalta. Kukaan ei voi tehdä (et edes sinä) uusia kommitteja, tai avata ongelmia tai vetopyyntöjä. +settings.mirror_settings.docs = Määritä tietovarastosi synkronoimaan kommitit, tagit ja haarat automaattisesti toisen tietovaraston kanssa. +settings.add_collaborator_duplicate = Avustaja on jo lisätty tähän tietovarastoon. +settings.add_collaborator_blocked_them = Avustajaa ei voi lisätä, koska hän on estänyt tietovaraston omistajan. +settings.add_collaborator_blocked_our = Avustajaa ei voi lisätä, koska tietovaraston omistaja on estänyt hänet. +settings.default_branch_desc = Valitse tietovaraston oletushaara vetopyyntöjä ja koodin kommitteja varten: issues.role.collaborator = Avustaja -settings.trust_model.collaboratorcommitter.long = Yhteistyöhenkilö + Sitoumuksen toimija: Luota yhteistyöhenkilöiden allekirjoituksiin, jotka vastaavat sitoumuksen toimijaa +settings.trust_model.collaboratorcommitter.long = Avustaja+kommitoija: Luota avustajien allekirjoituksiin, jotka vastaavat kommitoijaa settings.collaborator_deletion = Poista avustaja wiki.desc = Kirjoita ja jaa dokumentaatiota avustajien kesken. settings.trust_model.collaborator = Avustaja -mirror_sync_on_commit = Synkronoi, kun sitoumuksia työnnetään -settings.mirror_settings.docs.disabled_pull_mirror.instructions = Määritä projektisi työntämään sitoumukset, tagit ja haarat automaattisesti toiseen tietovarastoon. Sivustosi järjestelmänvalvoja on poistanut vetopeilit käytöstä. -settings.mirror_settings.docs.more_information_if_disabled = Löydät lisätietoja push- ja pull-peileistä täältä: -settings.mirror_settings.push_mirror.add = Lisää push-peili +mirror_sync_on_commit = Synkronoi, kun kommitteja työnnetään +settings.mirror_settings.docs.disabled_pull_mirror.instructions = Määritä projektisi työntämään kommitit, tagit ja haarat automaattisesti toiseen tietovarastoon. Sivustosi järjestelmänvalvoja on poistanut vetopeilit käytöstä. +settings.mirror_settings.docs.more_information_if_disabled = Löydät lisätietoja työntö- ja vetopeileistä täältä: +settings.mirror_settings.push_mirror.add = Lisää työntöpeili settings.mirror_settings.push_mirror.edit_sync_time = Muokkaa peilin synkronoinnin aikaväliä -settings.trust_model.collaboratorcommitter = Yhteistyöhenkilö + Sitoumuksen toimija +settings.trust_model.collaboratorcommitter = Avustaja+kommitoija settings.trust_model.default = Oletusarvoinen luottamusmalli settings.admin_enable_health_check = Ota tietovaraston terveystarkastukset käyttöön (git fsck) settings.remove_collaborator_success = Avustaja on poistettu. -issues.role.collaborator_helper = Tämä käyttäjä on kutsuttu yhteistyöhön tietovaraston parissa. +issues.role.collaborator_helper = Tämä käyttäjä on kutsuttu avustajaksi tietovarastoon. settings.pulls_desc = Ota tietovaraston vetopyynnöt käyttöön mirror_interval_invalid = Peilauksen aikaväli ei ole kelvollinen. settings.collaboration = Avustajat @@ -2126,32 +2154,32 @@ mirror_interval = Peilauksen aikaväli (kelvolliset yksiköt ovat "h", "m", "s") settings.add_collaborator_success = Avustaja on lisätty. settings.add_collaborator_owner = Omistajaa ei voi lisätä avustajaksi. settings.signing_settings = Allekirjoituksen vahvistuksen asetukset -settings.mirror_settings.docs.disabled_push_mirror.instructions = Määritä projektisi automaattisesti vetämään sitoumukset, tagit ja haarat toisesta tietovarastosta. +settings.mirror_settings.docs.disabled_push_mirror.instructions = Määritä projektisi automaattisesti vetämään kommitit, tagit ja haarat toisesta tietovarastosta. settings.trust_model.collaborator.long = Avustaja: Luota avustajien allekirjoituksiin issues.dependency.setting = Käytä riippuvuuksia ongelmiin ja vetopyyntöihin settings.allow_only_contributors_to_track_time = Salli vain avustajien seurata aikaa settings.actions_desc = Käytä integroituja CI-/CD-putkia Forgejo Actionsia hyödyntäen -settings.admin_enable_close_issues_via_commit_in_any_branch = Sulje vianlippu muussa kuin oletushaarassa tehdyllä sitoumuksella +settings.admin_enable_close_issues_via_commit_in_any_branch = Sulje ongelma muussa kuin oletushaarassa tehdyllä kommitilla settings.mirror_settings.pushed_repository = Työnnetty tietovarasto pulls.compare_changes_desc = Valitse haara, johon yhdistetään, ja haara, josta vedetään. no_eol.text = Ei EOL:ää auto_init_description = Aloita Git-historia README-tiedostolla ja valinnaisesti License- ja .gitignore-tiedostoilla. new_from_template = Käytä mallipohjaa -new_from_template_description = Voit valita olemassa olevan tietovarastomallipohjan tässä ilmentymässä ja käyttää sen asetuksia. +new_from_template_description = Voit valita olemassa olevan tietovarastomallipohjan tässä instanssissa ja käyttää sen asetuksia. new_advanced = Lisäasetukset new_advanced_expand = Laajenna napsauttamalla template_description = Mallipohjaisten tietovarastojen avulla käyttäjät voivat luoda uusia tietovarastoja, joilla on sama hakemistorakenne, tiedostot ja valinnaiset asetukset. settings.enter_repo_name = Syötä omistajan ja tietovaraston nimi täsmälleen kuten esitetty: settings.confirmation_string = Vahvistusteksti -settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi %s tietovaraston, joka sisältää koodin, viat, kommentit, Wiki-tiedot ja yhteistyöhenkilön asetukset. +settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi tietovaraston %s mukaan lukien koodin, ongelmat, kommentit, wikidatan ja avustaja-asetukset. issues.filter_assginee_no_select = Kaikki käsittelijät issues.new.assign_to_me = Osoita itselle pulls.closed_at = `sulki tämän vetopyynnön %[2]s` tree_path_not_found_branch = Polkua %[1]s ei ole olemassa haarassa %[2]s transfer.no_permission_to_reject = Sinulla ei ole oikeutta hylätä tätä siirtoa. generate_repo = Luo tietovarasto -tree_path_not_found_commit = Polkua %[1]s ei ole olemassa sitoumuksessa %[2]s -archive.pull.noreview = Tämä tietovarasto on arkistoitu. Et voi arvioida vetopyyntöjä. +tree_path_not_found_commit = Polkua %[1]s ei ole olemassa kommitissa %[2]s +archive.pull.noreview = Tämä tietovarasto on arkistoitu. Et voi katselmoida vetopyyntöjä. tree_path_not_found_tag = Polkua %[1]s ei ole olemassa tagissa %[2]s transfer.no_permission_to_accept = Sinulla ei ole oikeutta hyväksyä tätä siirtoa. settings.web_hook_name_feishu = Feishu / Lark Suite @@ -2161,21 +2189,21 @@ issues.add_label = lisäsi tunnisteen %s %s issues.due_date_added = lisäsi määräpäivän %s %s issues.review.add_review_request = pyysi katselmointia käyttäjältä %[1]s %[2]s issues.ref_pull_from = `viittasi tähän vetopyyntöön %[4]s %[2]s` -pulls.commit_ref_at = `viittasi tähän vetopyyntöön sitoumuksesta %[2]s` +pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista %[2]s` issues.review.comment = katselmoi %s issues.add_labels = lisäsi tunnisteet %s %s issues.review.add_review_requests = pyysi katselmointeja käyttäjiltä %[1]s %[2]s pulls.blocked_by_official_review_requests = Tämä vetopyyntö on estetty, koska siltä puuttuu hyväksyntä yhdeltä tai useammalta viralliselta katselmoijalta. issues.author.tooltip.issue = Tämä käyttäjä on tämän ongelman tekijä. issues.author.tooltip.pr = Tämä käyttäjä on tämän vetopyynnön tekijä. -issues.role.contributor_helper = Tämä käyttäjä on aiemmin sitoutunut tähän tietovarastoon. +issues.role.contributor_helper = Tämä käyttäjä on aiemmin kommitoinut tähän tietovarastoon. settings.event_pull_request_label = Tunnisteet issues.due_date_remove = poisti määräpäivän %s %s settings.event_issue_label = Tunnisteet settings.authorization_header = Authorization-otsake diff.has_escaped = Tällä rivillä on piilotettuja Unicode-merkkejä issues.max_pinned = Et voi kiinnittää enempää ongelmia -settings.external_tracker_url_error = Ulkoisen ongelmienseurannan URL-osoite ei ole kelvollinen. +settings.external_tracker_url_error = Ulkoisen ongelmanseurannan URL-osoite ei ole kelvollinen. settings.event_pull_request_review = Katselmoinnit settings.event_pull_request_review_request = Katselmointipyynnöt issues.num_reviews_one = %d katselmointi @@ -2206,27 +2234,311 @@ delete_preexisting = Poista olemassa olevat tiedostot issues.reaction.add = Lisää reaktio pulls.merged_by = %[3]s:n yhdistettiin %[1]s pulls.merged_by_fake = %[2]s:n yhdistettiin %[1]s -delete_preexisting_success = Poistetut omaksumattomat tiedostot %s:ssa +delete_preexisting_success = Poistetut omaksumattomat tiedostot kohteesta %s pulls.no_merge_wip = Tätä vetopyyntöä ei voida yhdistää, koska se on merkitty keskeneräiseksi työksi. pulls.clear_merge_message = Tyhjennä yhdistämisviesti activity.title.prs_merged_by = %s yhdisti %s -settings.protect_status_check_patterns_desc = Syötä kuviot määrittääksesi, mitkä tilatarkistukset on läpäistävä ennen kuin haarat voidaan yhdistää tätä sääntöä vastaavaan haaraan. Jokainen rivi määrittää kuvion. Kuviot eivät saa olla tyhjiä. +settings.protect_status_check_patterns_desc = Syötä kaavat määrittääksesi, mitkä tilatarkistukset on läpäistävä, ennen kuin haarat voidaan yhdistää tätä sääntöä vastaavaan haaraan. Jokainen rivi määrittää kaavan. Kaavat eivät saa olla tyhjiä. adopt_search = Syötä käyttäjänimi etsiäksesi omaksumattomia tietovarastoja (jätä tyhjäksi löytääksesi kaikki) pulls.cmd_instruction_merge_warning = Varoitus: Asetusta ”Tunnista manuaalinen yhdistäminen automaattisesti” ei ole otettu käyttöön tässä tietovarastossa. Sinun on merkittävä tämä vetopyyntö manuaalisesti yhdistetyksi jälkikäteen. pulls.cmd_instruction_merge_desc = Yhdistä muutokset ja päivitä Forgejossa. pulls.cannot_auto_merge_desc = Tätä vetopyyntöä ei voida yhdistää automaattisesti ristiriitojen vuoksi. -adopt_preexisting_success = Omaksutut tiedostot ja luotu tietovarasto %s:lta -issues.comment_manually_pull_merged_at = manuaalisesti yhdistetty sitoumus %[1]s %[2]s:hen %[3]s +adopt_preexisting_success = Omaksuttu tiedostot ja luotu tietovarasto lähteestä %s +issues.comment_manually_pull_merged_at = manuaalisesti yhdistetty kommitti %[1]s %[2]s tietovarastoon %[3]s pulls.cmd_instruction_merge_title = Yhdistä pulls.has_merged = Epäonnistui: vetopyyntö on yhdistetty, joten et voi yhdistää uudelleen tai muuttaa kohdehaaraa. pulls.cmd_instruction_checkout_title = Uloskuittaus -pulls.cmd_instruction_checkout_desc = Projektitietovarastostasi, kuita ulos uusi haara ja testaa muutokset. -pulls.clear_merge_message_hint = Yhdistämisviestin tyhjentäminen poistaa vain sitoumusviestin sisällön ja säilyttää luodut git-trailerit, kuten "Yhteistekijänä …". -settings.protect_check_status_contexts_desc = Vaadi tilatarkistusten läpäisy ennen yhdistämistä. Kun se on otettu käyttöön, sitoumukset on ensin työnnettävä toiseen haaraan ja sitten yhdistettävä tai työnnettävä suoraan tätä sääntöä vastaavaan haaraan tilantarkistuksen jälkeen. Jos konteksteja ei löydy, viimeisen sitoumuksen on oltava onnistunut kontekstista riippumatta. -issues.comment_pull_merged_at = yhdistetty sitoumus %[1]s %[2]s :hen %[3]s +pulls.cmd_instruction_checkout_desc = Kuittaa ulos uusi haara projektitietovarastostasi ja testaa muutokset. +pulls.clear_merge_message_hint = Yhdistämisviestin tyhjentäminen poistaa vain kommittiviestin sisällön ja säilyttää luodut git-trailerit, kuten "Co-Authored-By…". +settings.protect_check_status_contexts_desc = Vaadi tilatarkistusten läpäisy ennen yhdistämistä. Kun käytössä, kommitit on ensin työnnettävä toiseen haaraan ja sitten yhdistettävä tai työnnettävä suoraan tätä sääntöä vastaavaan haaraan tilantarkistuksen jälkeen. Jos konteksteja ei löydy, viimeisen kommitin on oltava onnistunut kontekstista riippumatta. +issues.comment_pull_merged_at = yhdistetty kommitti %[1]s %[2]s tietovarastoon %[3]s settings.pulls.enable_autodetect_manual_merge = Ota Tunnista manuaalinen yhdistäminen automaattisesti -asetus käyttöön (Huomaa: joissakin erityistapauksissa voi esiintyä virhearviointeja) pulls.no_merge_desc = Tätä vetopyyntöä ei voida yhdistää, koska kaikki tietovaraston yhdistämisvaihtoehdot ovat poistettu käytöstä. -pulls.no_merge_not_ready = Tämä vetopyyntö ei ole valmis yhdistettäväksi. Tarkista arvioinnin tila ja tilantarkistukset. +pulls.no_merge_not_ready = Tämä vetopyyntö ei ole valmis yhdistettäväksi. Tarkista katselmoinnin tila ja tilantarkistukset. +issues.is_stale = Tähän vetopyyntöön on kohdistunut muutoksia tämän katselmoinnin jälkeen +migrate.repo_desc_helper = Jätä tyhjäksi tuodaksesi olemassa olevan kuvauksen +issues.role.first_time_contributor = Avustaja ensimmäistä kertaa +issues.role.contributor = Avustaja +activity.opened_prs_label = Ehdotettu +settings.require_signed_commits_desc = Hylkää työnnöt tähän haaraan, jos niitä ei ole allekirjoitettu tai ne eivät ole vahvistettuja. +issues.role.first_time_contributor_helper = Tämä on käyttäjän ensimmäinen kontribuutio tähän tietovarastoon. +editor.upload_files_to_dir = Lähetä tiedostot hakemistoon "%s" +adopt_preexisting = Omaksu olemassa olevat tiedostot +issues.dismiss_review_warning = Haluatko hylätä katselmoinnin? +commit.operations = Toimenpiteet +commits.view_single_diff = Näytä tässä kommitissa tähän tiedostoon kohdistuneet muutokset +issues.choose.ignore_invalid_templates = Virheelliset mallipohjat on jätetty huomiotta +migrate.migrating_milestones = Suoritetaan merkkipaalujen migraatiota +migrate.migrating_issues = Suoritetaan ongelmien migraatiota +migrate.clone_local_path = tai paikallisen palvelimen polku +pulls.filter_changes_by_commit = Suodata kommitin perusteella +pulls.show_changes_since_your_last_review = Näytä viimeisimmän katselmointisi jälkeiset muutokset +pulls.cant_reopen_deleted_branch = Tätä vetopyyntöä ei voi avata uudelleen, koska haara poistettiin. +mirror_sync = synkronoitu +mirror_lfs_endpoint = LFS-päätepiste +language_other = Muu +adopt_preexisting_label = Omaksu tiedostot +issues.role.member_helper = Tämä käyttäjä on tietovaraston omistavan organisaation jäsen. +migrate.invalid_local_path = Paikallinen polku on virheellinen. Sitä ei ole olemassa tai se ei ole hakemisto. +migrate.invalid_lfs_endpoint = LFS-päätepiste ei ole kelvollinen. +issues.new.clear_projects = Tyhjennä projektit +mirror_denied_combination = Julkiseen avaimeen ja salasanaan pohjautuvaa todennusta ei voi käyttää yhdessä. +template.git_content = Git-sisältö (Oletushaara) +migrate.migrating_releases = Suoritetaan julkaisujen migraatiota +unit_disabled = Sivuston ylläpitäjä on poistanut käytöstä tämän tietovarasto-osion. +issues.filter_sort.relevance = Asiaankuuluvuus +pulls.reopen_to_merge = Avaa tämä vetopyyntö uudelleen suorittaaksesi yhdistämisen. +archive.nocomment = Kommentointi ei ole mahdollista, koska tietovarasto on arkistoitu. +projects.column.set_default_desc = Aseta tämä sarake oletukseksi luokittelemattomille ongelmille ja vedoille +issues.review.remove_review_request_self = kieltäytyi katselmoimasta %s +from_comment = (kommentti) +issues.dismiss_review = Hylkää katselmointi +editor.file_changed_while_editing = Tiedoston sisältö on muuttunut sen avaamisen jälkeen. Napsauta tästä nähdäksesi muutokset tai kommitoi muutokset uudelleen korvataksesi muutokset. +sync_fork.button = Synkronoi +migrated_from = Suoritettu migraatio lähteestä %[2]s +migrate.migrating_topics = Suoritetaan aiheiden migraatiota +migrate.migrating_pulls = Suoritetaan vetopyyntöjen migraatiota +migrate.cancel_migrating_title = Peruuta migraatio +file_follow = Seuraa symbolista linkkiä +commit.load_referencing_branches_and_tags = Lataa haarat ja tagit, jotka viittaavat tähän kommittiin +editor.commit_id_not_matching = Tiedosto muuttui sillä aikaa, kun muokkasit sitä. Kommitoi uuteen haaraan ja yhdistä sen jälkeen. +projects.new_subheader = Koordinoi, seuraa ja päivitä työtä yhdessä paikassa, jotta projektit pysyvät läpinäkyvinä ja aikataulussa. +issues.del_time_history = `poisti käytetyn ajan %s` +issues.dependency.removed_dependency = `poisti riippuvuuden %s` +issues.dependency.add_error_same_issue = Et voi tehdä ongelmaa riippuvaiseksi itsestään. +projects.card_type.desc = Korttiesikatselut +issues.dependency.added_dependency = `lisäsi uuden riippuvuuden %s` +fork_no_valid_owners = Tätä tietovarastoa ei voi forkata, koska sillä ei ole kelvollisia omistajia. +fork_branch = Forkkiin kloonattava haara +editor.must_be_on_a_branch = Sinun tulee olla haarassa, jotta voit tehdä tai ehdottaa muutoksia tähän tiedostoon. +editor.commit_message_desc = Lisää valinnainen, laajennettu kuvaus… +migrate_options_lfs = Tee migraatio LFS-tiedostoille +migrate_options_lfs_endpoint.label = LFS-päätepiste +commits.browse_further = Selaa kauemmas +issues.filter_projects = Suodata projekti +issues.filter_labels = Suodata tunniste +commits.no_commits = Ei yhteisiä kommitteja. "%s" ja "%s" omaavat täysin eri historiat. +projects.column.deletion_desc = Projektin sarakkeen poistaminen siirtää kaikki siihen liittyvät ongelmat oletussarakkeeseen. Jatketaanko? +issues.del_time = Poista tämä aikaloki +migrated_from_fake = Suoritettu migraatio lähteestä %[1]s +migrate.migrate = Tee migraatio lähteestä %s +migrate.migrating_labels = Suoritetaan tunnisteiden migraatiota +file_view_rendered = Näytä renderöitynä +editor.invalid_commit_mail = Virheellinen sähköposti kommitin luomista varten. +sync_fork.branch_behind_one = Tämä haara on %[1]d kommitin jäljessä %[2]s +sync_fork.branch_behind_few = Tämä haara on %[1]d kommittia jäljessä %[2]s +no_eol.tooltip = Tämä tiedosto ei sisällä lopussa olevaa rivin loppu -merkkiä. +issues.filter_reviewers = Suodata katselmoija +commits.view_path = Näytä tässä historian kohdassa +commit.revert = Palauta +commit.revert-header = Palauta: %s +mirror_use_ssh.not_available = SSH-todennus ei ole käytettävissä. +pulls.showing_specified_commit_range = Näytetään vain muutokset välillä %[1]s..%[2]s +pulls.select_commit_hold_shift_for_range = Valitse kommitti. Pidä pohjassa shift + napsauta valitaksesi alueen +blame_prior = Näytä blame ennen tätä muutosta +migrate.migrating_failed.error = Migraatio epäonnistui: %s +editor.no_commit_to_branch = Kommitointi suoraan haaraan ei onnistu syystä: +editor.user_no_push_to_branch = Käyttäjä ei voi työntää haaraan +issues.delete.text = Haluatko varmasti poistaa tämän ongelman? (Sisältö poistetaan pysyvästi. Harkitse sen sijaan sulkemista, jos haluat pitää ongelman arkistoituna) +issues.context.menu = Kommenttivalikko +archive.title_date = Tämä tietovarasto arkistoitiin %s. Voit tarkastella sen tiedostoja ja kloonata sen, mutta et voi tehdä muutoksia sen tilaan, kuten tehdä työntöjä tai luoda uusia ongelmia, vetopyyntöjä tai kommentteja. +pulls.cannot_auto_merge_helper = Yhdistä manuaalisesti selvittääksesi konfliktit. +pulls.merge_conflict = Yhdistäminen epäonnistui: Yhdistämisen aikana tapahtui ristiriita. Vihje: Kokeile eri strategiaa +pulls.is_checking = Yhdistämisen ristiriidan tarkistus on meneillään. Kokeile uudelleen hetken kuluttua. +pulls.is_empty = Tässä haarassa olevat muutokset ovat jo kohdehaarassa. Tästä tulee tyhjä kommitti. +pulls.required_status_check_administrator = Ylläpitäjänä voit silti yhdistää tämän vetopyynnön. +pulls.required_status_check_failed = Jotkin vaaditut tarkistukset eivät onnistuneet. +pulls.required_status_check_missing = Jotkin vaaditut tarkistukset puuttuvat. +pulls.status_checks_failure = Jotkin tarkistukset epäonnistuivat +pulls.status_checks_show_all = Näytä kaikki tarkistukset +pulls.auto_merge_cancel_schedule = Peru automaattinen yhdistäminen +pulls.auto_merge_newly_scheduled = Tämä vetopyyntö aikataulutettiin yhdistettäväksi, kun kaikki tarkistukset onnistuvat. +pulls.auto_merge_not_scheduled = Tätä vetopyyntöä ei ole aikataulutettu automaattisesti yhdistettäväksi. +comments.edit.already_changed = Muutosten tallentaminen kommenttiin epäonnistui. Vaikuttaa siltä, että sisältöä on jo muutettu toisen käyttäjän toimesta. Päivitä sivu ja muokkaa uudelleen välttääksesi ylikirjoittamasta muiden muutoksia +signing.wont_sign.nokey = Tässä instanssissa ei ole avainta tämän kommitin allekirjoittamiseksi. +pulls.is_ancestor = Tämä haara on jo sisällytetty kohdehaaraan. Yhdistettävää ei ole. +pulls.blocked_by_rejection = Tämä vetopyyntö sisältää virallisen katselmoijan vaatimisia muutoksia. +pulls.status_checks_success = Kaikki tarkistukset onnistuivat +pulls.agit_explanation = Luotu käyttäen AGit-työnkulkua. AGit antaa avustajien ehdottaa muutoksia käyttämällä "git push" ilman, että uutta forkkia tai uutta haaraa luodaan. +milestones.invalid_due_date_format = Määräpäivän muodon tulee olla "yyyy-mm-dd". +wiki.original_git_entry_tooltip = Näytä alkuperäinen Git-tiedosto sen sijaan, että ystävällistä linkkiä käytetään. +pulls.blocked_by_approvals = Tällä vetopyynnöllä ei ole riittävästi hyväksyntöjä. %d/%d hyväksyntää myönnetty. +pulls.status_checks_hide_all = Piilota kaikki tarkistukset +pulls.blocked_by_user = Et voi luoda vetopyyntöä tähän tietovarastoon, koska tietovarastojan omistaja on estänyt sinut. +pulls.delete.text = Haluatko varmasti poistaa tämän vetopyynnön? (Kaikki sisältö poistetaan pysyvästi. Harkitse sen sijaan sulkemista, jos haluat pitää arkistoituna) +wiki.page_name_desc = Kirjoita tämän wikisivun nimi. Joitain erikoisnimiä ovat: "Home", "_Sidebar" ja "_Footer". +pulls.blocked_by_changed_protected_files_1 = Tämä vetopyyntö sisältää suojatun tiedoston ja on siksi estetty: +pulls.status_checks_warning = Jotkin tarkistukset raportoivat varoituksia +pulls.status_checks_error = Jotkin tarkistukset raportoivat virheitä +pulls.reopened_at = `avasi uudelleen tämän vetopyynnön %[2]s` +pulls.auto_merge_when_succeed = Yhdistä automaatisesti kun kaikki tarkistukset onnistuvat +signing.wont_sign.error = Tapahtui virhe tarkistaessa voiko kommitin allekirjoittaa. +signing.wont_sign.twofa = Sinulla tulee olla kaksivaiheinen todennus käytössä, jotta kommitit voi allekirjoittaa. +pulls.data_broken = Tämä vetopyyntö on rikki johtuen puuttuvasta forkkitiedosta. +pulls.files_conflicted = Tämä vetopyyntö sisältää muutoksia, jotka ovat ristiriidassa kohdehaaran kanssa. +pulls.auto_merge_button_when_succeed = (Kun tarkistukset onnistuvat) +pulls.blocked_by_outdated_branch = Tämä vetopyyntö on vanhentunut ja siksi estetty. +pulls.blocked_by_changed_protected_files_n = Tämä vetopyyntö on estetty, koske se muuttaa suojattuja tiedostoja: +pulls.status_checking = Jotkin tarkistukset odottavat +pulls.auto_merge_canceled_schedule = Tämän vetopyynnön automaattinen yhdistäminen peruttiin. +activity.title.prs_opened_by = %s ehdottanut %s +settings.convert_fork = Konvertoi tavalliseksi tietovarastoksi +settings.convert_fork_confirm = Konvertoi tietovarasto +settings.add_collaborator_inactive_user = Inaktiivista käyttäjää ei voi lisätä avustajaksi. +settings.mirror_settings.docs.pulling_remote_title = Etätietovarastosta vetäminen +settings.pull_mirror_sync_in_progress = Vedetään muutoksia etäpuolelta %s tällä hetkellä. +settings.convert = Konvertoi tavalliseksi tietovarastoksi +settings.transfer_in_progress = Meneillään on jo siirto. Peruuta siirto, jos haluat siirtää tämän tietovaraston toiselle käyttäjälle. +settings.trust_model.collaborator.desc = Tämän tietovaraston avustajien kelvolliset allekirjoitukset merkitään luotetuiksi ("trusted") - (ottamatta kantaa, vastaavatko he kommitoijaa vai eivät). Muussa tapauksessa allekirjoitetukset merkitään ei-luotetuiksi ("untrusted"), jos allekirjoitus vastaa kommitoijaa, ja vastaamaton ("unmatched") jos ei. +settings.confirm_wiki_branch_rename = Nimeä uudelleen wikin haara +settings.event_pull_request_assign = Toimeksianto +settings.event_pull_request_assign_desc = Vetopyynnön toimeksianto luotu tai toimeksiannon osoitus poistettu. +settings.event_pull_request_label_desc = Vetopyynnön tunnisteita lisätty tai poistettu. +settings.active = Aktiivinen +settings.packagist_api_token = API-poletti +settings.protect_whitelist_committers = Sallittujen listalla rajoitettu työntö +settings.protect_enable_merge = Ota yhdistäminen käyttöön +settings.protect_enable_merge_desc = Kenen tahansa kirjoituspääsyllä sallitaan yhdistää vetopyynnöt tähän haaraan. +settings.protect_status_check_patterns = Tilatarkistuskaavat +settings.protect_invalid_status_check_pattern = Virheellinen tilatarkistuksen kaava: "%s". +settings.protect_no_valid_status_check_patterns = Ei kelvollisia tilatarkistuksen kaavoja. +settings.protect_required_approvals_desc = Salli yhdistäminen vain vetopyynnöille, joilla on riittävästi positiivisia katselmointeja. +settings.protect_protected_file_patterns = Suojattujen tiedostojen kaavat (erotettu puolipisteellä ";") +settings.remove_protected_branch_success = Haaran suojaus säännölle "%s" on poistettu. +settings.enforce_on_admins = Pakota tämä sääntö tietovaraston ylläpitäjiin +settings.add_key_success = Toimitusavain "%s" on lisätty. +settings.update_mirror_settings = Päivitä peilin asetukset +settings.tracker_url_format_error = Ulkoisen ongelmanseurannan URL-muoto ei ole kelvollinen URL-osoite. +settings.wiki_rename_branch_main = Normalisoi Wiki-haaran nimi +settings.wiki_delete_notices_1 = - Tämä poistaa pysyvästi wikin ja poistaa tietovaraston %s wikin käytöstä. +settings.webhook.replay.description_disabled = Toista webkoukku aktivoimalla se. +settings.add_webhook_desc = Forgejo lähettää POST-pyyntöjä määritetyllä "Content-Type":llä kohde-URL-osoitteeseen. Lue lisää webkoukkujen oppaasta. +activity.title.unresolved_conv_n = %d selvittämätöntä keskustelua +activity.title.releases_published_by = %s julkaissut %s +settings.desc = Asetuksissa voit hallita tietovaraston asetuksia +settings.external_tracker_url_desc = Kävijät ohjataan ulkoisen ongelmanseurannan URL-osoitteeseen, kun Ongelmat-välilehteä napsautetaan. +settings.tracker_issue_style = Ulkoisen ongelmanseurannan numeromuoto +settings.tracker_issue_style.regexp_pattern = Säännöllisen lausekkeen kaava +settings.convert_confirm = Konvertoi tietovarasto +settings.webhook.delivery.success = Tapahtuma on lisätty toimitusjonoon. Saattaa kestää muutama sekunti, ennen kuin se näkyy toimitushistoriassa. +settings.event_pull_request_approvals = Vetopyynnön hyväksynnät +settings.protect_enable_push = Ota työntö käyttöön +settings.protect_whitelist_committers_desc = Vain sallittujen listalla olevat käyttäjät tai tiimit sallitaan työntää tähän haaraan (mutta ei työntää väkisin). +settings.block_rejected_reviews = Estä yhdistäminen hylätyillä katselmoinneilla +settings.is_writable = Ota kirjoituspääsy käyttöön +settings.delete_notices_fork_1 = - Poistamisen jälkeen tämän tietovaraston forkeista tulee itsenäisiä. +settings.event_pull_request_review_request_desc = Vetopyynnön katselmointi pyydetty tai katselmointipyyntö poistettu. +settings.event_pull_request_merge = Vetopyynnön yhdistäminen +settings.protect_approvals_whitelist_enabled = Rajoita hyväksynnät vain sallittujen käyttäjien tai tiimien listoilla oleviin +settings.packagist_package_url = Packagist-paketin URL-osoite +settings.packagist_username = Packagist-käyttäjätunnus +settings.sourcehut_builds.manifest_path = Koontimanifestin polku +settings.event_pull_request_sync_desc = Haara päivitetty automaattisesti kohdehaaralla. +settings.trust_model.committer = Kommitoija +settings.convert_succeed = Peili on konvertoitu tavalliseksi tietovarastoksi. +settings.transfer_notices_1 = - Menetät pääsyn tähän tietovarastoon, jos siirrät sen yksittäiselle käyttäjälle. +settings.githooks_desc = Git-koukuista vastaa Git itse. Voit muokata koukkutiedostoja alla määrittääksesi omavalintaisia toimenpiteitä. +settings.update_protect_branch_success = Haaran suojaus säännölle "%s" on päivitetty. +settings.mirror_settings.direction.push = Työntö +settings.convert_desc = Voit konvertoida tämän peilin tavalliseksi tietovarastoksi. Tätä ei voi perua. +settings.trust_model.committer.long = Kommitoija: Luota allekirjoituksiin, jotka vastaavat kommitoijia (Tämä vastaa GitHubia ja pakottaa Forgejo-allekirjoitetut kommitit olemaan Forgejo-käyttäjän kommitoimia) +settings.protected_branch_deletion_desc = Haaran suojauksen poistaminen käytöstä sallii käyttäjien kirjoitusoikeudella työntää haaraan. Jatketaanko? +settings.admin_code_indexer = Koodin indeksoija +settings.remove_protected_branch_failed = Haaran suojaussäännön "%s" poistaminen epäonnistui. +settings.block_outdated_branch = Estä yhdistäminen jos vetopyyntö on vanhentunut +settings.protect_branch_name_pattern_desc = Suojattujen haarojen nimien kaavat. Lue dokumentaatio kaavojen syntaksista. Esimerkkejä: main, release/** +settings.block_rejected_reviews_desc = Yhdistäminen ei ole mahdollista, kun viralliset katselmoijat pyytävät muutoksia, siitä huolimatta että hyväksyntöjä olisi riittävästi. +settings.convert_notices_1 = Tämä toimenpide konvertoi peilin tavalliseksi tietovarastoksi, eikä toimenpidettä voi perua. +settings.convert_fork_notices_1 = Tämä toimenpide konvertoi forkin tavalliseksi tietovarastoksi, eikä toimenpidettä voi perua. +settings.convert_fork_succeed = Forkki on konvertoitu tavalliseksi tietovarastoksi. +settings.transfer_abort_invalid = Et voi perua olematonta tietovaraston siirtoa. +settings.trust_model.default.desc = Käytä oletusarvoista tietovaraston luottamusmallia tälle asennukselle. +settings.enforce_on_admins_desc = Tietovaraston ylläpitäjät eivät voi ohittaa tätä sääntöä. +settings.reindex_requested = Uudelleenindeksointi pyydetty +settings.admin_stats_indexer = Kooditilastojen indeksoija +settings.admin_indexer_commit_sha = Viimeksi indeksoitu kommitti +settings.webhook.replay.description = Toista webkoukku uudelleen. +settings.event_push_only = Työntötapahtumat +settings.authorization_header_desc = Sisällytetään Authorization-otsakkeeseen pyynnöissä. Esimerkkejä: %s. +settings.protect_approvals_whitelist_teams = Katselmointeihin oikeutettujen tiimien lista +settings.federation_not_enabled = Federaatiota ei ole käytössä käyttämässäsi instanssissa. +settings.mirror_settings.push_mirror.none_ssh = Ei mitään +settings.mirror_settings.direction.pull = Veto +settings.push_mirror_sync_in_progress = Työnnetään muutoksia etäpuoleen %s tällä hetkellä. +settings.transfer_notices_2 = - Säilytät pääsyn tietovarastoon, jos siirrät sen organisaatiolle, jonka omistat kokonaan tai osittain. +settings.protect_merge_whitelist_teams = Yhdistämiseen oikeutettujen tiimien lista +settings.protect_check_status_contexts_list = Tilatarkistuksia löytyi tälle tietovarastolle viime viikolta +settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Tällä hetkellä tämä on mahdollista vain valitsemalla "Uusi migraatio" valikosta. Saadaksesi lisätietoja: +settings.mirror_settings.docs.disabled_push_mirror.info = Työntöpeilit on poistettu käytöstä sivuston ylläpidon toimesta. +settings.mirror_settings.mirrored_repository = Peilattu tietovarasto +settings.protect_check_status_contexts = Ota tilatarkistus käyttöön +settings.pulls.default_delete_branch_after_merge = Poista vetopyynnön haara yhdistämisen jälkeen oletusarvoisesti +settings.content_type = POST-sisältötyyppi +settings.event_pull_request_comment_desc = Vetopyynnön kommentti luotu, muokattu tai poistettu. +settings.event_pull_request_review_desc = Vetopyyntö hyväksytty, hylätty tai katselmoinnin kommentteja lisätty. +settings.admin_indexer_unindexed = Indeksöimätön +settings.reindex_button = Lisää uudelleenindeksoinnin jonoon +settings.web_hook_name_wechatwork = WeCom (Wechat Work) +settings.delete_team_tip = Tällä tiimillä on pääsy kaikkiin tietovarastoihin, eikä sitä voi poistaa +settings.sourcehut_builds.visibility = Työn näkyvyys +settings.sourcehut_builds.access_token_helper = Pääsypoletti, jolla on myöntö JOBS:RW. Luo builds.sr.ht-poletti tai builds.sr.ht-poletti pääsyllä salaisuuksiin meta.sr.ht:ssä. +settings.team_not_in_organization = Tiimi ei ole samassa organisaatiossa kuin tietovarasto +settings.add_team_duplicate = Tiimillä on jo tietovarasto +settings.add_webhook.invalid_channel_name = Webkoukun kanavan nimi ei voi olla tyhjä, eikä se voi sisältää vain merkkiä #. +settings.sourcehut_builds.secrets_helper = Anna työlle pääsy koontisalaisuuksiin (vaatii myönnön SECRETS:RO) +settings.protect_disable_push_desc = Tähän haaraan ei sallita työntämistä. +settings.protect_disable_push = Poista työntö käytöstä +activity.unresolved_conv_desc = Näitä äskettäin muuttuneita ongelmia ja vetopyyntöjä ei ole vielä selvitetty. +activity.title.unresolved_conv_1 = %d selvittämätön keskustelu +activity.git_stats_files_changed_1 = on muuttunut +settings.convert_fork_desc = Voit konvertoida tämän forkin tavalliseksi tietovarastoksi. Tätä ei voi perua. +settings.protect_merge_whitelist_committers = Ota yhdistämiseen sallittu lista käyttöön +settings.protect_whitelist_teams = Työntämiseen oikeutettujen tiimien lista +settings.protect_enable_push_desc = Kenen tahansa kirjoituspääsyllä sallitaan työntää tähän haaraan (mutta ei työntää väkisin). +settings.change_team_access_not_allowed = Tiimin pääsyn muuttaminen tietovarastoon on rajoitettu organisaation omistajaan +settings.change_team_permission_tip = Tiimin käyttöoikeus on asetettu tiimin asetussivulla, eikä sitä voi muuttaa tietovarastokohtaisesti +settings.rename_branch_failed_protected = Haaraa %s ei voi nimetä uudelleen, koska se on suojattu haara. +release.hide_archive_links = Piilota automaattisesti luodut arkistot +diff.review.self_reject = Vetopyyntöjen tekijät eivät voi pyytää muutoksia omiin vetopyyntöihinsä +settings.unarchive.text = Tietovaraston arkistoinnin kumoaminen palauttaa mahdollisuuden vastaanottaa kommitteja ja työntöjä, sekä luoda uusia ongelmia ja vetopyyntöjä. +settings.unarchive.success = Tietovaraston arkistointi kumottiin. +settings.update_avatar_success = Tietovaraston profiilikuva päivitettiin. +diff.review.self_approve = Vetopyyntöjen tekijät eivät voi hyväksyä omia vetopyyntöjään +tag.ahead.target = haaraan %s tämän tagin jälkeen +release.add_tag_msg = Käytä otsikkoa ja julkaisun sisältöä tagin viestinä. +branch.rename_branch_to = Aseta haaran "%s" uudeksi nimeksi: +error.csv.too_large = Tätä tiedostoa ei voi renderöidä, koska se on liian suuri. +settings.unarchive.error = Tietovaraston arkistointia kumotessa tapahtui virhe. Katso lisätietoja lokista. +settings.rename_branch_failed_exist = Haaraa ei voi nimetä uudelleen, koska kohdehaara %s on olemassa. +release.tag_helper_new = Uusi tagi. Tämä tagi luodaan kohteesta. +diff.review = Viimeistele katselmointi +error.broken_git_hook = Tämän tietovaraston Git-koukut vaikuttavat olevan rikki. Seuraa dokumentaation ohjeita korjataksesi koukut, sen jälkeen työnnä kommitteja päivittääksesi tilan. +error.csv.unexpected = Tätä tiedostoa ei voi renderöidä, koska se sisältää odottamattoman merkin rivillä %d ja sarakkeessa %d. +settings.archive.error = Tietovarastoa arkistoitaessa tapahtui virhe. Katso lisätietoja lokista. +diff.comment.add_line_comment = Lisää rivikommentti +settings.lfs_lock = Lukitse +settings.chat_id = Keskustelun ID-tunniste +settings.thread_id = Ketjun ID-tunniste +settings.archive.mirrors_unavailable = Peilit eivät ole saatavilla arkistoiduissa tietovarastoissa. +branch.tag_collision = Haaraa "%s" ei voi luoda, koska tagi samalla nimellä on jo olemassa tietovarastossa. +diff.git-notes.remove-header = Poista huomautus +diff.git-notes.remove-body = Tämä huomautus poistetaan. +diff.git-notes.add = Lisää huomautus +release.invalid_external_url = Virheellinen ulkoinen URL-osoite: "%s" +release.asset_external_url = Ulkoinen URL-osoite +branch.delete_branch_has_new_commits = Haaraa "%s" ei voi poistaa, koska uusia kommitteja on lisätty yhdistämisen jälkeen. +branch.branch_name_conflict = Haaran nimi "%s" on ristiriidassa olemassa olevan haaran "%s" kanssa. +settings.unarchive.header = Kumoa tämän tietovaraston arkistointi +settings.matrix.room_id = Huoneen ID-tunniste +issues.blocked_by_user = Et voi luoda ongelmia tähän tietovarastoon, koska tietovaraston omistaja on estänyt sinut. +pulls.desc = Ota vetopyynnöt ja koodikatselmoinnit käyttöön. +pulls.push_rejected = Työntö epäonnistui: Työntö hylättiin. Katselmoi tämän tietovaraston Git-koukut. +form.name_reserved = Tietovaraston nimi "%s" on varattu. +form.reach_limit_of_creation_1 = Omistajan %d tietovaraston rajoitus on jo täynnä. +form.reach_limit_of_creation_n = Omistajan %d tietovaraston rajoitus on jo täynnä. +form.string_too_long = Merkkijono on pidempi kuin %d merkkiä. +mirror_address_protocol_invalid = Määritetty URL-osoite on virheellinen. Vain http(s):// tai git:// -sijainteja voi käyttää peilaukseen. +form.name_pattern_not_allowed = Kaava "%s" ei ole sallittu tietovaraston nimessä. @@ -2234,8 +2546,8 @@ pulls.no_merge_not_ready = Tämä vetopyyntö ei ole valmis yhdistettäväksi. T component_loading_info = Tämä saattaa kestää hetken… component_failed_to_load = Odottamaton virhe. component_loading = Ladataan %s… -contributors.what = panokset -recent_commits.what = viimeaikaiset sitoumukset +contributors.what = kontribuutiot +recent_commits.what = viimeaikaiset kommitit code_frequency.what = koodifrekvenssi component_loading_failed = Ei voitu ladata %s @@ -2248,7 +2560,7 @@ repo_updated=Päivitetty %s members=Jäsenet teams=Tiimit lower_members=jäsentä -lower_repositories=repot +lower_repositories=tietovarastot create_new_team=Uusi tiimi create_team=Luo tiimi org_desc=Kuvaus @@ -2263,10 +2575,10 @@ team_unit_desc=Salli pääsy tietovaraston osioihin settings=Asetukset settings.options=Organisaatio settings.full_name=Koko nimi -settings.website=Nettisivu +settings.website=Verkkosivusto settings.location=Sijainti settings.permission=Käyttöoikeudet -settings.repoadminchangeteam=Repon ylläpitäjä voi lisätä ja poistaa pääsyn tiimeihin +settings.repoadminchangeteam=Tietovaraston ylläpitäjä voi lisätä ja poistaa pääsyn tiimeihin settings.visibility=Näkyvyys settings.visibility.public=Julkinen settings.visibility.limited_shortname=Rajattu @@ -2276,10 +2588,10 @@ settings.visibility.private_shortname=Yksityinen settings.update_settings=Päivitä asetukset settings.delete=Poista organisaatio settings.delete_account=Poista tämä organisaatio -settings.delete_prompt=Organisaatio poistetaan pysyvästi, ja tätä EI VOI peruuttaa myöhemmin! +settings.delete_prompt=Organisaatio poistetaan pysyvästi, ja tätä EI VOI perua myöhemmin! settings.confirm_delete_account=Vahvista poisto settings.delete_org_title=Poista organisaatio -settings.hooks_desc=Lisää webkoukkuja, jotka suoritetaan kaikissa repoissa tässä organisaatiossa. +settings.hooks_desc=Lisää webkoukkuja, jotka suoritetaan kaikissa tietovarastoissa tässä organisaatiossa. members.membership_visibility=Jäsenyyden näkyvyys: @@ -2292,52 +2604,52 @@ members.owner=Omistaja members.member=Jäsen members.remove=Poista members.leave=Poistu -members.invite_desc=Lisää uusi jäsen %s: +members.invite_desc=Lisää uusi jäsen organisaatioon %s: members.invite_now=Kutsu nyt teams.join=Liity teams.leave=Poistu -teams.read_access=Luettu +teams.read_access=Lue teams.read_access_helper=Tiimin jäsenet voivat katsella ja kloonata tiimin varastoja. teams.write_access_helper=Tiimin jäsenet voivat lukea ja työntää tiimin varastoja/varastoihin. -teams.admin_access=Järjestelmänvalvojan pääsy -teams.admin_access_helper=Tiimin jäsenet voivat työntää (push) ja vetää (pull) tiimin varastoista/varastoihin ja lisätä yhteistyökumppaneita. +teams.admin_access=Ylläpitopääsy +teams.admin_access_helper=Tiimin jäsenet voivat työntää (push) ja vetää (pull) tiimin varastoista/varastoihin ja lisätä avustajia. teams.no_desc=Tällä tiimillä ei ole kuvausta teams.settings=Asetukset -teams.owners_permission_desc=Omistajilla on täydet käyttöoikeudet kaikkiin organisaation repoihin sekä organisaation ylläpitäjän oikeudet. +teams.owners_permission_desc=Omistajilla on täydet käyttöoikeudet kaikkiin organisaation tietovarastoihin sekä organisaation ylläpitäjän oikeudet. teams.members=Tiimin jäsenet teams.update_settings=Päivitä asetukset teams.delete_team=Poista tiimi teams.add_team_member=Lisää tiimin jäsen teams.delete_team_title=Poista tiimi -teams.delete_team_desc=Tiimin poisto peruuttaa sen jäseniltä oikeuden päästä tiimin varastoihin. Jatketaanko? +teams.delete_team_desc=Tiimin poisto peruuttaa sen jäseniltä oikeuden päästä tiimin tietovarastoihin. Jatketaanko? teams.delete_team_success=Tiimi on poistettu. -teams.read_permission_desc=Tämä tiimi myöntää jäsenille Luku oikeudet: tiimin jäsenet voivat katsella ja kloonata tiimin varastoja. -teams.write_permission_desc=Tämä tiimi myöntää jäsenille Kirjoitus oikeuden: tiimin jäsenet voivat lukea ja kirjoittaa tiimin repoihin. -teams.admin_permission_desc=Tämä joukkue myöntää järjestelmänvalvojapääsyn: jäsenet voivat lukea joukkueen tietovarastoista, vetää ja lisätä yhteistyöhenkilöitä niihin. -teams.repositories=Joukkueen tietovarastot +teams.read_permission_desc=Tämä tiimi myöntää jäsenille Lue-oikeuden: tiimin jäsenet voivat katsella ja kloonata tiimin tietovarastoja. +teams.write_permission_desc=Tämä tiimi myöntää jäsenille Kirjoita-oikeuden: tiimin jäsenet voivat lukea ja kirjoittaa tiimin tietovarastoihin. +teams.admin_permission_desc=Tämä tiimi myöntää Ylläpito-oikeuden: jäsenet voivat lukea tiimin tietovarastoista, työntää ja lisätä avustajia niihin. +teams.repositories=Tiimin tietovarastot teams.members.none=Ei jäseniä tässä tiimissä. -teams.all_repositories=Kaikki repot +teams.all_repositories=Kaikki tietovarastot teams.invite.by = Kutsunut %s members.leave.detail = Haluatko varmasti poistua organisaatiosta "%s"? teams.add_all_repos_title = Lisää kaikki tietovarastot teams.invite_team_member.list = Odottavat kutsut teams.invite.description = Napsauta alla olevaa painiketta liittyäksesi tiimiin. -settings.update_setting_success = Organisaatioasetukset on päivitetty. +settings.update_setting_success = Organisaation asetukset on päivitetty. form.create_org_not_allowed = Sinulla ei ole oikeutta luoda organisaatiota. teams.leave.detail = Haluatko varmasti poistua tiimistä "%s"? teams.invite.title = Sinut on kutsuttu tiimiin %s organisaatiossa %s. -teams.add_duplicate_users = Käyttäjä on jo tiimijäsen. +teams.add_duplicate_users = Käyttäjä on jo tiimin jäsen. settings.visibility.limited = Rajattu (näkyvissä vain kirjautuneille käyttäjille) code = Koodi -teams.remove_all_repos_title = Poista kaikki joukkueen tietovarastot +teams.remove_all_repos_title = Poista kaikki tiimin tietovarastot form.name_reserved = Organisaation nimi "%s" on varattu. settings.delete_org_desc = Organisaatio poistetaan pysyvästi. Jatketaanko? team_access_desc = Tietovarastopääsy teams.specific_repositories = Määritetyt tietovarastot open_dashboard = Avaa kojelauta -teams.remove_all_repos_desc = Tämä poistaa kaikki tietovarastot joukkueelta. -teams.add_all_repos_desc = Tämä lisää kaikki organisaation tietovarastot joukkueelle. +teams.remove_all_repos_desc = Tämä poistaa kaikki tietovarastot tiimiltä. +teams.add_all_repos_desc = Tämä lisää kaikki organisaation tietovarastot tiimille. team_unit_disabled = (Pois käytöstä) follow_blocked_user = Et voi seurata tätä organisaatiota, koska organisaatio on estänyt sinut. teams.can_create_org_repo = Luo tietovarastoja @@ -2347,12 +2659,20 @@ settings.email = Yhteydenoton sähköposti teams.general_access = Mukautettu pääsy settings.change_orgname_redirect_prompt = Vanha nimi uudelleenohjaa, kunnes nimi otetaan uudelleen käyttöön. settings.change_orgname_prompt = Huomio: organisaation nimen vaihtaminen vaihtaa myös organisaation URL-osoitteen ja vapauttaa vanhan nimen. +teams.write_access = Kirjoita +settings.update_avatar_success = Organisaation profiilikuva on päivitetty. +teams.can_create_org_repo_helper = Jäsenet voivat luoda uusia tietovarastoja organisaatiossa. Tietovaraston luonut saa ylläpito-oikeuden uuteen tietovarastoon. +teams.create_repo_permission_desc = Lisäksi tämä tiimi myöntää Luo tietovarasto -oikeuden: jäsenet voivat luoda uusia tietovarastoja organisaatiossa. +teams.add_nonexistent_repo = Tietovarasto, jota yrität lisätä, ei ole olemassa. Luo se ensin. +teams.repos.none = Tällä tiimillä ei ole pääsyä tietovarastoihin. +settings.change_orgname_redirect_prompt.with_cooldown.one = Vanha organisaation nimi on kenen tahansa saatavilla %[1]d päivän suojaamisjakson jälkeen. Voit palauttaa organisaation nimen itsellesi suojaamisjakson aikana. +settings.change_orgname_redirect_prompt.with_cooldown.few = Vanha organisaation nimi on kenen tahansa saatavilla %[1]d päivän suojaamisjakson jälkeen. Voit palauttaa organisaation nimen itsellesi suojaamisjakson aikana. [admin] dashboard=Kojelauta users=Käyttäjätilit organizations=Organisaatiot -repositories=Repot +repositories=Tietovarastot authentication=Todennuslähteet emails=Käyttäjien sähköpostit config=Asetukset @@ -2365,10 +2685,10 @@ total=Yhteensä: %d dashboard.statistic=Yhteenveto dashboard.operations=Huoltotoimet dashboard.system_status=Järjestelmän tila -dashboard.operation_name=Toiminnon nimi +dashboard.operation_name=Toimenpiteen nimi dashboard.operation_switch=Vaihda dashboard.operation_run=Suorita -dashboard.delete_inactive_accounts=Poista kaikki aktivoimattomat käyttäjät +dashboard.delete_inactive_accounts=Poista kaikki aktivoimattomat tilit dashboard.delete_repo_archives=Poista kaikki tietovarastojen arkistot (ZIP, TAR.GZ, jne.) dashboard.server_uptime=Palvelimen uptime dashboard.current_goroutine=Nykyiset goroutinet @@ -2406,7 +2726,7 @@ users.activated=Aktivoitu users.admin=Ylläpito users.restricted=Rajoitettu users.2fa=2FA -users.repos=Repot +users.repos=Tietovarastot users.created=Luotu users.last_login=Viimeksi kirjautunut users.never_login=Ei koskaan kirjautunut @@ -2466,7 +2786,7 @@ repos.size=Koko packages.owner=Omistaja packages.name=Nimi packages.type=Tyyppi -packages.repository=Repo +packages.repository=Tietovarasto packages.size=Koko @@ -2488,7 +2808,7 @@ auths.user_base=Käyttäjähakukanta auths.user_dn=Käyttäjä DN auths.search_page_size=Sivukoko auths.filter=Käyttäjäsuodatin -auths.admin_filter=Järjestelmänvalvojasuodatin +auths.admin_filter=Ylläpitosuodatin auths.restricted_filter=Rajoitettu suodatin auths.smtp_auth=SMTP-todennustyyppi auths.smtphost=SMTP-isäntä @@ -2496,7 +2816,7 @@ auths.smtpport=SMTP-portti auths.allowed_domains=Sallitut verkkotunnukset auths.skip_tls_verify=Ohita TLS-vahvistus auths.pam_service_name=PAM-palvelun nimi -auths.oauth2_tokenURL=Pääsymerkki URL +auths.oauth2_tokenURL=Pääsypoletin URL-osoite auths.enable_auto_register=Ota käyttöön automaattinen rekisteröinti auths.tips=Vinkit auths.tips.oauth2.general=OAuth2-autentikointi @@ -2509,7 +2829,7 @@ auths.delete_auth_desc=Todennuslähteen poisto estää käyttäjiä käyttämäs auths.deletion_success=Todennuslähde on poistettu. config.server_config=Palvelimen asetukset -config.app_name=Ilmentymän otsikko +config.app_name=Instanssin otsikko config.app_ver=Forgejo-versio config.disable_router_log=Poista reitittimen lokinkirjaaminen käytöstä config.run_mode=Suoritustila @@ -2524,7 +2844,7 @@ config.ssh_port=Portti config.ssh_listen_port=Kuuntele porttia config.ssh_root_path=Juuren polku config.ssh_key_test_path=Avaimen testipolku -config.ssh_keygen_path=Keygen ('ssh-keygen') polku +config.ssh_keygen_path=Keygen-polku ('ssh-keygen') config.ssh_minimum_key_size_check=Avaimen vähimmäiskoon tarkistus config.ssh_minimum_key_sizes=Avaimen vähimmäiskoot @@ -2573,12 +2893,12 @@ config.https_only=Vain HTTPS config.cookie_life_time=Evästeen elinikä config.picture_service=Kuvapalvelu -config.disable_gravatar=Poista käytöstä Gravatar +config.disable_gravatar=Poista Gravatar käytöstä config.git_gc_args=Roskienkeruu-argumentit -config.git_migrate_timeout=Siirron aikakatkaisu +config.git_migrate_timeout=Migraation aikakatkaisu config.git_mirror_timeout=Peilin päivityksen aikakatkaisu -config.git_clone_timeout=Kloonitoiminnon aikakatkaisu +config.git_clone_timeout=Kloonaustoimenpiteen aikakatkaisu config.git_gc_timeout=Roskienkeruun aikakatkaisu config.log_config=Lokiasetukset @@ -2609,12 +2929,12 @@ notices.inverse_selection=Käänteinen valinta notices.delete_selected=Poista valitut notices.delete_all=Poista kaikki ilmoitukset notices.type=Tyyppi -notices.type_1=Repo +notices.type_1=Tietovarasto notices.desc=Kuvaus notices.op=Toiminta auths.sspi_auto_create_users = Luo käyttäjät automaattisesti integrations = Integraatiot -emails.change_email_header = Päivitä sähköpostiominaisuudet +emails.change_email_header = Päivitä sähköpostin ominaisuudet emails.change_email_text = Haluatko varmasti päivittää tämän sähköpostiosoitteen? emails.updated = Sähköpostiosoite päivitetty users.organization_creation.description = Salli uusien organisaatioiden luonti. @@ -2630,8 +2950,8 @@ auths.force_smtps = Pakota SMTPS config.mailer_use_sendmail = Käytä Sendmailia users.new_success = Käyttäjätili "%s" on luotu. config.disable_register = Poista itserekisteröinti käytöstä -config.enable_openid_signin = Käytä OpenID-kirjautumista -config.enable_openid_signup = Käytä OpenID-itserekisteröintiä +config.enable_openid_signin = Ota OpenID-kirjautuminen käyttöön +config.enable_openid_signup = Ota OpenID-itserekisteröinti käyttöön monitor.queue.settings.changed = Asetukset päivitetty config.db_schema = Skeema settings = Ylläpitäjän asetukset @@ -2647,7 +2967,7 @@ users.details = Käyttäjän tiedot config_summary = Yhteenveto config.send_test_mail = Lähetä testisähköposti auths.oauth2_icon_url = Kuvakkeen URL-osoite -config.mail_notify = Käytä sähköposti-ilmoituksia +config.mail_notify = Ota sähköposti-ilmoitukset käyttöön config.send_test_mail_submit = Lähetä systemhooks = Järjestelmän webkoukut packages.total_size = Koko yhteensä: %s @@ -2676,7 +2996,7 @@ repos.lfs_size = LFS:n koko config.lfs_config = LFS-asetukset config.register_email_confirm = Vaadi sähköpostivahvistus rekisteröitymiseen config.ssh_domain = SSH-palvelimen verkkotunnus -config.app_slogan = Ilmentymän tunnuslause +config.app_slogan = Instanssin tunnuslause config.lfs_content_path = LFS-sisällön polku users.max_repo_creation = Tietovarastojen enimmäismäärä defaulthooks.update_webhook = Päivitä oletusarvoinen webkoukku @@ -2701,31 +3021,116 @@ monitor.download_diagnosis_report = Lataa diagnostiikkaraportti monitor.duration = Kesto (s) monitor.last_execution_result = Tulos users.bot = Botti -auths.syncenabled = Käytä käyttäjäsynkronointia -auths.enable_ldap_groups = Käytä LDAP-ryhmiä +auths.syncenabled = Ota käyttäjäsynkronointi käyttöön +auths.enable_ldap_groups = Ota LDAP-ryhmät käyttöön dashboard.sync_branch.started = Haarasynkronointi aloitettu dashboard.sync_tag.started = Tagisynkronointi aloitettu auths.login_source_exist = Todennuslähde "%s" on jo olemassa. -config.enable_timetracking = Ota ajan seuranta käyttöön -config.default_enable_timetracking = Ota ajan seuranta käyttöön oletuksena -config.no_reply_address = Piilotetun sähköpostin toimialue +config.enable_timetracking = Ota ajanseuranta käyttöön +config.default_enable_timetracking = Ota ajanseuranta käyttöön oletuksena +config.no_reply_address = Piilotetun sähköpostin verkkotunnus config.allow_dots_in_usernames = Salli käyttäjien käyttää pisteitä käyttäjänimissään. Ei vaikuta olemassa oleviin tileihin. repos.unadopted.no_more = Omaksumattomia tietovarastoja ei löytynyt. repos.unadopted = Omaksumattomat tietovarastot +dashboard.repo_health_check = Tee terveystarkastus kaikille tietovarastoille +users.reserved = Varattu +users.purge = Hävitä käyttäjä +dashboard.cron.process = Cron: %[1]s +auths.tip.github = Rekisteröi uusi OAuth-sovellus %sissa +config.app_data_path = Sovellusdatan polku +config.cache_test_slow = Välimuistin testi onnistui, mutta vastaus on hidas: %s. +dashboard.delete_repo_archives.started = Poista kaikki tietovarastojen arkistot -tehtävä aloitettu. +dashboard.check_repo_stats = Tarkista kaikkien tietovarastojen tilastot +users.still_own_packages = Tämä käyttäjä omistaa yhden tai useamman paketin. Poista paketit ensin. +users.block.description = Estä tätä käyttäjää olemasta vuorovaikutuksessa tämän palvelun kanssa tilinsä välityksellä ja estä sisäänkirjautuminen. +auths.attribute_username = Käyttäjänimen attribuutti +auths.oauth2_emailURL = Sähköpostin URL-osoite +auths.tip.discord = Rekisteröi uusi sovellus %sissa +config.default_enable_dependencies = Ota ongelmariippuvuudet käyttöön oletuksena +config.mailer_config = Postittimen asetukset +config.cache_test_succeeded = Välimuistin testi onnistui, vastauksen saamisessa kesti %s. +dashboard.sync_external_users = Synkronoi ulkoinen käyttäjädata +auths.tip.gitea = Rekisteröi uusi OAuth2-sovellus. Ohje on osoitteessa %s +config.test_mail_failed = Testisähköpostin lähettäminen osoitteeseen "%s" epäonnistui: %v +auths.attribute_surname = Sukunimen attribuutti +config.mailer_enable_helo = Ota HELO käyttöön +auths.attribute_username_placeholder = Jätä tyhjäksi käyttääksesi Forgejo:ssa asetettua käyttänimeä. +auths.oauth2_authURL = Valtuutuksen URL-osoite +auths.new_success = Todennus "%s" on lisätty. +users.still_own_repo = Tämä käyttäjä omistaa yhden tai useamman tietovaraston. Poista tai siirrä nämä tietovarastot ensin. +dashboard.cleanup_hook_task_table = Siivoa hook_task-taulu +dashboard.delete_old_actions = Poista kaikki vanhat aktiviteetit tietokannasta +auths.attribute_mail = Sähköpostiosoitteen attribuutti +auths.attribute_ssh_public_key = Julkisen SSH-avaimen attribuutti +auths.group_attribute_list_users = Ryhmäattribuutti sisältäen listan käyttäjistä +auths.oauth2_profileURL = Profiilin URL-osoite +auths.skip_local_two_fa = Ohita paikallinen 2FA +auths.oauth2_scopes = Lisäskoopit +auths.tip.gitlab_new = Rekisteröi uusi sovellus %sissa +config.cache_test = Testaa välimuisti +auths.still_in_used = Todennuslähde on edelleen käytössä. Konvertoi tai poista ensin käyttäjät, jotka käyttävät tätä todennuslähdettä. +users.admin.description = Myönnä tälle käyttäjälle täydet oikeudet selainkäyttöliittymän ja rajapinnan kautta saatavilla oleviin ylläpito-ominaisuuksiin. +dashboard.delete_inactive_accounts.started = Poista kaikki aktivoimattomat tilit -tehtävä aloitettu. +config.run_user = Suorita käyttäjänä +config.mailer_sendmail_args = Lisäargumentit Sendmailille +dashboard.archive_cleanup = Poista vanhat tietovarastojen arkistot +dashboard.deleted_branches_cleanup = Siivoa poistetut haarat +dashboard.update_checker = Päivitysten tarkistaja +auths.allowed_domains_helper = Jätä tyhjäksi salliaksesi kaikki verkkotunnukset. Erota useat verkkotunnukset pilkulla (","). +auths.activated = Tämä todennuslähde on aktivoitu +auths.login_source_of_type_exist = Tätä tyyppiä oleva todennuslähde on jo olemassa. +dashboard.memory_allocate_times = Muistiallokaatiot +users.send_register_notify = Ilmoita rekisteröitymisestä sähköpostitse +config.offline_mode = Paikallinen tila +config.cache_item_ttl = Välimuistitietueen TTL +config.log_file_root_path = Lokipolku +config.lfs_root_path = LFS-juuren polku +users.allow_import_local = Voi tuoda paikallisia tietovarastoja +users.still_has_org = Tämä käyttäjä on organisaation jäsen. Poista käyttäjä organisaatiosta ensin. +packages.creator = Luoja +auths.attribute_avatar = Profiilikuvan attribuutti +users.remote = Etä +auths.disable_helo = Poista HELO käytöstä +defaulthooks.desc = Webkoukut tekevät automaattisesti HTTP POST -pyyntöjä palvelimelle, kun tietyt Forgejo-tapahtumat ilmenevät. Tässä määritetyt webkoukut ovat oletusarvot ja ne kopioidaan kaikkiin uusiin tietovarastoihin. Lue lisää webkoukkujen oppaasta. +auths.attribute_name = Etunimen attribuutti +users.local_import.description = Salli tietovarastojen tuominen palvelimen paikallisesta tiedostojärjestelmästä. Tämä voi olla tietoturvaongelma. +emails.not_updated = Pyydetyn sähköpostiosoitteen päivittäminen epäonnistui: %v +dashboard.update_mirrors = Päivitä peilit +config.mailer_protocol = Protokolla +users.activated.description = Sähköpostivahvistuksen valmistuminen. Aktivoimattoman tilin omistaja ei voi kirjautua sisään, ennen kuin sähköpostivahvistus on suoritettu. +users.purge_help = Poista käyttäjä pakottaen, sekä kaikki käyttäjän omistamat tietovarastot, organisaatiot ja paketit. Kaikki käyttäjän luomat kommentit ja ongelmat poistetaan myös. +users.restricted.description = Salli vuorovaikutus vain niihin tietovarastoihin ja organisaatioihin, joissa käyttäjä on avustajan roolissa. Tämä estää pääsyn tässä instanssissa oleviin julkisiin tietovarastoihin. +config.git_pull_timeout = Vetotoimenpiteen aikakatkaisu +monitor.process.cancel_desc = Prosessin peruuttaminen saattaa aiheuttaa datan menetyksen +self_check.no_problem_found = Ongelmia ei ole vielä löytynyt. +config.git_disable_diff_highlight = Poista diff-syntaksin korostus käytöstä +config.git_max_diff_lines = Diff-rivejä enintään tiedostoa kohden +config.access_log_template = Pääsylokin mallipohja +monitor.process.cancel_notices = Perutaanko: %s? +config.enable_federated_avatar = Ota federoidut profiilikuvat käyttöön +notices.operations = Toimenpiteet +config.xorm_log_sql = Lokita SQL +monitor.queue.settings.remove_all_items_done = Kaikki jonossa olleet tietueet on poistettu. +config.logger_name_fmt = Lokittaja: %s +config.git_max_diff_line_characters = Diff-merkkejä enintään riviä kohden +config.git_max_diff_files = Diff-tiedostoja enintään näytettäväksi +config.access_log_mode = Pääsylokin tila +config.picture_config = Kuvan ja avatarin asetukset +notices.delete_success = Järjestelmäilmoitukset on poistettu. [action] create_repo=loi tietovaraston %s -rename_repo=uudelleennimetty repo %[1]s nimelle %[3]s -transfer_repo=siirretty repo %s kohteeseen %s +rename_repo=asetti tietovaraston %[1]s uudeksi nimeksi %[3]s +transfer_repo=siirsi tietovaraston %s käyttäjälle %s push_tag=työnsi tagin %[3]s kohteeseen %[4]s delete_tag=poisti tagin %[2]s kohteesta %[3]s -compare_commits_general=Vertaa committeja -create_branch=loi haaran %[3]s repossa %[4]s -compare_commits = Vertaa %d sitoumukset +compare_commits_general=Vertaa kommitteja +create_branch=loi haaran %[3]s tietovarastossa %[4]s +compare_commits = Vertaa %d kommittia compare_branch = Vertaa review_dismissed_reason = Syy: -commit_repo = työnsi haaraan %[3]s %[4]s:ssa +commit_repo = työnsi haaraan %[3]s tietovarastossa %[4]s create_issue = `avasi ongelman %[3]s#%[2]s` reopen_issue = `avasi uudelleen ongelman %[3]s#%[2]s` create_pull_request = `loi vetopyynnön %[3]s#%[2]s` @@ -2735,7 +3140,12 @@ comment_issue = `kommentoi ongelmaa %[3]s#%[2]s` close_issue = `sulki ongelman %[3]s#%[2]s` merge_pull_request = `yhdisti vetopyynnön %[3]s#%[2]s` comment_pull = `kommentoi vetopyyntöä %[3]s#%[2]s` -auto_merge_pull_request = `automaattisesti yhdistetty vetopyyntö %[3]s#%[2]s` +auto_merge_pull_request = `automaattisesti yhdisti vetopyynnön %[3]s#%[2]s` +delete_branch = poisti haaran %[2]s tietovarastosta %[3]s +watched_repo = aloitti tietovaraston %[2]s tarkkailun +approve_pull_request = `hyväksyi %[3]s#%[2]s` +starred_repo = lisäsi tähden tietovarastolle %[2]s +reject_pull_request = `ehdotti muutoksia kohteeseen %[3]s#%[2]s` [tool] now=nyt @@ -2757,8 +3167,8 @@ raw_seconds=sekuntia raw_minutes=minuuttia [dropzone] -default_message=Pudota tiedostot tähän tai klikkaa aluetta ladataksesi tiedoston. -invalid_input_type=Tämäntyyppisiä tiedostoja ei voi ladata. +default_message=Pudota tiedostot tähän tai napsauta tästä lähettääksesi tiedoston. +invalid_input_type=Tätä tyyppiä olevia tiedostoja ei voi lähettää. remove_file=Poista tiedosto file_too_big = Tiedoston koko ({{filesize}} Mt) ylittää enimmäisrajan ({{maxFilesize}} Mt). @@ -2768,7 +3178,7 @@ unread=Lukematon read=Luettu no_unread=Ei lukemattomia ilmoituksia. no_read=Ei luettuja ilmoituksia. -pin=Merkitse ilmoitus +pin=Kiinnitä ilmoitus mark_as_read=Merkitse luetuksi mark_as_unread=Merkitse lukemattomaksi mark_all_as_read=Merkitse kaikki luetuiksi @@ -2777,19 +3187,24 @@ no_subscriptions = Ei tilauksia subscriptions = Tilaukset [gpg] -error.no_committer_account=Committaajan sähköpostiosoitteeseen ei ole linkitetty tiliä -error.not_signed_commit=Ei allekirjoitettu sitoumus +error.no_committer_account=Kommitin tekijän sähköpostiosoitteeseen ei ole linkitetty tiliä +error.not_signed_commit=Kommitti ei ole allekirjoitettu error.extract_sign = Allekirjoituksen purkaminen epäonnistui default_key = Allekirjoitettu oletusavaimella -error.failed_retrieval_gpg_keys = Sitoumuksen toimijan tiliin liitetyn avaimen nouto epäonnistui -error.generate_hash = Sitoumuksen tiivisteen luominen epäonnistui +error.failed_retrieval_gpg_keys = Ei saatu yhtäkään kommitin tekijän tiliin liitettyä avainta +error.generate_hash = Kommitin tiivisteen luominen epäonnistui +error.probable_bad_signature = VAROITUS! Vaikka tietokannassa on avain tällä ID-tunnistella, se ei vahvista tätä kommittia! Tämä kommitti on EPÄILYTTÄVÄ. +error.probable_bad_default_signature = VAROITUS! Vaikka oletusavaimella on tämä ID-tunniste, se ei vahvista tätä kommittia! Tämä kommitti on EPÄILYTTÄVÄ. +error.no_gpg_keys_found = Tälle allekirjoitukselle ei löytynyt tunnettua avainta tietokannasta [units] unit = Yksikkö +error.unit_not_allowed = Sinulla ei ole pääsyä tähän tietovaraston osioon. +error.no_unit_allowed_repo = Sinulla ei ole pääsyä mihinkään tämän tietovaraston osioon. [packages] title=Paketit -desc=Hallitse repon paketteja. +desc=Hallitse tietovaraston paketteja. empty=Täällä ei vielä ole paketteja. filter.type=Tyyppi filter.type.all=Kaikki @@ -2797,12 +3212,12 @@ filter.no_result=Suodattimesi ei tuottanut tuloksia. installation=Asennus details.author=Tekijä alpine.repository.branches=Haarat -alpine.repository.repositories=Repot -conan.details.repository=Repo +alpine.repository.repositories=Tietovarastot +conan.details.repository=Tietovarasto owner.settings.cleanuprules.enabled=Käytössä details.license = Lisenssi about = Tietoja tästä paketista -debian.install = Asenna paketti seuraavalla komennolla: +debian.install = Asenna paketti komennolla: owner.settings.cleanuprules.edit = Muokkaa siivoussääntöä arch.version.groups = Ryhmä details.project_site = Projektin verkkosivusto @@ -2814,11 +3229,11 @@ keywords = Avainsanat dependencies = Riippuvuudet container.labels.key = Avain container.labels.value = Arvo -pypi.install = Asenna paketti pipillä seuraavalla komennolla: -npm.install = Asenna paketti npm:llä seuraavalla komennolla: +pypi.install = Asenna paketti pipillä komennolla: +npm.install = Asenna paketti npm:llä komennolla: npm.install2 = tai lisää se package.json-tiedostoon: empty.documentation = Lisätietoja pakettirekisteristä on saatavilla dokumentaatiossa. -helm.install = Asenna paketti seuraavalla komennolla: +helm.install = Asenna paketti komennolla: owner.settings.chef.keypair = Luo avainpari settings.delete.error = Paketin poistaminen epäonnistui. requirements = Vaatimukset @@ -2826,7 +3241,7 @@ published_by_in = Julkaistu %[1]s, julkaisija %[3]s projekti pypi.requires = Vaatii Pythonin alpine.install = Asenna paketti seuraavalla komennolla: debian.repository.components = Komponentit -cran.install = Asenna paketti seuraavalla komennolla: +cran.install = Asenna paketti komennolla: settings.link.select = Valitse tietovarasto owner.settings.chef.title = Chef-rekisteri owner.settings.cleanuprules.add = Lisää siivoussääntö @@ -2835,7 +3250,7 @@ versions.view_all = Näytä kaikki debian.repository.architectures = Arkkitehtuurit container.details.type = Levykuvan tyyppi arch.version.properties = Version ominaisuudet -rpm.install = Asenna paketti seuraavalla komennolla: +rpm.install = Asenna paketti komennolla: owner.settings.cleanuprules.none = Siivoussääntöjä ei vielä ole. container.details.platform = Alusta npm.dependencies = Riippuvuudet @@ -2847,47 +3262,47 @@ settings.delete.success = Paketti on poistettu. npm.dependencies.optional = Valinnaiset riippuvuudet debian.repository.distributions = Jakelut composer.dependencies = Riippuvuudet -chef.install = Asenna paketti seuraavalla komennolla: +chef.install = Asenna paketti komennolla: details.documentation_site = Dokumentaation verkkosivusto go.install = Asenna paketti komentoriviltä: alpine.repository.architectures = Arkkitehtuurit composer.registry = Määritä tämä rekisteri ~/.composer/config.json-tiedostossa: debian.registry = Määritä tämä rekisteri komentoriviltä: rpm.registry = Määritä rekisteri komentoriviltä: -maven.install = Käytä pakettia sisällyttämällä seuraava dependencies-lohkoon pom.xml-tiedostossa: +maven.install = Käytä pakettia sisällyttämällä seuraava sisältö dependencies-lohkoon pom.xml-tiedostossa: npm.registry = Määritä rekisteri projektin .npmrc-tiedostossa: alpine.repository = Tietovaraston tiedot cargo.registry = Määritä tämä rekisteri Cargon asetustiedostossa (esimerkiksi ~/.cargo/config.toml): cargo.install = Asenna paketti Cargolla suorittamalla seuraava komento: -composer.install = Asenna paketti Composerilla suorittamalla seuraava komento: +composer.install = Asenna paketti Composerilla suorittamalla komento: rpm.distros.redhat = RedHatiin pohjautuvilla jakeluilla rpm.distros.suse = SUSE:en pohjautuvilla jakeluilla rpm.repository.architectures = Arkkitehtuurit cran.registry = Määritä rekisteri Rprofile.site-tiedostossa: -swift.install2 = ja suorita seuraava komento: +swift.install2 = ja suorita komento: maven.registry = Määritä tämä rekisteri projektin pom.xml-tiedostossa: maven.install2 = Suorita komentoriviltä: nuget.registry = Määritä rekisteri komentoriviltä: -nuget.install = Asenna paketti NuGetillä suorittamalla seuraava komento: -rubygems.install = Asenna paketti gemillä suorittamalla seuraava komento: +nuget.install = Asenna paketti NuGetillä suorittamalla komento: +rubygems.install = Asenna paketti gemillä suorittamalla komento: rubygems.install2 = tai lisää se Gemfileen: swift.registry = Määritä rekisteri komentoriviltä: swift.install = Lisää paketti Package.swift-tiedostoon: owner.settings.cleanuprules.keep.count.1 = 1 versio per paketti owner.settings.cleanuprules.keep.count.n = %d versiota per paketti -conan.install = Asenna paketti Conanilla suorittamalla seuraava komento: +conan.install = Asenna paketti Conanilla suorittamalla komento: chef.registry = Määritä tämä rekisteri ~/.chef/config.rb-tiedostossa: conan.registry = Määritä tämä rekisteri komentoriviltä: -conda.install = Asenna paketti Condalla suorittamalla seuraava komento: +conda.install = Asenna paketti Condalla suorittamalla komento: helm.registry = Määritä tämä rekisteri komentoriviltä: -pub.install = Asenna paketti Dartilla suorittamalla seuraava komento: -owner.settings.cargo.title = Cargon rekisteri-indeksi +pub.install = Asenna paketti Dartilla suorittamalla komento: +owner.settings.cargo.title = Cargo-rekisterin indeksi settings.delete.description = Paketin poistaminen on peruuttamaton toimenpide, sitä ei voi perua. settings.link.success = Tietovaraston linkki päivitettiin onnistuneesti. settings.link.button = Päivitä tietovaraston linkki owner.settings.cleanuprules.preview.overview = %d pakettia on ajastettu poistettavaksi. owner.settings.cargo.initialize.success = Cargo-indeksi luotiin onnistuneesti. -vagrant.install = Lisää Vagrant-boksi suorittamalla seuraava komento: +vagrant.install = Lisää Vagrant-boksi suorittamalla komento: rubygems.dependencies.development = Kehitysriippuvuudet owner.settings.cleanuprules.preview = Siivoussäännön esikatselu npm.dependencies.development = Kehitysriippuvuudet @@ -2899,7 +3314,7 @@ maven.download = Lataa riippuvuus suorittamalla komentorivillä: registry.documentation = Lisätietoja %s-rekisteristä on dokumentaatiossa. owner.settings.chef.keypair.description = Chef-rekisteriin lähetettävät pyynnöt on allekirjoitettava salauskirjoituksella todennuskeinona. Avainparia luotaessa vain julkinen avain tallennetaan Forgejoon. Yksityinen avain toimitetaan sinulle käytettäväksi knifen kanssa. Uuden avainparin luominen korvaa edellisen. owner.settings.cleanuprules.keep.pattern = Säilytä kaavaa vastaavat versiot -owner.settings.cleanuprules.pattern_full_match = Toteuta kaavio paketin koko nimeen +owner.settings.cleanuprules.pattern_full_match = Toteuta kaava paketin koko nimeen owner.settings.cleanuprules.keep.title = Näitä sääntöjä vastaavat versiot säilytetään, vaikka ne vastaisivat alla olevaa poistosääntöä. owner.settings.cleanuprules.keep.count = Säilytä viimeisimmät owner.settings.cleanuprules.remove.pattern = Poista kaavaa vastaavat versiot @@ -2919,7 +3334,7 @@ alpine.registry = Aseta tämä rekisteri lisäämällä URL-osoite tiedostoon paketin asetuksiin ja linkitä se tähän tietovarastoon. +empty.repo = Lähetitkö paketin, mutta se ei näy täällä? Siirry paketin asetuksiin ja linkitä se tähän tietovarastoon. alpine.registry.info = Valitse $branch ja $repository alla olevasta listasta. container.images.title = Levykuvat owner.settings.cargo.initialize = Alusta indeksi @@ -2928,7 +3343,20 @@ settings.link.error = Tietovaraston linkin päivittäminen epäonnistui. alt.repository.multiple_groups = Tämä paketti on saatavilla useissa ryhmissä. alt.repository.architectures = Arkkitehtuurit alt.install = Asenna paketti -alt.registry.install = Asenna paketti suorittamalla komento: +alt.registry.install = Asenna paketti komennolla: +details = Yksityiskohdat +arch.version.provides = Tarjoaa +rpm.repository = Tietovaraston tiedot +rubygems.required.ruby = Vaatii Ruby-version +settings.delete.notice = Olet aikeissa poistaa %s (%s). Tätä toimenpidettä ei voi perua. Haluatko varmasti jatkaa? +owner.settings.cargo.initialize.error = Cargo-indeksin alustaminen epäonnistui: %v +owner.settings.cargo.rebuild.no_index = Ei voi rakentaa uudelleen, indeksiä ei ole alustettu. +rubygems.required.rubygems = Vaatii RubyGem-version +alt.registry = Määritä tämä rekisteri komentoriviltä: +alt.repository = Tietovaraston tiedot +arch.version.replaces = Korvaa +debian.repository = Tietovaraston tiedot +conda.registry = Määritä tämä rekisteri Conda-tietovarastoksi .condarc-tiedostossa: [secrets] creation.failed = Salaisuuden lisääminen epäonnistui. @@ -2942,16 +3370,17 @@ secrets = Salaisuudet deletion.description = Salaisuuden poistaminen on pysyvä toimenpide, eikä sitä voi perua. Jatketaanko? deletion.success = Salaisuus on poistettu. description = Salaisuudet välitetään tietyille toimenpiteille, eikä niitä voi muuten lukea. +creation.name_placeholder = kirjoinkoolla ei merkitystä, vain aakkosnumeerisia merkkejä ja alaviivoja, ei voi alkaa GITEA_ tai GITHUB_ [actions] runners.name=Nimi runners.owner_type=Tyyppi runners.description=Kuvaus runners.task_list.run=Suorita -runners.task_list.repository=Repo -runners.task_list.commit=Commit +runners.task_list.repository=Tietovarasto +runners.task_list.commit=Kommitti -runs.commit=Commit +runs.commit=Kommitti status.success = Onnistunut status.unknown = Tuntematon status.waiting = Odotustilassa @@ -2966,10 +3395,10 @@ runners.update_runner = Päivitä muutokset runners.edit_runner = Muokkaa testinajajaa runners.update_runner_success = Testinajaja päivitetty onnistuneesti runners.delete_runner_success = Testinajaja poistettu onnistuneesti -runners.reset_registration_token = Uudelleenaseta rekisteröintiavain +runners.reset_registration_token = Uudelleenaseta rekisteröintipoletti runs.scheduled = Ajastettu runs.status = Tila -runs.empty_commit_message = (tyhjä sitoumusviesti) +runs.empty_commit_message = (tyhjä kommittiviesti) variables.deletion = Poista muuttuja runners.new_notice = Testinajajan aloitusohjeet workflow.dispatch.input_required = Arvo syötteelle "%s" vaadittu. @@ -2980,7 +3409,7 @@ runners.labels = Tunnisteet runners.delete_runner_failed = Testinajajan poisto epäonnistui runners.delete_runner_header = Varmista testinajajan poisto runners.task_list.status = Tila -runners.reset_registration_token_success = Testiajajan rekisteröintiavain uudelleenasetettu onnistuneesti +runners.reset_registration_token_success = Testiajajan rekisteröintipoletti asetettu uudelleen onnistuneesti variables.none = Ei muuttujia vielä. runners.id = Tunniste runners.status = Tila @@ -2992,18 +3421,18 @@ runners.task_list.done_at = Valmistunut ajankohtana runs.no_matching_online_runner_helper = Testiajajaa tunnisteella %s ei löytynyt runs.no_results = Ei tuloksia. runners.delete_runner = Poista testinajaja -variables.deletion.description = Muuttujan poistaminen on lopullista eikä sitä voi peruuttaa. Jatketaanko? +variables.deletion.description = Muuttujan poistaminen on lopullista, eikä sitä voi perua. Jatketaanko? workflow.dispatch.invalid_input_type = Syötetyyppi "%s" ei kelpaa. workflow.dispatch.warn_input_limit = Näytetään vain ensimmäiset %d syötettä. runners.runner_manage_panel = Hallinnoi testinajajia variables = Muuttujat -variables.management = Hallinnoi muuttujia +variables.management = Hallitse muuttujia variables.creation = Lisää muuttuja runs.no_workflows.quick_start = Etkö tiedä kuinka Forgejo Actions toimii? Katso aloitusohje. runners.new = Luo uusi testinajaja runners.version = Versio runs.expire_log_message = Lokitiedostot on tyhjätty vanhenemisen vuoksi. -runners.delete_runner_notice = Jos tehtävä on käynnissä tällä suorittajalla, se lopetetaan ja merkitään epäonnistuneeksi. Se voi rikkoa koonnin työnkulun. +runners.delete_runner_notice = Jos tehtävä on käynnissä tällä testinajajalla, se lopetetaan ja merkitään epäonnistuneeksi. Se voi rikkoa koonnin työnkulun. runners.update_runner_failed = Testinajajan päivitys epäonnistui variables.deletion.success = Muuttuja poistettu. variables.edit = Muokkaa muuttujaa @@ -3015,11 +3444,11 @@ variables.update.success = Muuttuja muokattu. variables.id_not_exist = Muuttujaa tunnisteella %d ei ole olemassa. runs.all_workflows = Kaikki työnkulut workflow.dispatch.run = Suorita työnkulku -workflow.enable = Käytä työnkulkua +workflow.enable = Ota työnkulku käyttöön runs.no_workflows = Ei työnkulkuja vielä. runs.actors_no_select = Kaikki toimijat runs.workflow = Työnkulku -workflow.enable_success = Työnkulku "%s" otettu käyttöön. +workflow.enable_success = Työnkulku "%s" on otettu käyttöön. workflow.disabled = Työnkulku on poistettu käytöstä. runs.actor = Toimija workflow.disable = Poista työnkulku käytöstä @@ -3028,11 +3457,14 @@ runs.no_job = Työnkulun tulee sisältää vähintään yksi työ runs.invalid_workflow_helper = Työnkulun asetustiedosto on virheellinen. Tarkista asetustiedosto: %s runners = Ajajat actions = Actions -unit.desc = Hallitse integroituja CI/CD-putkia Forgejo Actionsia hyödyntäen. +unit.desc = Hallitse integroituja CI-/CD-putkia Forgejo Actionsia hyödyntäen. runs.pushed_by = työntänyt runs.no_workflows.help_no_write_access = Lisätietoja Forgejo Actionsista on saatavilla dokumentaatiosta. -runners.status.idle = Tyhjäkäynti +runners.status.idle = Jouten runners.status.offline = Ei-verkkotilassa +runs.no_job_without_needs = Työnkulun tulee sisältää vähintään yksi työ ilman riippuvuuksia. +runs.no_runs = Työnkululla ei ole vielä suorituksia. +variables.not_found = Muuttujaa ei löytynyt. @@ -3040,13 +3472,16 @@ runners.status.offline = Ei-verkkotilassa [projects] type-1.display_name = Yksittäinen projekti deleted.display_name = Poistettu projekti +type-3.display_name = Organisaatioprojekti +type-2.display_name = Tietovarastoprojekti [git.filemode] changed_filemode = %[1]s -> %[2]s -executable_file = Ajettava tiedosto +executable_file = Suoritettava tiedosto symbolic_link = Symbolinen linkki normal_file = Tavallinen tiedosto -directory = Kansio +directory = Hakemisto +submodule = Alimoduuli [search] search = Hae… @@ -3059,20 +3494,30 @@ exact = Täsmällinen exact_tooltip = Sisällytä vain täsmälleen hakusanaa vastaavat tulokset team_kind = Etsi tiimejä… code_kind = Etsi koodia… -code_search_unavailable = Koodihaku ei tällä hetkellä ole saatavilla. Ota yhteyttä järjestelmänvalvojaan. +code_search_unavailable = Koodihaku ei tällä hetkellä ole saatavilla. Ota yhteyttä sivuston ylläpitoon. union = yhdistelmähaku union_tooltip = Sisällytä tulokset, jotka vastaavat minkä tahansa välilyönnillä erotetuista avainsanoista project_kind = Etsi projekteja… no_results = Hakutuloksia ei löytynyt. -keyword_search_unavailable = Avainsanahaku ei tällä hetkellä ole saatavilla. Ota yhteyttä järjestelmänvalvojaan. +keyword_search_unavailable = Avainsanahaku ei tällä hetkellä ole saatavilla. Ota yhteyttä sivuston ylläpitoon. repo_kind = Etsi tietovarastoja… user_kind = Etsi käyttäjiä… org_kind = Etsi organisaatioita… branch_kind = Etsi haaroja… -issue_kind = Etsi vikoja… +issue_kind = Etsi ongelmia… milestone_kind = Etsi merkkipaaluja... -pull_kind = Etsi pull-vetoja… -commit_kind = Etsi sitoutumisia… +pull_kind = Etsi vetoja… +commit_kind = Etsi kommitteja… fuzzy = Sumea runner_kind = Etsi ajajia… code_search_by_git_grep = Nykyiset koodin hakutulokset pohjautuvat komentoon "git grep". Parempia tuloksia on mahdollista saada, jos sivuston ylläpitäjä ottaa käyttöön koodin indeksoijan. + + +[repo.permissions] +code.read = Lue: Pääsy koodiin ja tietovaraston kloonaaminen. +code.write = Kirjoita: Työnnä tietovarastoon, luo haaroja ja tageja. +issues.read = Lue: Lue ja luo ongelmia ja kommentteja. +releases.read = Lue: Katsele ja lataa julkaisuja. +pulls.read = Lue: Vetopyyntöjen lukeminen ja luominen. +ext_issues = Pääsy ulkoisen ongelmanseurannan linkkiin. Käyttöoikeuksia hallitaan ulkoisesti. +ext_wiki = Pääsy ulkoisen wikin linkkiin. Käyttöoikeuksia hallitaan ulkoisesti. \ No newline at end of file diff --git a/options/locale/locale_he.ini b/options/locale/locale_he.ini index bff7682a95..19c4815277 100644 --- a/options/locale/locale_he.ini +++ b/options/locale/locale_he.ini @@ -663,4 +663,10 @@ issues.label_archived_filter = הצגת תוויות מהארכיון issues.label_archive_tooltip = תוויות בארכיון לא מוצעות בחיפוש על־בסיס תווית כברירת מחדל. [translation_meta] -test = ואהבת לרעך כמוך \ No newline at end of file +test = ואהבת לרעך כמוך + +[git.filemode] +executable_file = קובץ הרצה +directory = תיקיה +normal_file = קובץ רגיל +symbolic_link = קישור סמלי \ No newline at end of file diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index e2d82ee28b..a4999e9751 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -200,6 +200,7 @@ table_modal.placeholder.header = Sidhuvud table_modal.placeholder.content = Innehåll table_modal.label.rows = Rader table_modal.label.columns = Kolumner +buttons.switch_to_legacy.tooltip = Använd legacy-redigeraren istället [filter] string.asc = A - Ö diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 42773de48d..1b257fd47a 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -3298,6 +3298,7 @@ config.mailer_protocol = Протокол dashboard.cron.cancelled = Cron: %[1]s скасовано: %[3]s defaulthooks.desc = Вебхуки автоматично сповіщають HTTP-сервер POST-запитами, коли в Forgejo відбуваються певні події. Вказані тут вебхуки є типовими і будуть скопійовані до всіх нових репозиторіїв. Докладніше — в посібнику з вебхуків. assets = Ресурси коду +auths.invalid_openIdConnectAutoDiscoveryURL = Неправильна URL-адреса автоматичного виявлення (повинна бути дійсна URL-адреса, що починається з http:// або https://) [action] diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 396e1a571d..83b536d06b 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -207,6 +207,8 @@ table_modal.header = 新增表格 buttons.indent.tooltip = 使項目縮排一層 buttons.unindent.tooltip = 使項目取消縮排一層 link_modal.header = 新增連結 +link_modal.url = 網址 +link_modal.description = 描述 [filter] string.asc=A - Z @@ -1062,6 +1064,18 @@ pronouns_custom_label = 自訂代名詞 change_username_redirect_prompt.with_cooldown.one = 舊的使用者名稱將在 %[1]d 天的冷卻期後對所有人開放,你仍然可以在冷卻期內重新獲得舊的使用者名稱。 change_username_redirect_prompt.with_cooldown.few = 舊的使用者名稱將在 %[1]d 天的冷卻期後對所有人開放,你仍然可以在冷卻期內重新獲得舊的使用者名稱。 keep_activity_private.description = 你的公開活動只有你和站點管理員可見。 +quota.rule.exceeded = 已超出 +quota.sizes.assets.packages.all = 軟體包 +storage_overview = 儲存空間概覽 +quota.rule.no_limit = 無限制 +quota.sizes.all = 全部 +regenerate_token = 重新產生 +quota.sizes.repos.all = 儲存庫 +quota.sizes.assets.all = 資產 +quota.sizes.assets.attachments.all = 附件 +quota.sizes.assets.artifacts = 製品 +quota.sizes.wiki = 百科 +quota = 配額 [repo] owner=所有者 @@ -1900,7 +1914,7 @@ milestones.filter_sort.most_issues=問題由多到少 milestones.filter_sort.least_issues=問題由少到多 -ext_wiki=外部 Wiki +ext_wiki=外部百科 ext_wiki.desc=連結外部 Wiki。 wiki=Wiki @@ -1927,7 +1941,7 @@ wiki.page_already_exists=相同名稱的 Wiki 頁面已經存在。 wiki.reserved_page=「%s」是保留的 Wiki 頁面名稱。 wiki.pages=所有頁面 wiki.last_updated=最後更新於 %s -wiki.page_name_desc=輸入此 Wiki 頁面的名稱。一些特殊名稱有:「Home」、「_Sidebar」、「_Footer」等。 +wiki.page_name_desc=輸入此百科頁面的名稱。一些特殊名稱有:「Home」、「_Sidebar」、「_Footer」等。 activity=動態 activity.period.filter_label=期間: @@ -2036,10 +2050,10 @@ settings.update_settings=儲存設定 settings.branches.update_default_branch=更新預設分支 settings.branches.add_new_rule=增加新規則 settings.advanced_settings=進階設定 -settings.wiki_desc=啟用儲存庫 Wiki -settings.use_internal_wiki=使用內建 Wiki -settings.use_external_wiki=使用外部 Wiki -settings.external_wiki_url=外部 Wiki 網址 +settings.wiki_desc=啟用儲存庫百科 +settings.use_internal_wiki=使用內建百科 +settings.use_external_wiki=使用外部百科 +settings.external_wiki_url=外部百科網址 settings.external_wiki_url_error=外部 Wiki 網址不是有效的網址。 settings.external_wiki_url_desc=點擊百科分頁時,使用者會被轉址至外部百科的 URL。 settings.issues_desc=啟用儲存庫問題追蹤器 @@ -2118,10 +2132,10 @@ settings.trust_model.committer.desc=提交者的有效簽署將被標記為「 settings.trust_model.collaboratorcommitter=協作者 + 提交者 settings.trust_model.collaboratorcommitter.long=協作者 + 提交者:信任協作者同時是提交者的簽署 settings.trust_model.collaboratorcommitter.desc=此儲存庫協作者的有效簽署在他同時是提交者時將被標記為「受信任」,簽署只符合提交者時將標記為「不受信任」,都不符合時標記為「不符合」。這會強制 Forgejo 成為受簽署提交的提交者,實際的提交者將於提交訊息結尾被標記為「Co-Authored-By:」和「Co-Committed-By:」。預設的 Forgejo 金鑰必須符合資料庫中的一位使用者。 -settings.wiki_delete=刪除 Wiki 資料 +settings.wiki_delete=刪除百科資料 settings.wiki_delete_desc=刪除儲存庫 Wiki 資料是永久的且不可還原。 settings.wiki_delete_notices_1=- 這將會永久刪除與停用 %s 的儲存庫 Wiki。 -settings.confirm_wiki_delete=刪除 Wiki 資料 +settings.confirm_wiki_delete=刪除百科資料 settings.wiki_deletion_success=已刪除儲存庫的 Wiki 資料。 settings.delete=刪除此儲存庫 settings.delete_desc=刪除儲存庫是永久的且不可還原。 @@ -2791,9 +2805,9 @@ settings.unarchive.text = 取消封存儲存庫將恢復其接收提交和推送 release.hide_archive_links = 隱藏自動產生的封存 settings.protect_no_valid_status_check_patterns = 沒有有效的狀態檢查式樣。 settings.enforce_on_admins = 為儲存庫管理員強制執行此規則 -settings.wiki_rename_branch_main_notices_2 = 這將永久重新命名儲存庫 %s 的 Wiki 的內部分支。現有的簽出將需要更新。 +settings.wiki_rename_branch_main_notices_2 = 這將永久重新命名儲存庫 %s 的百科的內部分支。現有的簽出將需要更新。 settings.discord_icon_url.exceeds_max_length = 圖示網址長度必須小於或等於 2048 個字符 -settings.wiki_branch_rename_success = 儲存庫 Wiki 的分支名稱已成功規範化。 +settings.wiki_branch_rename_success = 儲存庫百科的分支名稱已成功規範化。 commits.view_single_diff = 查看此提交中對此提交的變更 issues.new.assign_to_me = 指派給我 mirror_denied_combination = 不能組合使用公鑰和基於密碼的驗證。 @@ -2805,9 +2819,9 @@ settings.federation_following_repos = 關注儲存庫的網址。以半形分號 settings.federation_not_enabled = 你的站點上未啟用聯邦。 settings.federation_apapiurl = 此儲存庫的聯邦網址。將其複製並貼上至另一個儲存庫的聯邦設定中作為關注儲存庫的網址。 settings.enter_repo_name = 準確輸入擁有者和儲存庫名稱,如下所示: -settings.wiki_rename_branch_main = 規範化 Wiki 分支名稱 -settings.wiki_branch_rename_failure = 無法規範化儲存庫 Wiki 的分支名稱。 -settings.confirm_wiki_branch_rename = 重新命名 Wiki 分支 +settings.wiki_rename_branch_main = 規範化百科分支名稱 +settings.wiki_branch_rename_failure = 無法規範化儲存庫百科的分支名稱。 +settings.confirm_wiki_branch_rename = 重新命名百科分支 settings.transfer_quota_exceeded = 新擁有者(%s)已超出配額。儲存庫尚未轉移。 settings.wiki_rename_branch_main_notices_1 = 此操作無法撤銷。 settings.push_mirror_sync_in_progress = 目前正在將變更推送至遠端 %s。 @@ -2818,13 +2832,13 @@ pulls.delete_after_merge.head_branch.is_protected = 你要刪除的頭分支是 pulls.delete_after_merge.head_branch.insufficient_branch = 你沒有權限刪除頭分支。 settings.pull_mirror_sync_in_progress = 目前正在從遠端 %s 拉取變更。 settings.pull_mirror_sync_quota_exceeded = 配額已超出,不拉取變更。 -settings.wiki_globally_editable = 允許任何人編輯 Wiki +settings.wiki_globally_editable = 允許任何人編輯百科 settings.transfer_abort_success = 轉移儲存庫至 %s 已成功取消。 settings.add_collaborator_blocked_them = 無法新增協作者,因為他們已封鎖儲存庫擁有者。 settings.add_webhook.invalid_path = 路徑不能包含「.」、「..」或空字串。它不能以斜線開頭或結尾。 settings.webhook.test_delivery_desc_disabled = 要使用虛假事件測試此 Webhook,請啟動它。 settings.webhook.replay.description_disabled = 要重播此 Webhook,請啟動它。 -settings.wiki_rename_branch_main_desc = 將 Wiki 內部使用的分支重新命名為「%s」。此變更是永久性的,無法撤消。 +settings.wiki_rename_branch_main_desc = 將百科內部使用的分支重新命名為「%s」。此變更是永久性的,無法撤消。 settings.mirror_settings.push_mirror.copy_public_key = 複製公鑰 settings.default_update_style_desc = 用於更新落後於基礎分支的合併請求的預設更新模式。 summary_card_alt = 儲存庫 %s 的摘要卡 @@ -3978,7 +3992,7 @@ code_kind = 搜尋程式碼… code_search_unavailable = 目前無法使用程式碼搜尋。請連絡網站管理員。 no_results = 沒有找到相符的結果。 keyword_search_unavailable = 關鍵字搜尋目前無法使用。請連絡網站管理員。 -runner_kind = 搜尋 Runners … +runner_kind = 搜尋 Runners… project_kind = 搜尋專案… branch_kind = 搜尋分支… commit_kind = 搜尋提交… @@ -4013,4 +4027,4 @@ test = 好的 [repo.permissions] ext_issues = 存取外部問題追蹤器的連結。權限由外部管理。 -ext_wiki = 存取外部 Wiki 的連結。權限由外部管理。 \ No newline at end of file +ext_wiki = 存取外部百科的連結。權限由外部管理。 \ No newline at end of file diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 95c230e37a..d8ceab0d4c 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -72,5 +72,31 @@ "one": "Před %d měsícem", "few": "Před %d měsíci", "other": "Před %d měsíci" - } + }, + "moderation.report_content": "Nahlásit obsah", + "moderation.report_abuse_form.details": "Tento formulář je určen k nahlašování uživatelů, kteří si vytvářejí spamové profily, repozitáře, problémy, komentáře nebo se chovají nevhodně.", + "admin.config.moderation_config": "Nastavení moderování", + "moderation.report_abuse": "Nahlásit zneužití", + "moderation.report_abuse_form.header": "Nahlásit zneužití administrátorovi", + "moderation.report_abuse_form.invalid": "Neplatné argumenty", + "moderation.report_abuse_form.already_reported": "Tento obsah jste již nahlásili", + "moderation.abuse_category": "Kategorie", + "moderation.submit_report": "Odeslat hlášení", + "moderation.reporting_failed": "Nepodařilo se odeslat nové hlášení zneužití: %v", + "moderation.report_remarks.placeholder": "Zadejte prosím podrobnosti o zneužití, které nahlašujete.", + "moderation.reported_thank_you": "Děkujeme za vaše hlášení. Správa platformy na ni byla upozorněna.", + "moderation.report_remarks": "Informace", + "moderation.abuse_category.placeholder": "Vyberte kategorii", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.illegal_content": "Nezákonný obsah", + "moderation.abuse_category.other_violations": "Jiné porušení pravidel platformy", + "repo.form.cannot_create": "Všechny prostory, ve kterých můžete vytvářet repozitáře, dosáhly svého limitu.", + "repo.issue_indexer.title": "Indexování problémů", + "watch.list.none": "Tento repozitář nikdo nesleduje.", + "followers.incoming.list.self.none": "Váš profil nikdo nesleduje.", + "followers.incoming.list.none": "Tohoto uživatele nikdo nesleduje.", + "followers.outgoing.list.none": "%s nikoho nesleduje.", + "stars.list.none": "Tento repozitář si nikdo nepřidal do oblíbených.", + "followers.outgoing.list.self.none": "Nikoho nesledujete." } diff --git a/options/locale_next/locale_da.json b/options/locale_next/locale_da.json index 31e17c1d10..34eeec8260 100644 --- a/options/locale_next/locale_da.json +++ b/options/locale_next/locale_da.json @@ -64,5 +64,25 @@ "other": "%d år siden" }, "relativetime.2days": "2 dage siden", - "relativetime.1month": "sidste måned" + "relativetime.1month": "sidste måned", + "repo.form.cannot_create": "Alle områder, hvor du kan oprette depoter, har nået grænsen for antal depoter.", + "repo.issue_indexer.title": "Problemindekser", + "moderation.report_remarks": "Bemærkninger", + "admin.config.moderation_config": "Moderationskonfiguration", + "moderation.report_abuse": "Rapportér misbrug", + "moderation.report_content": "Rapportér indhold", + "moderation.report_abuse_form.header": "Rapportér misbrug til administrator", + "moderation.report_abuse_form.details": "Denne formular skal bruges til at rapportere brugere, der opretter spamprofiler, arkiver, problemer, kommentarer eller opfører sig upassende.", + "moderation.report_abuse_form.invalid": "Ugyldige argumenter", + "moderation.report_abuse_form.already_reported": "Du har allerede rapporteret dette indhold", + "moderation.abuse_category": "Kategori", + "moderation.abuse_category.placeholder": "Vælg en kategori", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.illegal_content": "Ulovligt indhold", + "moderation.abuse_category.other_violations": "Andre overtrædelser af platformens regler", + "moderation.report_remarks.placeholder": "Angiv venligst nogle detaljer vedrørende det misbrug, du anmelder.", + "moderation.submit_report": "Indsend rapport", + "moderation.reporting_failed": "Den nye misbrugsrapport kunne ikke indsendes: %v", + "moderation.reported_thank_you": "Tak for din rapport. Administrationen er blevet gjort opmærksom på den." } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index cbc69826cb..2f7fbff8da 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -64,5 +64,31 @@ "other": "vor %d Minuten" }, "relativetime.2days": "vorgestern", - "relativetime.future": "in der Zukunft" + "relativetime.future": "in der Zukunft", + "admin.config.moderation_config": "Moderations-Konfiguration", + "moderation.report_abuse": "Missbrauch melden", + "moderation.report_content": "Inhalt melden", + "moderation.report_abuse_form.header": "Dem Administrator Missbrauch melden", + "moderation.report_abuse_form.details": "Dieses Formular soll genutzt werden um Benutzer zu melden die Spam-Profile, -Repositorys, -Issues, und -Kommentare erstellen, oder sich unangemessen verhalten.", + "moderation.report_abuse_form.invalid": "Ungültige Argumente", + "moderation.report_abuse_form.already_reported": "Du hast diesen Inhalt bereits gemeldet", + "moderation.abuse_category": "Kategorie", + "moderation.abuse_category.placeholder": "Eine Kategorie auswählen", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.illegal_content": "Illegaler Inhalt", + "moderation.abuse_category.other_violations": "Andere Verstöße gegen die Plattformregeln", + "moderation.report_remarks": "Anmerkungen", + "moderation.report_remarks.placeholder": "Bitte stelle Details über den von dir gemeldeten Missbrauch bereit.", + "moderation.submit_report": "Meldung absenden", + "moderation.reporting_failed": "Kann die neue Missbrauchsmeldung nicht absenden: %v", + "moderation.reported_thank_you": "Danke für deine Meldung. Die Administration wurde darüber in Kenntnis gesetzt.", + "repo.form.cannot_create": "Alle Orte, wo du Repositorys erstellen kannst, haben die Obergrenze an Repositorys erreicht.", + "repo.issue_indexer.title": "Issue-Indexer", + "watch.list.none": "Niemand beobachtet dieses Repo.", + "followers.incoming.list.self.none": "Niemand folgt deinem Profil.", + "followers.outgoing.list.self.none": "Du folgst niemanden.", + "followers.outgoing.list.none": "%s folgt niemanden.", + "stars.list.none": "Niemand hat dieses Repo favorisiert.", + "followers.incoming.list.none": "Niemand folgt diesem Benutzer." } diff --git a/options/locale_next/locale_fi-FI.json b/options/locale_next/locale_fi-FI.json index b9e6208440..cb26d76e66 100644 --- a/options/locale_next/locale_fi-FI.json +++ b/options/locale_next/locale_fi-FI.json @@ -20,7 +20,7 @@ "themes.names.forgejo-dark": "Forgejo, tumma", "alert.range_error": " täytyy olla numero välillä %[1]s ja %[2]s.", "alert.asset_load_failed": "Staattisen tiedoston lataus kohteesta {path} epäonnistui. Varmista, että staattisiin tiedostoihin pääsee käsiksi.", - "install.invalid_lfs_path": "LFS juurta ei voitu luoda polkuun: %[1]s", + "install.invalid_lfs_path": "LFS-juurta ei voitu luoda polkuun: %[1]s", "mail.actions.not_successful_run_subject": "Työnkulku %[1]s epäonnistui tietovarastossa %[2]s", "mail.actions.not_successful_run": "Työnkulku %[1]s epäonnistui tietovarastossa %[2]s", "discussion.locked": "Tämä keskustelu on lukittu. Kommentointi on rajoitettu avustajille.", @@ -57,7 +57,7 @@ "one": "%d viikko sitten", "other": "%d viikkoa sitten" }, - "meta.last_line": "Thank you for translating Forgejo! This line isn't seen by the users but it serves other purposes in the translation management. You can place a fun fact in the translation instead of translating it. Päivitä tämä käännös, jos luet tämän viestin.", + "meta.last_line": "Thank you for translating Forgejo! Päivitä tämä käännös, jos luet tämän viestin.", "relativetime.2months": "kaksi kuukautta sitten", "mail.actions.successful_run_after_failure_subject": "Työnkulku %[1]s palautettu tietovarastoon %[2]s", "mail.actions.successful_run_after_failure": "Työnkulku %[1]s palautettu tietovarastoon %[2]s", diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 649f9d7acb..d7fdc59a7e 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -64,5 +64,24 @@ }, "discussion.locked": "Naka-kandado ang pag-uusap na ito. Nilimitahan ang pagkomento sa mga tagatulong.", "relativetime.1month": "nakaraang buwan", - "relativetime.1year": "nakaraang taon" + "relativetime.1year": "nakaraang taon", + "moderation.report_abuse_form.details": "Dapat gamitin ang form na ito upang mag-ulat ng mga user na gumagawa ng mga spam na profile, repositoryo, isyu, komento, o kumikilos nang hindi naaangkop.", + "admin.config.moderation_config": "Pagsasaayos ng Moderation", + "moderation.report_abuse": "Mag-ulat ng pang aabuso", + "moderation.report_content": "Iulat ng nilalaman", + "moderation.report_abuse_form.header": "Mag-ulat ng pang aabuso sa tagapangasiwa", + "moderation.report_abuse_form.invalid": "Hindi wasto ang mga argumento", + "moderation.report_abuse_form.already_reported": "Inulat mo na ang nilalaman na ito", + "moderation.abuse_category": "Kategorya", + "moderation.abuse_category.placeholder": "Pumili ng kategorya", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.illegal_content": "Ilegal na nilalaman", + "moderation.abuse_category.other_violations": "Mga ibang paglabag sa mga patakaran ng platform", + "moderation.report_remarks": "Mga pahayag", + "moderation.report_remarks.placeholder": "Mangyaring magbigay ng mga detalye tungkol sa pang aabuso na inuulat mo.", + "moderation.submit_report": "I-submit ang ulat", + "moderation.reporting_failed": "Hindi ma-submit ang bagong ulat sa pang aabuso: %v", + "moderation.reported_thank_you": "Salamat sa iyong ulat. Naipaalam na ito sa administrasyon.", + "repo.form.cannot_create": "Naabot na ng lahat ng mga espasyo kung saan ka makakagawa ng mga repositoryo ang limitasyon ng mga repositoryo." } diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index cff682b10a..a95e70384f 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -65,5 +65,29 @@ "error.not_found.title": "Page non trouvée", "relativetime.1month": "le mois dernier", "incorrect_root_url": "Cette instance Forgejo est configuré pour être servi sur \"%s\". Vous êtes actuellement en train de regarder Forgejo avec une URL différente, ce qui pourrait casser certaines parties de cette application. L'URL canonique est controllée par les administrateurs Forgejo grâce au paramètre ROOT_URL dans le app.ini.", - "meta.last_line": "Merci de traduire Forgejo ! Cette ligne n'est pas vue par les utilisateurs mais sert à d'autres fins dans la gestion de la traduction. Vous pouvez mettre une fun fact dans la traduction au lieu de la traduire. Miaou." + "meta.last_line": "Merci de traduire Forgejo ! Cette ligne n'est pas vue par les utilisateurs mais sert à d'autres fins dans la gestion de la traduction. Vous pouvez mettre une fun fact dans la traduction au lieu de la traduire. Miaou.", + "mail.actions.successful_run_after_failure": "Workflow %[1]s récupéré dans le dépôt %[2]s", + "mail.actions.successful_run_after_failure_subject": "Workflow %[1]s récupéré dans le dépôt %[2]s", + "mail.actions.not_successful_run_subject": "Workflow %[1]s a raté dans le dépôt %[2]s", + "mail.actions.not_successful_run": "Workflow %[1]s raté dans le dépôt %[2]s", + "mail.actions.run_info_cur_status": "Statut de cette exécution : %[1]s (vient de se mettre à jour de %[2]s)", + "mail.actions.run_info_previous_status": "Précédent statut de l'éxecution : %[1]s", + "moderation.reported_thank_you": "Merci pour votre signalement. L'administration a été mise au courant.", + "admin.config.moderation_config": "Configuration de la modération", + "moderation.report_content": "Contenu du rapport", + "moderation.report_abuse": "Signaler un rapport", + "moderation.report_abuse_form.header": "Signaler le rapport à l'administrateur", + "moderation.report_abuse_form.details": "Ce formulaire devrait être utilisé pour signaler des utilisateurs qui spam des comptes, des dépôts, des questions, des commentaires ou qui se comportent de façon inappropriée.", + "moderation.report_abuse_form.invalid": "Arguments invalides", + "moderation.report_abuse_form.already_reported": "Vous avez déjà signalisé ce contenu", + "moderation.abuse_category": "Catégorie", + "moderation.abuse_category.placeholder": "Sélectionnez une catégorie", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "logiciel malveillant", + "moderation.abuse_category.illegal_content": "Contenu illégal", + "moderation.abuse_category.other_violations": "Autres infractions des règles de la plateforme", + "moderation.report_remarks": "Remarques", + "moderation.report_remarks.placeholder": "S'il vous plaît fournissez quelques détails en rapport avec l'abus que vous signalez.", + "moderation.submit_report": "Soumettre le signalement", + "moderation.reporting_failed": "Impossible de soumettre le nouveau signalement : %v" } diff --git a/options/locale_next/locale_gl.json b/options/locale_next/locale_gl.json index 0967ef424b..e83a2ecbd9 100644 --- a/options/locale_next/locale_gl.json +++ b/options/locale_next/locale_gl.json @@ -1 +1,39 @@ -{} +{ + "relativetime.now": "agora", + "relativetime.future": "máis adiante", + "repo.form.cannot_create": "Todos os espazos onde podes crear repositorios chegaron ao límite de repositorios creados.", + "followers.outgoing.list.self.none": "Non esas seguindo a ninguén.", + "followers.outgoing.list.none": "%s non está seguindo a ninguén.", + "relativetime.1day": "onte", + "relativetime.2days": "hai dous días", + "relativetime.1week": "a semana pasada", + "relativetime.2weeks": "hai dúas semanas", + "relativetime.1month": "hai un mes", + "relativetime.2months": "hai dous meses", + "relativetime.1year": "hai un ano", + "relativetime.2years": "hai dous anos", + "relativetime.mins": { + "one": "hai %d minuto", + "other": "hai %d minutos" + }, + "relativetime.hours": { + "one": "hai %d hora", + "other": "hai %d horas" + }, + "relativetime.days": { + "one": "hai %d día", + "other": "hai %d días" + }, + "relativetime.weeks": { + "one": "hai %d semana", + "other": "hai %d semanas" + }, + "relativetime.months": { + "one": "hai %d mes", + "other": "hai %d meses" + }, + "relativetime.years": { + "one": "hai %d ano", + "other": "hai %d anos" + } +} diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 66224cd567..69fccbec90 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -72,5 +72,31 @@ "one": "pirms %d nedēļas", "other": "pirms %d nedēļām" }, - "relativetime.1month": "iepriekšējā mēnesī" + "relativetime.1month": "iepriekšējā mēnesī", + "moderation.abuse_category.other_violations": "Citi noteikumu pārkāpumi", + "moderation.reported_thank_you": "Paldies par ziņojumu! Pārvaldītājiem ir darīts zināms par to.", + "admin.config.moderation_config": "Satura pārraudzības konfigurācija", + "moderation.report_abuse": "Ziņot par pārkāpumu", + "moderation.report_content": "Ziņot par saturu", + "moderation.report_abuse_form.header": "Ziņot pārvaldītājam par pārkāpumu", + "moderation.report_abuse_form.details": "Šī veidlapa ir izmantojama, lai ziņotu par lietotājiem, kuri izveido mēstuļošanas profilus, glabātavas, pieteikumus un piebildes vai uzvedas nepienācīgi.", + "moderation.report_abuse_form.invalid": "Nederīgi argumenti", + "moderation.report_abuse_form.already_reported": "Tu jau ziņoji par šo saturu", + "moderation.abuse_category": "Kategorija", + "moderation.abuse_category.placeholder": "Atlasīt kategoriju", + "moderation.abuse_category.spam": "Mēstuļošana", + "moderation.abuse_category.malware": "Ļaunatūra", + "moderation.abuse_category.illegal_content": "Nelikumīgs saturs", + "moderation.report_remarks": "Piezīmes", + "moderation.report_remarks.placeholder": "Lūgums sniegt informāciju par pārkāpumu, par ko ziņo.", + "moderation.submit_report": "Iesniegt ziņojumu", + "moderation.reporting_failed": "Nevar iesniegt jauno ziņojumu par pārkāpumu: %v", + "repo.form.cannot_create": "Visas vietas, kurās vari izveidot glabātavas, ir sasniegušas glabātavu skaita ierobežojumu.", + "repo.issue_indexer.title": "Pieteikumu indeksētājs", + "watch.list.none": "Neviens nevēro šo glabātavu.", + "followers.incoming.list.none": "Neviens neseko šim lietotājam.", + "followers.outgoing.list.self.none": "Tu nevienam neseko.", + "followers.outgoing.list.none": "%s nevienam neseko.", + "stars.list.none": "Neviens šo glabātavu nav atzīmējis ar zvaigzni.", + "followers.incoming.list.self.none": "Neviens neseko Tavam profilam." } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index 52c3194bfd..71225a720b 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -64,5 +64,33 @@ "one": "vör %d Dag", "other": "vör %d Dagen" }, - "relativetime.2days": "vörgüstern" + "relativetime.2days": "vörgüstern", + "moderation.report_abuse": "Missbruuk mellen", + "moderation.report_content": "Inholl mellen", + "moderation.report_abuse_form.invalid": "Ungültige Argumenten", + "moderation.report_abuse_form.already_reported": "Du hest deesen Inholl al mellt", + "moderation.abuse_category": "Deel", + "moderation.abuse_category.placeholder": "Köör een Deel ut", + "moderation.abuse_category.spam": "Oolkert-Tüüg", + "moderation.abuse_category.illegal_content": "Verboden Inholl", + "moderation.abuse_category.other_violations": "Anner Verstöten tegen de Plattfoorms-Regels", + "moderation.report_remarks": "Anmarkens", + "moderation.submit_report": "Mellen ofschicken", + "moderation.reporting_failed": "Kann dat neje Missbruuk-Mellen nich ofschicken: %v", + "moderation.reported_thank_you": "Wees bedankt för dien Mellen. De Sied-Chefs hebben de Naricht daaröver kregen.", + "moderation.report_remarks.placeholder": "Bidde giff mehr Informatioonen över de Missbruuk, wat du mellst, an.", + "admin.config.moderation_config": "Moderatioons-Instellens", + "moderation.report_abuse_form.header": "Missbruuk an de Chef mellen", + "moderation.report_abuse_form.details": "Deeses Formular kann bruukt worden, um Brukers to mellen, wenn se Oolkert-Profilen, -Repositoriums, -Gefallens of -Kommentaren maken of sik unanstännig verhollen.", + "moderation.abuse_category.malware": "Schaa-Waar", + "repo.form.cannot_create": "All Rumen, waar du Repositoriums maken kannst, enthollen al de grootste Tahl vun verlöövt Repositoriums.", + "repo.issue_indexer.title": "Gefallen-Indizerer", + "followers.outgoing.list.none": "%s gaht nüms na.", + "watch.list.none": "Nüms beluurt deeses Repositorium.", + "followers.incoming.list.self.none": "Nüms gaht dienem Profil na.", + "followers.incoming.list.none": "Nüms gaht deesem Bruker na.", + "followers.outgoing.list.self.none": "Du gahst nüms na.", + "stars.list.none": "Nüms hett up deesem Repositorium eenen Steern sett.", + "editor.textarea.tab_hint": "Rieg al inschuven. Drück weer Tab of Esc, um de Bewarker to verlaten.", + "editor.textarea.shift_tab_hint": "Keen Inschuuv in deeser Rieg. Drück weer Umschalt+Tab of Esc, um de Bewarker to verlaten." } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index 52971e734a..a419c93078 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -30,5 +30,61 @@ "mail.actions.run_info_previous_status": "Status vorige run: %[1]s", "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", "mail.actions.run_info_trigger": "Getriggerd omdat: %[1]s door: %[2]s", - "discussion.locked": "Deze discussie is gesloten. Commentaar is beperkt tot bijdragers." + "discussion.locked": "Deze discussie is gesloten. Commentaar is beperkt tot bijdragers.", + "relativetime.now": "nu", + "relativetime.future": "in de toekomst", + "repo.form.cannot_create": "Alle spaces waarin u repositories kan maken hebben hun maximum aantal repositories bereikt.", + "moderation.report_content": "Meldt content", + "moderation.report_abuse_form.header": "Meld misbruik bij de beheerder", + "moderation.report_abuse_form.invalid": "Ongeldige argumenten", + "moderation.report_abuse_form.already_reported": "U heeft deze content reeds gerapporteerd", + "moderation.abuse_category": "Categorie", + "moderation.abuse_category.placeholder": "Selecteer een categorie", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.report_abuse_form.details": "Dit formulier dient gebruikt te worden om gebruikers te rapporteren die spamprofielen, -issues, -commentaren aanmaken of zich anderszins misdragen.", + "moderation.submit_report": "Verzend rapport", + "moderation.reporting_failed": "De nieuwe misbruikrapportage kan niet worden ingediend: %v", + "moderation.reported_thank_you": "Dank voor uw melding. Het beheer is hierover nu geïnformeerd.", + "moderation.report_remarks.placeholder": "Geef a.u.b. wat details betreffende het misbruik dat u meldt.", + "followers.outgoing.list.none": "%s volgt niemand.", + "relativetime.1day": "gisteren", + "relativetime.2days": "twee dagen geleden", + "relativetime.1week": "vorige week", + "relativetime.2weeks": "twee weken geleden", + "relativetime.1month": "vorige maand", + "relativetime.2months": "twee maanden geleden", + "relativetime.1year": "vorig jaar", + "relativetime.2years": "twee jaar geleden", + "moderation.abuse_category.illegal_content": "Illegale content", + "moderation.abuse_category.other_violations": "Andere overtreding van platformregels", + "moderation.report_remarks": "Opmerkingen", + "editor.textarea.tab_hint": "Regel springt reeds in. Type Tab nogmaals of Escape om de editor te verlaten.", + "editor.textarea.shift_tab_hint": "Geen inspringing op deze regel. Type Shift + Tab nogmaals of Escape om de editor te verlaten.", + "relativetime.days": { + "one": "%d dag geleden", + "other": "%d dagen geleden" + }, + "relativetime.weeks": { + "one": "%d week geleden", + "other": "%d weken geleden" + }, + "relativetime.months": { + "one": "%d maand geleden", + "other": "%d maanden geleden" + }, + "relativetime.years": { + "one": "%d jaar geleden", + "other": "%d jaren geleden" + }, + "admin.config.moderation_config": "Moderatie configuratie", + "moderation.report_abuse": "Meld misbruik", + "relativetime.mins": { + "one": "%d minuut geleden", + "other": "%d minuten geleden" + }, + "relativetime.hours": { + "one": "%d uur geleden", + "other": "%d uren geleden" + } } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index a1fc84fc90..eee993e1ff 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -72,5 +72,31 @@ "other": "%d horas atrás" }, "relativetime.2weeks": "duas semanas atrás", - "relativetime.now": "agora" + "relativetime.now": "agora", + "moderation.reported_thank_you": "Agradecemos pela sua denúncia. A administração foi notificada.", + "moderation.report_content": "Denunciar conteúdo", + "admin.config.moderation_config": "Configuração de moderação", + "moderation.report_abuse": "Denunciar abuso", + "moderation.report_abuse_form.header": "Denunciar abuso à administração", + "moderation.report_abuse_form.invalid": "Argumentos inválidos", + "moderation.report_abuse_form.already_reported": "Você já denunciou este conteúdo", + "moderation.abuse_category": "Categoria", + "moderation.abuse_category.placeholder": "Selecione uma categoria", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.illegal_content": "Conteúdo ilegal", + "moderation.abuse_category.other_violations": "Outras violações de regras da plataforma", + "moderation.report_remarks": "Observações", + "moderation.report_remarks.placeholder": "Por favor, forneça detalhes sobre o abuso que você está denunciando.", + "moderation.submit_report": "Enviar denúncia", + "moderation.reporting_failed": "Não foi possível enviar a denúncia: %v", + "moderation.report_abuse_form.details": "Este formulário deve ser utilizado para denunciar contas com perfis, repositórios, issues e comentários com spam ou que se comportam inapropriadamente.", + "repo.form.cannot_create": "Todos os espaços onde você pode criar repositórios atingiram o limite de repositórios.", + "repo.issue_indexer.title": "Indexador de Issues", + "watch.list.none": "Ninguém está observando este repositório.", + "followers.incoming.list.self.none": "Ninguém está seguindo seu perfil.", + "followers.incoming.list.none": "Ninguém está seguindo este perfil.", + "followers.outgoing.list.none": "%s não está seguindo ninguém.", + "stars.list.none": "Ninguém favoritou este repositório.", + "followers.outgoing.list.self.none": "Você não está seguindo ninguém." } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index 02d8087314..e43f34c6da 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -72,5 +72,33 @@ "other": "%d horas atrás" }, "relativetime.1week": "semana passada", - "relativetime.1day": "ontem" + "relativetime.1day": "ontem", + "moderation.report_remarks.placeholder": "Por favor forneça alguns pormenores sobre o abuso que está a denunciar.", + "admin.config.moderation_config": "Configuração de moderação", + "moderation.report_abuse": "Denunciar abuso", + "moderation.report_content": "Denunciar conteúdo", + "moderation.report_abuse_form.header": "Denunciar abuso à administração", + "moderation.report_abuse_form.details": "Este formulário deve ser utilizado para denunciar utilizadores que criam perfis, repositórios, questões, comentários de spam ou que se comportam de forma inadequada.", + "moderation.report_abuse_form.invalid": "Argumentos inválidos", + "moderation.report_abuse_form.already_reported": "Já denunciou este conteúdo", + "moderation.abuse_category": "Categoria", + "moderation.abuse_category.placeholder": "Escolha uma categoria", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.illegal_content": "Conteúdo ilegal", + "moderation.abuse_category.other_violations": "Outras violações das regras da plataforma", + "moderation.report_remarks": "Observações", + "moderation.submit_report": "Submeter denúncia", + "moderation.reporting_failed": "Não foi possível submeter a nova denúncia de abuso: %v", + "moderation.reported_thank_you": "Obrigado pela sua denúncia. A administração foi informada do facto.", + "repo.form.cannot_create": "Todos os espaços nos quais pode criar repositórios atingiram o limite de repositórios.", + "repo.issue_indexer.title": "Indexador de Questões", + "watch.list.none": "Ninguém está a vigiar este repositório.", + "followers.incoming.list.self.none": "Ninguém está a seguir o seu perfil.", + "followers.incoming.list.none": "Ninguém está a seguir este utilizador.", + "followers.outgoing.list.none": "%s não está a seguir ninguém.", + "followers.outgoing.list.self.none": "Não está a seguir ninguém.", + "editor.textarea.tab_hint": "Linha já indentada. Pressione Tab novamente ou Escape para sair do editor.", + "editor.textarea.shift_tab_hint": "Sem indentação nesta linha. Pressione Shift + Tab novamente ou Escape para sair do editor.", + "stars.list.none": "Ninguém juntou este repositório aos favoritos." } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index 86011f8ac9..0f2e7d0a3d 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -23,7 +23,7 @@ "alert.asset_load_failed": "Не удалось получить ресурсы из {path}. Убедитесь, что файлы ресурсов доступны.", "install.invalid_lfs_path": "Не удалось расположить корень LFS по указанному пути: %[1]s", "alert.range_error": " - число должно быть в диапазоне от %[1]s-%[2]s.", - "meta.last_line": "Triggering CI skip again.", + "meta.last_line": "Unskip.", "mail.actions.not_successful_run_subject": "Провал раб. потока %[1]s в репозитории %[2]s", "mail.actions.successful_run_after_failure_subject": "Возобновление раб. потока %[1]s в репозитории %[2]s", "mail.actions.run_info_ref": "Ветвь: %[1]s (%[2]s)", @@ -72,5 +72,31 @@ "few": "%d месяца назад", "many": "%d месяцев назад" }, - "relativetime.1year": "в прошлом году" + "relativetime.1year": "в прошлом году", + "repo.issue_indexer.title": "Индексатор задач", + "followers.incoming.list.self.none": "На вас никто не подписан.", + "followers.incoming.list.none": "На этого пользователя никто не подписан.", + "followers.outgoing.list.none": "%s ни на кого не подписан.", + "stars.list.none": "Никто не добавил этот репозиторий в избранное.", + "watch.list.none": "Никто не отслеживает этот репозиторий.", + "repo.form.cannot_create": "Во всех пространствах, где вы можете создавать репозитории, достигнуто ограничение количества репозиториев.", + "followers.outgoing.list.self.none": "Вы ни на кого не подписаны.", + "admin.config.moderation_config": "Настройки модерации", + "moderation.abuse_category": "Категория", + "moderation.abuse_category.placeholder": "Выберите категорию", + "moderation.abuse_category.spam": "Спам", + "moderation.abuse_category.malware": "Вредоносное ПО", + "moderation.abuse_category.illegal_content": "Незаконное содержимое", + "moderation.abuse_category.other_violations": "Прочие нарушения правил", + "moderation.report_remarks": "Подробности", + "moderation.report_abuse": "Пожаловаться", + "moderation.report_remarks.placeholder": "Пожалуйста, предоставьте немного подробностей о содержимом, на которое вы жалуетесь.", + "moderation.reporting_failed": "Не удалось отправить жалобу: %v", + "moderation.report_content": "Пожаловаться", + "moderation.report_abuse_form.already_reported": "Вы уже пожаловались на это содержимое", + "moderation.submit_report": "Пожаловаться", + "moderation.reported_thank_you": "Спасибо за ваше сообщение. Администрация оповещена.", + "moderation.report_abuse_form.details": "Через эту форму можно жаловаться на пользователей, распространяющих спам или ведущих себя неадекватно.", + "moderation.report_abuse_form.invalid": "Невалидные аргументы", + "moderation.report_abuse_form.header": "Жалоба администрации" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index 20f68b06ca..998237c6b4 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -72,5 +72,33 @@ "one": "%d рік тому", "few": "%d роки тому", "many": "%d років тому" - } + }, + "admin.config.moderation_config": "Конфігурація модерування", + "moderation.abuse_category.placeholder": "Виберіть категорію", + "moderation.report_abuse_form.invalid": "Недійсні аргументи", + "moderation.report_abuse_form.already_reported": "Ви вже скаржилися на цей вміст", + "moderation.report_abuse": "Повідомити про порушення", + "moderation.report_content": "Поскаржитися на вміст", + "moderation.abuse_category.spam": "Спам", + "moderation.report_remarks.placeholder": "Будь ласка, докладно опишіть порушення, про яке ви повідомляєте.", + "moderation.submit_report": "Надіслати скаргу", + "moderation.abuse_category.malware": "Шкідливе ПЗ", + "moderation.abuse_category.illegal_content": "Нелегальний вміст", + "moderation.report_abuse_form.header": "Повідомити адміністраторам про порушення", + "moderation.report_abuse_form.details": "Використовуйте цю форму, щоб повідомити про користувачів, які створюють спам-профілі, репозиторії, задачі, коментарі або поводяться неналежним чином.", + "moderation.abuse_category": "Категорія", + "moderation.abuse_category.other_violations": "Інші порушення правил платформи", + "moderation.reporting_failed": "Не вдалося надіслати нове повідомлення про порушення: %v", + "moderation.report_remarks": "Подробиці", + "moderation.reported_thank_you": "Дякуємо за ваше повідомлення. Адміністрацію поінформовано про нього.", + "repo.form.cannot_create": "Усі простори, в яких ви можете створювати репозиторії, досягли максимальної кількості репозиторіїв.", + "repo.issue_indexer.title": "Індексатор задач", + "followers.incoming.list.self.none": "Ніхто не стежить за вашим профілем.", + "followers.incoming.list.none": "Ніхто не стежить за цим користувачем.", + "followers.outgoing.list.self.none": "Ви ні за ким не стежите.", + "followers.outgoing.list.none": "%s ні за ким не стежить.", + "stars.list.none": "Ніхто не додав цей репозиторій в обрані.", + "watch.list.none": "Ніхто не спостерігає за цим репозиторієм.", + "editor.textarea.tab_hint": "У рядку вже є відступ. Натисніть Tab ще раз або Esc, щоб вийти з редактора.", + "editor.textarea.shift_tab_hint": "У цьому рядку немає відступів. Натисніть Shift + Tab ще раз або Esc, щоб вийти з редактора." } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index dab8832e9a..3d77467a4a 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -40,5 +40,31 @@ "relativetime.2years": "两年前", "relativetime.now": "现在", "relativetime.2weeks": "两周前", - "relativetime.days": "%d 天前" + "relativetime.days": "%d 天前", + "moderation.report_abuse_form.details": "此表单用于举报创建垃圾个人信息、仓库、议题、评论或行为不当的用户。", + "admin.config.moderation_config": "审核配置", + "moderation.report_abuse": "举报滥用", + "moderation.report_content": "举报内容", + "moderation.report_abuse_form.header": "向管理员举报滥用", + "moderation.report_abuse_form.invalid": "参数无效", + "moderation.report_abuse_form.already_reported": "您已举报此内容", + "moderation.abuse_category": "类别", + "moderation.abuse_category.placeholder": "选择类别", + "moderation.abuse_category.spam": "垃圾信息", + "moderation.abuse_category.malware": "恶意软件", + "moderation.abuse_category.illegal_content": "非法内容", + "moderation.abuse_category.other_violations": "其他违反平台规则的行为", + "moderation.report_remarks.placeholder": "请提供一些关于您所举报的滥用行为的详细信息。", + "moderation.report_remarks": "备注", + "moderation.submit_report": "提交举报", + "moderation.reporting_failed": "无法提交新的滥用举报:%v", + "moderation.reported_thank_you": "感谢您的举报,管理员已收到通知。", + "repo.form.cannot_create": "您可以创建仓库的所有空间均已达到仓库限制。", + "repo.issue_indexer.title": "议题索引器", + "watch.list.none": "没有人关注这个仓库。", + "followers.incoming.list.none": "没有人关注这位用户。", + "followers.outgoing.list.self.none": "你没有关注任何人。", + "followers.outgoing.list.none": "%s 没有关注任何人。", + "stars.list.none": "没有人点赞这个仓库。", + "followers.incoming.list.self.none": "没有人关注你的个人资料。" } From e2278e5a38187a1dc84dc41d583ec8b44e7257c1 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Wed, 28 May 2025 05:16:19 +0200 Subject: [PATCH 40/94] fix(ui): change escaping button bg on selected lines (#7944) * add escape part of line to the list of selectors, so it doesn't cause a hole in selected lines * fix duplicated element ID in template * move some CSS out of base.css to dedicated files, so it is less cluttered Before: https://codeberg.org/attachments/0eaa277b-98e7-42de-98a2-6aca99ffcbe4 After: https://codeberg.org/attachments/124bbb86-c377-4fef-a0e3-403e8c850275 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7944 Reviewed-by: floss4good Reviewed-by: Gusted --- templates/repo/view_file.tmpl | 2 +- tests/e2e/declare_repos_test.go | 4 ++++ tests/e2e/repo-code.test.e2e.ts | 34 +++++++++++++++++++++++++--- web_src/css/base.css | 35 ----------------------------- web_src/css/index.css | 1 + web_src/css/markup/content.css | 4 ++++ web_src/css/repo.css | 3 --- web_src/css/repo/file-view.css | 39 +++++++++++++++++++++++++++++++++ 8 files changed, 80 insertions(+), 42 deletions(-) create mode 100644 web_src/css/repo/file-view.css diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index b2443e82c4..bf668e1347 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -135,7 +135,7 @@ {{range $idx, $code := .FileContent}} {{$line := Eval $idx "+" 1}} - + {{if $.EscapeStatus.Escaped}} {{if (index $.LineEscapeStatus $idx).Escaped}}{{end}} {{end}} diff --git a/tests/e2e/declare_repos_test.go b/tests/e2e/declare_repos_test.go index f45687651c..83ee40c71a 100644 --- a/tests/e2e/declare_repos_test.go +++ b/tests/e2e/declare_repos_test.go @@ -53,6 +53,10 @@ func DeclareGitRepos(t *testing.T) func() { CommitMsg: "Another commit which mentions @user1 in the title\nand @user2 in the text", }, }), + newRepo(t, 2, "unicode-escaping", []FileChanges{{ + Filename: "a-file", + Versions: []string{"{a}{а}"}, + }}), // add your repo declarations here } diff --git a/tests/e2e/repo-code.test.e2e.ts b/tests/e2e/repo-code.test.e2e.ts index 11b710c956..9184c3ce67 100644 --- a/tests/e2e/repo-code.test.e2e.ts +++ b/tests/e2e/repo-code.test.e2e.ts @@ -1,7 +1,13 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + // @watch start -// web_src/js/features/repo-code.js -// web_src/css/repo.css // services/gitdiff/** +// templates/repo/view_file.tmpl +// web_src/css/repo.css +// web_src/css/repo/file-view.css +// web_src/js/features/repo-code.js +// web_src/js/features/repo-unicode-escape.js // @watch end import {expect, type Page} from '@playwright/test'; @@ -16,7 +22,7 @@ async function assertSelectedLines(page: Page, nums: string[]) { .toStrictEqual(nums); // the first line selected has an action button - if (nums.length > 0) await expect(page.locator(`#L${nums[0]} .code-line-button`)).toBeVisible(); + if (nums.length > 0) await expect(page.locator(`.lines-num:has(#L${nums[0]}) .code-line-button`)).toBeVisible(); }; await pageAssertions(); @@ -99,3 +105,25 @@ test.describe('As authenticated user', () => { await save_visual(page); }); }); + +test('Unicode escape highlight', async ({page}) => { + const unselectedBg = 'rgba(0, 0, 0, 0)'; + const selectedBg = 'rgb(255, 237, 213)'; + + const response = await page.goto('/user2/unicode-escaping/src/branch/main/a-file'); + expect(response?.status()).toBe(200); + + await expect(page.locator('.unicode-escape-prompt')).toBeVisible(); + expect(await page.locator('.lines-num').evaluate((el) => getComputedStyle(el).backgroundColor)).toBe(unselectedBg); + expect(await page.locator('.lines-escape').evaluate((el) => getComputedStyle(el).backgroundColor)).toBe(unselectedBg); + expect(await page.locator('.lines-code').evaluate((el) => getComputedStyle(el).backgroundColor)).toBe(unselectedBg); + + await page.locator('#L1').click(); + expect(await page.locator('.lines-num').evaluate((el) => getComputedStyle(el).backgroundColor)).toBe(selectedBg); + expect(await page.locator('.lines-escape').evaluate((el) => getComputedStyle(el).backgroundColor)).toBe(selectedBg); + expect(await page.locator('.lines-code').evaluate((el) => getComputedStyle(el).backgroundColor)).toBe(selectedBg); + + await page.locator('.code-line-button').click(); + await expect(page.locator('.tippy-box .view_git_blame[href$="/a-file#L1"]')).toBeVisible(); + await expect(page.locator('.tippy-box .copy-line-permalink[data-url$="/a-file#L1"]')).toBeVisible(); +}); diff --git a/web_src/css/base.css b/web_src/css/base.css index a962ea031a..bc9c8d3f39 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1119,41 +1119,6 @@ svg.text.purple, padding-left: 1ch; } -.lines-escape { - width: 0; -} - -.lines-code { - padding-left: 5px; -} - -.file-view tr.active { - color: inherit !important; - background: inherit !important; -} - -.file-view tr.active .lines-num, -.file-view tr.active .lines-code { - background: var(--color-highlight-bg) !important; -} - -.file-view tr.active:last-of-type .lines-code { - border-bottom-right-radius: var(--border-radius); -} - -.file-view tr.active .lines-num { - position: relative; -} - -.file-view tr.active .lines-num::before { - content: ""; - position: absolute; - left: 0; - width: 2px; - height: 100%; - background: var(--color-highlight-fg); -} - .code-inner { font: 12px var(--fonts-monospace); white-space: pre-wrap; diff --git a/web_src/css/index.css b/web_src/css/index.css index 88aa9bbf4a..0e9f2b173a 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -57,6 +57,7 @@ @import "./form.css"; @import "./repo.css"; +@import "./repo/file-view.css"; @import "./repo/release-tag.css"; @import "./repo/issue-card.css"; @import "./repo/issue-label.css"; diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css index a70a32b227..2d3617fac3 100644 --- a/web_src/css/markup/content.css +++ b/web_src/css/markup/content.css @@ -561,6 +561,10 @@ border-top-right-radius: 0 !important; } +.file-view.markup { + padding: 2em; +} + .file-view.markup.orgmode li.unchecked::before { content: "[ ] "; } diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 651923e60b..cf916f0361 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -1684,9 +1684,6 @@ details.repo-search-result summary::marker { white-space: nowrap; } -.file-view.markup { - padding: 2em; -} .repository .activity-header { display: flex; justify-content: space-between; diff --git a/web_src/css/repo/file-view.css b/web_src/css/repo/file-view.css new file mode 100644 index 0000000000..2ce7f3ec0f --- /dev/null +++ b/web_src/css/repo/file-view.css @@ -0,0 +1,39 @@ +.file-view tr.active { + color: inherit !important; + background: inherit !important; +} + +.lines-escape { + width: 0; +} + +.lines-code { + padding-left: 5px; +} + +.file-view tr.active .lines-num, +.file-view tr.active .lines-escape, +.file-view tr.active .lines-code { + background: var(--color-highlight-bg) !important; +} + +.file-view tr.active:last-of-type .lines-code { + border-bottom-right-radius: var(--border-radius); +} + +.file-view tr.active .lines-num { + position: relative; +} + +.file-view tr.active .interact-bg:hover { + background: var(--color-primary-alpha-50) !important; +} + +.file-view tr.active .lines-num::before { + content: ""; + position: absolute; + left: 0; + width: 2px; + height: 100%; + background: var(--color-highlight-fg); +} From a9f9e7c013d90757bb7d464cb89e26bb27cd246e Mon Sep 17 00:00:00 2001 From: kramo Date: Wed, 28 May 2025 13:22:10 +0200 Subject: [PATCH 41/94] fix(i18n): use correct base capitalization style (#7975) Update several strings according to https://forgejo.org/docs/next/contributor/localization-english/#capitalization Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7975 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Gusted Co-authored-by: kramo Co-committed-by: kramo --- options/locale/locale_en-US.ini | 90 ++++++++++++++++----------------- tests/e2e/webauthn.test.e2e.ts | 2 +- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 856e070efb..1cd824b45d 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -7,9 +7,9 @@ logo = Logo sign_in = Sign in sign_in_with_provider = Sign in with %s sign_in_or = or -sign_out = Sign Out +sign_out = Sign out sign_up = Register -link_account = Link Account +link_account = Link account register = Register version = Version powered_by = Powered by %s @@ -740,7 +740,7 @@ ssh_gpg_keys = SSH / GPG keys applications = Applications orgs = Organizations repos = Repositories -delete = Delete Account +delete = Delete account twofa = Two-factor authentication (TOTP) organization = Organizations uid = UID @@ -1540,11 +1540,11 @@ projects.card_type.images_and_text = Images and text projects.card_type.text_only = Text only issues.desc = Organize bug reports, tasks and milestones. -issues.filter_assignees = Filter Assignee -issues.filter_milestones = Filter Milestone -issues.filter_projects = Filter Project -issues.filter_labels = Filter Label -issues.filter_reviewers = Filter Reviewer +issues.filter_assignees = Filter assignee +issues.filter_milestones = Filter milestone +issues.filter_projects = Filter project +issues.filter_labels = Filter label +issues.filter_reviewers = Filter reviewer issues.filter_no_results = No results issues.filter_no_results_placeholder = Try adjusting your search filters. issues.new = New issue @@ -2106,7 +2106,7 @@ signing.wont_sign.not_signed_in = You are not signed in. ext_wiki = External Wiki wiki = Wiki -wiki.welcome = Welcome to the Wiki. +wiki.welcome = Welcome to the wiki. wiki.welcome_desc = The wiki lets you write and share documentation with collaborators. wiki.desc = Write and share documentation with collaborators. wiki.create_first_page = Create the first page @@ -2389,7 +2389,7 @@ settings.add_collaborator_duplicate = The collaborator is already added to this settings.add_collaborator_blocked_our = Cannot add the collaborator, because the repository owner has blocked them. settings.add_collaborator_blocked_them = Cannot add the collaborator, because they have blocked the repository owner. settings.delete_collaborator = Remove -settings.collaborator_deletion = Remove Collaborator +settings.collaborator_deletion = Remove collaborator settings.collaborator_deletion_desc = Removing a collaborator will revoke their access to this repository. Continue? settings.remove_collaborator_success = The collaborator has been removed. settings.org_not_allowed_to_be_collaborator = Organizations cannot be added as a collaborator. @@ -2645,36 +2645,36 @@ settings.unarchive.text = Unarchiving the repo will restore its ability to recei settings.unarchive.success = The repo was successfully unarchived. settings.unarchive.error = An error occurred while trying to unarchive the repo. See the log for more details. settings.update_avatar_success = The repository avatar has been updated. -settings.lfs=LFS -settings.lfs_filelist=LFS files stored in this repository -settings.lfs_no_lfs_files=No LFS files stored in this repository -settings.lfs_findcommits=Find commits -settings.lfs_lfs_file_no_commits=No commits found for this LFS file -settings.lfs_noattribute=This path does not have the lockable attribute in the default branch -settings.lfs_delete=Delete LFS file with OID %s -settings.lfs_delete_warning=Deleting an LFS file may cause "object does not exist" errors on checkout. Are you sure? -settings.lfs_findpointerfiles=Find pointer files -settings.lfs_locks=Locks -settings.lfs_invalid_locking_path=Invalid path: %s -settings.lfs_invalid_lock_directory=Cannot lock directory: %s -settings.lfs_lock_already_exists=Lock already exists: %s -settings.lfs_lock=Lock -settings.lfs_lock_path=Filepath to lock… -settings.lfs_locks_no_locks=No locks -settings.lfs_lock_file_no_exist=Locked file does not exist in default branch -settings.lfs_force_unlock=Force unlock -settings.lfs_pointers.found=Found %d blob pointer(s) - %d associated, %d unassociated (%d missing from store) -settings.lfs_pointers.sha=Blob hash -settings.lfs_pointers.oid=OID -settings.lfs_pointers.inRepo=In repo -settings.lfs_pointers.exists=Exists in store -settings.lfs_pointers.accessible=Accessible to user -settings.lfs_pointers.associateAccessible=Associate accessible %d OIDs -settings.rename_branch_failed_protected=Cannot rename branch %s because it is a protected branch. -settings.rename_branch_failed_exist=Cannot rename branch because target branch %s exists. -settings.rename_branch_failed_not_exist=Cannot rename branch %s because it does not exist. -settings.rename_branch_success =Branch %s was successfully renamed to %s. -settings.rename_branch=Rename branch +settings.lfs = LFS +settings.lfs_filelist = LFS files stored in this repository +settings.lfs_no_lfs_files = No LFS files stored in this repository +settings.lfs_findcommits = Find commits +settings.lfs_lfs_file_no_commits = No commits found for this LFS file +settings.lfs_noattribute = This path does not have the lockable attribute in the default branch +settings.lfs_delete = Delete LFS file with OID %s +settings.lfs_delete_warning = Deleting an LFS file may cause "object does not exist" errors on checkout. Are you sure? +settings.lfs_findpointerfiles = Find pointer files +settings.lfs_locks = Locks +settings.lfs_invalid_locking_path = Invalid path: %s +settings.lfs_invalid_lock_directory = Cannot lock directory: %s +settings.lfs_lock_already_exists = Lock already exists: %s +settings.lfs_lock = Lock +settings.lfs_lock_path = Filepath to lock… +settings.lfs_locks_no_locks = No locks +settings.lfs_lock_file_no_exist = Locked file does not exist in default branch +settings.lfs_force_unlock = Force unlock +settings.lfs_pointers.found = Found %d blob pointer(s) - %d associated, %d unassociated (%d missing from store) +settings.lfs_pointers.sha = Blob hash +settings.lfs_pointers.oid = OID +settings.lfs_pointers.inRepo = In repo +settings.lfs_pointers.exists = Exists in store +settings.lfs_pointers.accessible = Accessible to user +settings.lfs_pointers.associateAccessible = Associate accessible %d OIDs +settings.rename_branch_failed_protected = Cannot rename branch %s because it is a protected branch. +settings.rename_branch_failed_exist = Cannot rename branch because target branch %s exists. +settings.rename_branch_failed_not_exist = Cannot rename branch %s because it does not exist. +settings.rename_branch_success = Branch %s was successfully renamed to %s. +settings.rename_branch = Rename branch diff.browse_source = Browse source diff.parent = parent @@ -3777,8 +3777,8 @@ swift.install2 = and run the following command: vagrant.install = To add a Vagrant box, run the following command: settings.link = Link this package to a repository settings.link.description = If you link a package with a repository, the package is listed in the repository's package list. -settings.link.select = Select Repository -settings.link.button = Update Repository Link +settings.link.select = Select repository +settings.link.button = Update repository link settings.link.success = Repository link was successfully updated. settings.link.error = Failed to update repository link. settings.delete = Delete package @@ -3824,7 +3824,7 @@ owner.settings.chef.keypair.description = Requests sent to the Chef registry mus secrets = Secrets description = Secrets will be passed to certain actions and cannot be read otherwise. none = There are no secrets yet. -creation = Add Secret +creation = Add secret creation.name_placeholder = case-insensitive, alphanumeric characters or underscores only, cannot start with GITEA_ or GITHUB_ creation.value_placeholder = Input any content. Whitespace at the start and end will be omitted. creation.success = The secret "%s" has been added. @@ -3866,7 +3866,7 @@ runners.task_list.run = Run runners.task_list.status = Status runners.task_list.repository = Repository runners.task_list.commit = Commit -runners.task_list.done_at = Done At +runners.task_list.done_at = Done at runners.edit_runner = Edit Runner runners.update_runner = Update changes runners.update_runner_success = Runner updated successfully @@ -3939,7 +3939,7 @@ variables.update.failed = Failed to edit variable. variables.update.success = The variable has been edited. [projects] -deleted.display_name = Deleted Project +deleted.display_name = Deleted project type-1.display_name = Individual project type-2.display_name = Repository project type-3.display_name = Organization project diff --git a/tests/e2e/webauthn.test.e2e.ts b/tests/e2e/webauthn.test.e2e.ts index 144f52e374..0b5a6a6c2b 100644 --- a/tests/e2e/webauthn.test.e2e.ts +++ b/tests/e2e/webauthn.test.e2e.ts @@ -40,7 +40,7 @@ test('WebAuthn register & login flow', async ({browser, request}, workerInfo) => // Logout. await expect(async () => { await page.locator('div[aria-label="Profile and settings…"]').click(); - await page.getByText('Sign Out').click(); + await page.getByText('Sign out').click(); }).toPass(); await page.waitForURL(`${workerInfo.project.use.baseURL}/`); From 4c4fe595c26c4ac03ff33b68eb9d59e3527b5546 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 28 May 2025 14:44:51 +0200 Subject: [PATCH 42/94] chore(cleanup): fix and simplify API comparison helper (#7978) headIsTag := headGitRepo.IsCommitExist(baseBranch) is wrong on two counts: - it must be assigned to commitIsTag - it must check headBranch and not baseBranch this is not a bug but it certainly is confusing. Also, the logic below headBranchRef := headBranch if headIsBranch { headBranchRef = headBranch } else if headIsTag { headBranchRef = headBranch } can be simplified as: headBranchRef := headBranch Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7978 Reviewed-by: Gusted Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- routers/api/v1/repo/pull.go | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 8456dcff59..b0bc7be90f 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1194,10 +1194,17 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) return nil, nil, nil, "", "" } + baseBranchRef := baseBranch + if baseIsBranch { + baseBranchRef = git.BranchPrefix + baseBranch + } else if baseIsTag { + baseBranchRef = git.TagPrefix + baseBranch + } + // Check if head branch is valid. - headIsCommit := headGitRepo.IsBranchExist(headBranch) - headIsBranch := headGitRepo.IsTagExist(headBranch) - headIsTag := headGitRepo.IsCommitExist(baseBranch) + headIsCommit := ctx.Repo.GitRepo.IsCommitExist(headBranch) + headIsBranch := ctx.Repo.GitRepo.IsBranchExist(headBranch) + headIsTag := ctx.Repo.GitRepo.IsTagExist(headBranch) if !headIsCommit && !headIsBranch && !headIsTag { // Check if headBranch is short sha commit hash if headCommit, _ := headGitRepo.GetCommit(headBranch); headCommit != nil { @@ -1209,18 +1216,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) } } - baseBranchRef := baseBranch - if baseIsBranch { - baseBranchRef = git.BranchPrefix + baseBranch - } else if baseIsTag { - baseBranchRef = git.TagPrefix + baseBranch - } headBranchRef := headBranch - if headIsBranch { - headBranchRef = headBranch - } else if headIsTag { - headBranchRef = headBranch - } compareInfo, err := headGitRepo.GetCompareInfo(repo_model.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranchRef, headBranchRef, false, false) if err != nil { From 7d2a7b855986b2cfdede8d69abd3cb329f072785 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 28 May 2025 14:46:23 +0200 Subject: [PATCH 43/94] feat: add validating user password as trace region (#7981) - Password hashing can take a measurable amount of time, make this more visible in the trace by capturing the computations done in the password hash in their own region. - Ref: forgejo/forgejo#6470 ## Screenshot ![image](/attachments/9834b094-a78f-4ac2-847e-91f221a84833) The upper part are where the tasks are shown (and nothing else). The bottom part is where the interesting execution tracing happens and the part where the user password hashing happens is now properly indicated/highlighted and does not need to be inferred by looking at the stack traces. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7981 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- models/user/user.go | 4 +++- models/user/user_test.go | 2 +- routers/web/auth/auth.go | 2 +- routers/web/user/setting/account.go | 2 +- services/auth/source/db/authenticate.go | 2 +- tests/integration/user_test.go | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/models/user/user.go b/models/user/user.go index d75fe56a20..eedd1db80e 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -15,6 +15,7 @@ import ( "net/url" "path/filepath" "regexp" + "runtime/trace" "strings" "time" "unicode" @@ -397,7 +398,8 @@ func (u *User) SetPassword(passwd string) (err error) { } // ValidatePassword checks if the given password matches the one belonging to the user. -func (u *User) ValidatePassword(passwd string) bool { +func (u *User) ValidatePassword(ctx context.Context, passwd string) bool { + defer trace.StartRegion(ctx, "Validate user password").End() return hash.Parse(u.PasswdHashAlgo).VerifyPassword(passwd, u.Passwd, u.Salt) } diff --git a/models/user/user_test.go b/models/user/user_test.go index 3c302864c7..2a9e652a35 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -267,7 +267,7 @@ func TestHashPasswordDeterministic(t *testing.T) { r2 := u.Passwd assert.NotEqual(t, r1, r2) - assert.True(t, u.ValidatePassword(pass)) + assert.True(t, u.ValidatePassword(t.Context(), pass)) } } } diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 5ee80769d3..dbb6665398 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -764,7 +764,7 @@ func ActivatePost(ctx *context.Context) { ctx.HTML(http.StatusOK, TplActivate) return } - if !user.ValidatePassword(password) { + if !user.ValidatePassword(ctx, password) { ctx.Data["IsPasswordInvalid"] = true ctx.HTML(http.StatusOK, TplActivate) return diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 4967b5f26e..1dfcc90e35 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -57,7 +57,7 @@ func AccountPost(ctx *context.Context) { return } - if ctx.Doer.IsPasswordSet() && !ctx.Doer.ValidatePassword(form.OldPassword) { + if ctx.Doer.IsPasswordSet() && !ctx.Doer.ValidatePassword(ctx, form.OldPassword) { ctx.Flash.Error(ctx.Tr("settings.password_incorrect")) } else if form.Password != form.Retype { ctx.Flash.Error(ctx.Tr("form.password_not_match")) diff --git a/services/auth/source/db/authenticate.go b/services/auth/source/db/authenticate.go index 7c18540a10..b1d8eae6ae 100644 --- a/services/auth/source/db/authenticate.go +++ b/services/auth/source/db/authenticate.go @@ -50,7 +50,7 @@ func Authenticate(ctx context.Context, user *user_model.User, login, password st if !user.IsPasswordSet() { return nil, ErrUserPasswordNotSet{UID: user.ID, Name: user.Name} - } else if !user.ValidatePassword(password) { + } else if !user.ValidatePassword(ctx, password) { return nil, ErrUserPasswordInvalid{UID: user.ID, Name: user.Name} } diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go index 9da62c1cae..0f659e6b02 100644 --- a/tests/integration/user_test.go +++ b/tests/integration/user_test.go @@ -1062,7 +1062,7 @@ func TestUserPasswordReset(t *testing.T) { session.MakeRequest(t, req, http.StatusSeeOther) unittest.AssertNotExistsBean(t, &auth_model.AuthorizationToken{ID: authToken.ID}) - assert.True(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).ValidatePassword("new_password")) + assert.True(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).ValidatePassword(t.Context(), "new_password")) } func TestActivateEmailAddress(t *testing.T) { From e78d1c8210dc0ccaef6488e98ac333cf6242d88c Mon Sep 17 00:00:00 2001 From: Robert Wolff Date: Wed, 28 May 2025 14:50:05 +0200 Subject: [PATCH 44/94] fix: pull request cross references (#7979) Closes #7974. ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7979 Reviewed-by: Earl Warren Co-authored-by: Robert Wolff Co-committed-by: Robert Wolff --- modules/references/references.go | 3 ++- modules/references/references_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/modules/references/references.go b/modules/references/references.go index 81267f0065..7df5119393 100644 --- a/modules/references/references.go +++ b/modules/references/references.go @@ -460,7 +460,8 @@ func findAllIssueReferencesBytes(content []byte, links []string) []*rawReference } parts := strings.Split(u.EscapedPath(), "/") // /user/repo/issues/3 - if len(parts) != 5 || parts[0] != "" { + // /user/repo/pulls/7/files/... + if len(parts) < 5 || parts[0] != "" { continue } var sep string diff --git a/modules/references/references_test.go b/modules/references/references_test.go index 77f6bfbae3..bb22c0bd59 100644 --- a/modules/references/references_test.go +++ b/modules/references/references_test.go @@ -132,6 +132,30 @@ func TestFindAllIssueReferences(t *testing.T) { {203, "user4", "repo5", "203", true, XRefActionNone, nil, nil, ""}, }, }, + { + "This http://gitea.com:3000/user4/repo5/pulls/202#x yes.", + []testResult{ + {202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""}, + }, + }, + { + "This http://gitea.com:3000/user4/repo5/pulls/202/commits yes.", + []testResult{ + {202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""}, + }, + }, + { + "This http://gitea.com:3000/user4/repo5/pulls/202/files yes.", + []testResult{ + {202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""}, + }, + }, + { + "This http://gitea.com:3000/user4/repo5/pulls/202/files#diff- yes.", + []testResult{ + {202, "user4", "repo5", "202", true, XRefActionNone, nil, nil, ""}, + }, + }, { "This http://GiTeA.COM:3000/user4/repo6/pulls/205 yes.", []testResult{ From 022eeee6575704add670221350d8454cbb5ebfd2 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 28 May 2025 18:22:10 +0200 Subject: [PATCH 45/94] fix: ignore expired artifacts for quota calculation (#7976) - Expired artifacts are kept in the database but the artifact has been deleted from the storage. Ignore them for the quota calculation. - Added unit test. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7976 Reviewed-by: Earl Warren Reviewed-by: Otto Co-authored-by: Gusted Co-committed-by: Gusted --- .../TestGetUsedForUser/action_artifact.yaml | 17 ++++++++++++++ models/quota/main_test.go | 19 +++++++++++++++ models/quota/used.go | 3 ++- models/quota/used_test.go | 23 +++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 models/fixtures/TestGetUsedForUser/action_artifact.yaml create mode 100644 models/quota/main_test.go create mode 100644 models/quota/used_test.go diff --git a/models/fixtures/TestGetUsedForUser/action_artifact.yaml b/models/fixtures/TestGetUsedForUser/action_artifact.yaml new file mode 100644 index 0000000000..db5392126d --- /dev/null +++ b/models/fixtures/TestGetUsedForUser/action_artifact.yaml @@ -0,0 +1,17 @@ +- + id: 1001 + run_id: 792 + runner_id: 1 + repo_id: 4 + owner_id: 1 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 + storage_path: "27/5/1730330775594233150.chunk" + file_size: 693147180559 + file_compressed_size: 693147180559 + content_encoding: "application/zip" + artifact_path: "big-file.zip" + artifact_name: "big-file" + status: 4 + created_unix: 1730330775 + updated_unix: 1730330775 + expired_unix: 1738106775 diff --git a/models/quota/main_test.go b/models/quota/main_test.go new file mode 100644 index 0000000000..ec0a0e0013 --- /dev/null +++ b/models/quota/main_test.go @@ -0,0 +1,19 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package quota + +import ( + "testing" + + "forgejo.org/models/unittest" + + _ "forgejo.org/models" + _ "forgejo.org/models/actions" + _ "forgejo.org/models/activities" + _ "forgejo.org/models/forgefed" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} diff --git a/models/quota/used.go b/models/quota/used.go index 4a1bc84c0a..22815165f6 100644 --- a/models/quota/used.go +++ b/models/quota/used.go @@ -131,7 +131,8 @@ func createQueryFor(ctx context.Context, userID int64, q string) db.Engine { case "artifacts": session = session. Table("action_artifact"). - Join("INNER", "`repository`", "`action_artifact`.repo_id = `repository`.id") + Join("INNER", "`repository`", "`action_artifact`.repo_id = `repository`.id"). + Where("`action_artifact`.status != ?", action_model.ArtifactStatusExpired) case "packages": session = session. Table("package_version"). diff --git a/models/quota/used_test.go b/models/quota/used_test.go new file mode 100644 index 0000000000..82cc5b9bcc --- /dev/null +++ b/models/quota/used_test.go @@ -0,0 +1,23 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package quota + +import ( + "testing" + + "forgejo.org/models/unittest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGetUsedForUser(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/TestGetUsedForUser/")() + require.NoError(t, unittest.PrepareTestDatabase()) + + used, err := GetUsedForUser(t.Context(), 5) + require.NoError(t, err) + + assert.EqualValues(t, 4096, used.Size.Assets.Artifacts) +} From de3a3d728d525a66568d0c0314462339442a7979 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 29 May 2025 10:00:12 +0200 Subject: [PATCH 46/94] feat: update ambigious characters (#7988) - Update the ambigious characters list with the values in https://github.com/hediet/vscode-unicode-data/blob/f3d1aeb2cf538f5f44d89d2ae961df62e03ea0a1/out/ambiguous.json Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7988 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/charset/ambiguous/ambiguous.json | 2 +- modules/charset/ambiguous_gen.go | 247 ++++++++++++----------- 2 files changed, 125 insertions(+), 124 deletions(-) diff --git a/modules/charset/ambiguous/ambiguous.json b/modules/charset/ambiguous/ambiguous.json index d0f69f6ae2..82bbc7da1c 100644 --- a/modules/charset/ambiguous/ambiguous.json +++ b/modules/charset/ambiguous/ambiguous.json @@ -1 +1 @@ -"{\"_common\":[8232,32,8233,32,5760,32,8192,32,8193,32,8194,32,8195,32,8196,32,8197,32,8198,32,8200,32,8201,32,8202,32,8287,32,8199,32,8239,32,2042,95,65101,95,65102,95,65103,95,8208,45,8209,45,8210,45,65112,45,1748,45,8259,45,727,45,8722,45,10134,45,11450,45,1549,44,1643,44,8218,44,184,44,42233,44,894,59,2307,58,2691,58,1417,58,1795,58,1796,58,5868,58,65072,58,6147,58,6153,58,8282,58,1475,58,760,58,42889,58,8758,58,720,58,42237,58,451,33,11601,33,660,63,577,63,2429,63,5038,63,42731,63,119149,46,8228,46,1793,46,1794,46,42510,46,68176,46,1632,46,1776,46,42232,46,1373,96,65287,96,8219,96,8242,96,1370,96,1523,96,8175,96,65344,96,900,96,8189,96,8125,96,8127,96,8190,96,697,96,884,96,712,96,714,96,715,96,756,96,699,96,701,96,700,96,702,96,42892,96,1497,96,2036,96,2037,96,5194,96,5836,96,94033,96,94034,96,65339,91,10088,40,10098,40,12308,40,64830,40,65341,93,10089,41,10099,41,12309,41,64831,41,10100,123,119060,123,10101,125,65342,94,8270,42,1645,42,8727,42,66335,42,5941,47,8257,47,8725,47,8260,47,9585,47,10187,47,10744,47,119354,47,12755,47,12339,47,11462,47,20031,47,12035,47,65340,92,65128,92,8726,92,10189,92,10741,92,10745,92,119311,92,119355,92,12756,92,20022,92,12034,92,42872,38,708,94,710,94,5869,43,10133,43,66203,43,8249,60,10094,60,706,60,119350,60,5176,60,5810,60,5120,61,11840,61,12448,61,42239,61,8250,62,10095,62,707,62,119351,62,5171,62,94015,62,8275,126,732,126,8128,126,8764,126,65372,124,65293,45,120784,50,120794,50,120804,50,120814,50,120824,50,130034,50,42842,50,423,50,1000,50,42564,50,5311,50,42735,50,119302,51,120785,51,120795,51,120805,51,120815,51,120825,51,130035,51,42923,51,540,51,439,51,42858,51,11468,51,1248,51,94011,51,71882,51,120786,52,120796,52,120806,52,120816,52,120826,52,130036,52,5070,52,71855,52,120787,53,120797,53,120807,53,120817,53,120827,53,130037,53,444,53,71867,53,120788,54,120798,54,120808,54,120818,54,120828,54,130038,54,11474,54,5102,54,71893,54,119314,55,120789,55,120799,55,120809,55,120819,55,120829,55,130039,55,66770,55,71878,55,2819,56,2538,56,2666,56,125131,56,120790,56,120800,56,120810,56,120820,56,120830,56,130040,56,547,56,546,56,66330,56,2663,57,2920,57,2541,57,3437,57,120791,57,120801,57,120811,57,120821,57,120831,57,130041,57,42862,57,11466,57,71884,57,71852,57,71894,57,9082,97,65345,97,119834,97,119886,97,119938,97,119990,97,120042,97,120094,97,120146,97,120198,97,120250,97,120302,97,120354,97,120406,97,120458,97,593,97,945,97,120514,97,120572,97,120630,97,120688,97,120746,97,65313,65,119808,65,119860,65,119912,65,119964,65,120016,65,120068,65,120120,65,120172,65,120224,65,120276,65,120328,65,120380,65,120432,65,913,65,120488,65,120546,65,120604,65,120662,65,120720,65,5034,65,5573,65,42222,65,94016,65,66208,65,119835,98,119887,98,119939,98,119991,98,120043,98,120095,98,120147,98,120199,98,120251,98,120303,98,120355,98,120407,98,120459,98,388,98,5071,98,5234,98,5551,98,65314,66,8492,66,119809,66,119861,66,119913,66,120017,66,120069,66,120121,66,120173,66,120225,66,120277,66,120329,66,120381,66,120433,66,42932,66,914,66,120489,66,120547,66,120605,66,120663,66,120721,66,5108,66,5623,66,42192,66,66178,66,66209,66,66305,66,65347,99,8573,99,119836,99,119888,99,119940,99,119992,99,120044,99,120096,99,120148,99,120200,99,120252,99,120304,99,120356,99,120408,99,120460,99,7428,99,1010,99,11429,99,43951,99,66621,99,128844,67,71922,67,71913,67,65315,67,8557,67,8450,67,8493,67,119810,67,119862,67,119914,67,119966,67,120018,67,120174,67,120226,67,120278,67,120330,67,120382,67,120434,67,1017,67,11428,67,5087,67,42202,67,66210,67,66306,67,66581,67,66844,67,8574,100,8518,100,119837,100,119889,100,119941,100,119993,100,120045,100,120097,100,120149,100,120201,100,120253,100,120305,100,120357,100,120409,100,120461,100,1281,100,5095,100,5231,100,42194,100,8558,68,8517,68,119811,68,119863,68,119915,68,119967,68,120019,68,120071,68,120123,68,120175,68,120227,68,120279,68,120331,68,120383,68,120435,68,5024,68,5598,68,5610,68,42195,68,8494,101,65349,101,8495,101,8519,101,119838,101,119890,101,119942,101,120046,101,120098,101,120150,101,120202,101,120254,101,120306,101,120358,101,120410,101,120462,101,43826,101,1213,101,8959,69,65317,69,8496,69,119812,69,119864,69,119916,69,120020,69,120072,69,120124,69,120176,69,120228,69,120280,69,120332,69,120384,69,120436,69,917,69,120492,69,120550,69,120608,69,120666,69,120724,69,11577,69,5036,69,42224,69,71846,69,71854,69,66182,69,119839,102,119891,102,119943,102,119995,102,120047,102,120099,102,120151,102,120203,102,120255,102,120307,102,120359,102,120411,102,120463,102,43829,102,42905,102,383,102,7837,102,1412,102,119315,70,8497,70,119813,70,119865,70,119917,70,120021,70,120073,70,120125,70,120177,70,120229,70,120281,70,120333,70,120385,70,120437,70,42904,70,988,70,120778,70,5556,70,42205,70,71874,70,71842,70,66183,70,66213,70,66853,70,65351,103,8458,103,119840,103,119892,103,119944,103,120048,103,120100,103,120152,103,120204,103,120256,103,120308,103,120360,103,120412,103,120464,103,609,103,7555,103,397,103,1409,103,119814,71,119866,71,119918,71,119970,71,120022,71,120074,71,120126,71,120178,71,120230,71,120282,71,120334,71,120386,71,120438,71,1292,71,5056,71,5107,71,42198,71,65352,104,8462,104,119841,104,119945,104,119997,104,120049,104,120101,104,120153,104,120205,104,120257,104,120309,104,120361,104,120413,104,120465,104,1211,104,1392,104,5058,104,65320,72,8459,72,8460,72,8461,72,119815,72,119867,72,119919,72,120023,72,120179,72,120231,72,120283,72,120335,72,120387,72,120439,72,919,72,120494,72,120552,72,120610,72,120668,72,120726,72,11406,72,5051,72,5500,72,42215,72,66255,72,731,105,9075,105,65353,105,8560,105,8505,105,8520,105,119842,105,119894,105,119946,105,119998,105,120050,105,120102,105,120154,105,120206,105,120258,105,120310,105,120362,105,120414,105,120466,105,120484,105,618,105,617,105,953,105,8126,105,890,105,120522,105,120580,105,120638,105,120696,105,120754,105,1110,105,42567,105,1231,105,43893,105,5029,105,71875,105,65354,106,8521,106,119843,106,119895,106,119947,106,119999,106,120051,106,120103,106,120155,106,120207,106,120259,106,120311,106,120363,106,120415,106,120467,106,1011,106,1112,106,65322,74,119817,74,119869,74,119921,74,119973,74,120025,74,120077,74,120129,74,120181,74,120233,74,120285,74,120337,74,120389,74,120441,74,42930,74,895,74,1032,74,5035,74,5261,74,42201,74,119844,107,119896,107,119948,107,120000,107,120052,107,120104,107,120156,107,120208,107,120260,107,120312,107,120364,107,120416,107,120468,107,8490,75,65323,75,119818,75,119870,75,119922,75,119974,75,120026,75,120078,75,120130,75,120182,75,120234,75,120286,75,120338,75,120390,75,120442,75,922,75,120497,75,120555,75,120613,75,120671,75,120729,75,11412,75,5094,75,5845,75,42199,75,66840,75,1472,108,8739,73,9213,73,65512,73,1633,108,1777,73,66336,108,125127,108,120783,73,120793,73,120803,73,120813,73,120823,73,130033,73,65321,73,8544,73,8464,73,8465,73,119816,73,119868,73,119920,73,120024,73,120128,73,120180,73,120232,73,120284,73,120336,73,120388,73,120440,73,65356,108,8572,73,8467,108,119845,108,119897,108,119949,108,120001,108,120053,108,120105,73,120157,73,120209,73,120261,73,120313,73,120365,73,120417,73,120469,73,448,73,120496,73,120554,73,120612,73,120670,73,120728,73,11410,73,1030,73,1216,73,1493,108,1503,108,1575,108,126464,108,126592,108,65166,108,65165,108,1994,108,11599,73,5825,73,42226,73,93992,73,66186,124,66313,124,119338,76,8556,76,8466,76,119819,76,119871,76,119923,76,120027,76,120079,76,120131,76,120183,76,120235,76,120287,76,120339,76,120391,76,120443,76,11472,76,5086,76,5290,76,42209,76,93974,76,71843,76,71858,76,66587,76,66854,76,65325,77,8559,77,8499,77,119820,77,119872,77,119924,77,120028,77,120080,77,120132,77,120184,77,120236,77,120288,77,120340,77,120392,77,120444,77,924,77,120499,77,120557,77,120615,77,120673,77,120731,77,1018,77,11416,77,5047,77,5616,77,5846,77,42207,77,66224,77,66321,77,119847,110,119899,110,119951,110,120003,110,120055,110,120107,110,120159,110,120211,110,120263,110,120315,110,120367,110,120419,110,120471,110,1400,110,1404,110,65326,78,8469,78,119821,78,119873,78,119925,78,119977,78,120029,78,120081,78,120185,78,120237,78,120289,78,120341,78,120393,78,120445,78,925,78,120500,78,120558,78,120616,78,120674,78,120732,78,11418,78,42208,78,66835,78,3074,111,3202,111,3330,111,3458,111,2406,111,2662,111,2790,111,3046,111,3174,111,3302,111,3430,111,3664,111,3792,111,4160,111,1637,111,1781,111,65359,111,8500,111,119848,111,119900,111,119952,111,120056,111,120108,111,120160,111,120212,111,120264,111,120316,111,120368,111,120420,111,120472,111,7439,111,7441,111,43837,111,959,111,120528,111,120586,111,120644,111,120702,111,120760,111,963,111,120532,111,120590,111,120648,111,120706,111,120764,111,11423,111,4351,111,1413,111,1505,111,1607,111,126500,111,126564,111,126596,111,65259,111,65260,111,65258,111,65257,111,1726,111,64428,111,64429,111,64427,111,64426,111,1729,111,64424,111,64425,111,64423,111,64422,111,1749,111,3360,111,4125,111,66794,111,71880,111,71895,111,66604,111,1984,79,2534,79,2918,79,12295,79,70864,79,71904,79,120782,79,120792,79,120802,79,120812,79,120822,79,130032,79,65327,79,119822,79,119874,79,119926,79,119978,79,120030,79,120082,79,120134,79,120186,79,120238,79,120290,79,120342,79,120394,79,120446,79,927,79,120502,79,120560,79,120618,79,120676,79,120734,79,11422,79,1365,79,11604,79,4816,79,2848,79,66754,79,42227,79,71861,79,66194,79,66219,79,66564,79,66838,79,9076,112,65360,112,119849,112,119901,112,119953,112,120005,112,120057,112,120109,112,120161,112,120213,112,120265,112,120317,112,120369,112,120421,112,120473,112,961,112,120530,112,120544,112,120588,112,120602,112,120646,112,120660,112,120704,112,120718,112,120762,112,120776,112,11427,112,65328,80,8473,80,119823,80,119875,80,119927,80,119979,80,120031,80,120083,80,120187,80,120239,80,120291,80,120343,80,120395,80,120447,80,929,80,120504,80,120562,80,120620,80,120678,80,120736,80,11426,80,5090,80,5229,80,42193,80,66197,80,119850,113,119902,113,119954,113,120006,113,120058,113,120110,113,120162,113,120214,113,120266,113,120318,113,120370,113,120422,113,120474,113,1307,113,1379,113,1382,113,8474,81,119824,81,119876,81,119928,81,119980,81,120032,81,120084,81,120188,81,120240,81,120292,81,120344,81,120396,81,120448,81,11605,81,119851,114,119903,114,119955,114,120007,114,120059,114,120111,114,120163,114,120215,114,120267,114,120319,114,120371,114,120423,114,120475,114,43847,114,43848,114,7462,114,11397,114,43905,114,119318,82,8475,82,8476,82,8477,82,119825,82,119877,82,119929,82,120033,82,120189,82,120241,82,120293,82,120345,82,120397,82,120449,82,422,82,5025,82,5074,82,66740,82,5511,82,42211,82,94005,82,65363,115,119852,115,119904,115,119956,115,120008,115,120060,115,120112,115,120164,115,120216,115,120268,115,120320,115,120372,115,120424,115,120476,115,42801,115,445,115,1109,115,43946,115,71873,115,66632,115,65331,83,119826,83,119878,83,119930,83,119982,83,120034,83,120086,83,120138,83,120190,83,120242,83,120294,83,120346,83,120398,83,120450,83,1029,83,1359,83,5077,83,5082,83,42210,83,94010,83,66198,83,66592,83,119853,116,119905,116,119957,116,120009,116,120061,116,120113,116,120165,116,120217,116,120269,116,120321,116,120373,116,120425,116,120477,116,8868,84,10201,84,128872,84,65332,84,119827,84,119879,84,119931,84,119983,84,120035,84,120087,84,120139,84,120191,84,120243,84,120295,84,120347,84,120399,84,120451,84,932,84,120507,84,120565,84,120623,84,120681,84,120739,84,11430,84,5026,84,42196,84,93962,84,71868,84,66199,84,66225,84,66325,84,119854,117,119906,117,119958,117,120010,117,120062,117,120114,117,120166,117,120218,117,120270,117,120322,117,120374,117,120426,117,120478,117,42911,117,7452,117,43854,117,43858,117,651,117,965,117,120534,117,120592,117,120650,117,120708,117,120766,117,1405,117,66806,117,71896,117,8746,85,8899,85,119828,85,119880,85,119932,85,119984,85,120036,85,120088,85,120140,85,120192,85,120244,85,120296,85,120348,85,120400,85,120452,85,1357,85,4608,85,66766,85,5196,85,42228,85,94018,85,71864,85,8744,118,8897,118,65366,118,8564,118,119855,118,119907,118,119959,118,120011,118,120063,118,120115,118,120167,118,120219,118,120271,118,120323,118,120375,118,120427,118,120479,118,7456,118,957,118,120526,118,120584,118,120642,118,120700,118,120758,118,1141,118,1496,118,71430,118,43945,118,71872,118,119309,86,1639,86,1783,86,8548,86,119829,86,119881,86,119933,86,119985,86,120037,86,120089,86,120141,86,120193,86,120245,86,120297,86,120349,86,120401,86,120453,86,1140,86,11576,86,5081,86,5167,86,42719,86,42214,86,93960,86,71840,86,66845,86,623,119,119856,119,119908,119,119960,119,120012,119,120064,119,120116,119,120168,119,120220,119,120272,119,120324,119,120376,119,120428,119,120480,119,7457,119,1121,119,1309,119,1377,119,71434,119,71438,119,71439,119,43907,119,71919,87,71910,87,119830,87,119882,87,119934,87,119986,87,120038,87,120090,87,120142,87,120194,87,120246,87,120298,87,120350,87,120402,87,120454,87,1308,87,5043,87,5076,87,42218,87,5742,120,10539,120,10540,120,10799,120,65368,120,8569,120,119857,120,119909,120,119961,120,120013,120,120065,120,120117,120,120169,120,120221,120,120273,120,120325,120,120377,120,120429,120,120481,120,5441,120,5501,120,5741,88,9587,88,66338,88,71916,88,65336,88,8553,88,119831,88,119883,88,119935,88,119987,88,120039,88,120091,88,120143,88,120195,88,120247,88,120299,88,120351,88,120403,88,120455,88,42931,88,935,88,120510,88,120568,88,120626,88,120684,88,120742,88,11436,88,11613,88,5815,88,42219,88,66192,88,66228,88,66327,88,66855,88,611,121,7564,121,65369,121,119858,121,119910,121,119962,121,120014,121,120066,121,120118,121,120170,121,120222,121,120274,121,120326,121,120378,121,120430,121,120482,121,655,121,7935,121,43866,121,947,121,8509,121,120516,121,120574,121,120632,121,120690,121,120748,121,1199,121,4327,121,71900,121,65337,89,119832,89,119884,89,119936,89,119988,89,120040,89,120092,89,120144,89,120196,89,120248,89,120300,89,120352,89,120404,89,120456,89,933,89,978,89,120508,89,120566,89,120624,89,120682,89,120740,89,11432,89,1198,89,5033,89,5053,89,42220,89,94019,89,71844,89,66226,89,119859,122,119911,122,119963,122,120015,122,120067,122,120119,122,120171,122,120223,122,120275,122,120327,122,120379,122,120431,122,120483,122,7458,122,43923,122,71876,122,66293,90,71909,90,65338,90,8484,90,8488,90,119833,90,119885,90,119937,90,119989,90,120041,90,120197,90,120249,90,120301,90,120353,90,120405,90,120457,90,918,90,120493,90,120551,90,120609,90,120667,90,120725,90,5059,90,42204,90,71849,90,65282,34,65284,36,65285,37,65286,38,65290,42,65291,43,65294,46,65295,47,65296,48,65297,49,65298,50,65299,51,65300,52,65301,53,65302,54,65303,55,65304,56,65305,57,65308,60,65309,61,65310,62,65312,64,65316,68,65318,70,65319,71,65324,76,65329,81,65330,82,65333,85,65334,86,65335,87,65343,95,65346,98,65348,100,65350,102,65355,107,65357,109,65358,110,65361,113,65362,114,65364,116,65365,117,65367,119,65370,122,65371,123,65373,125],\"_default\":[160,32,8211,45,65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"cs\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"de\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"es\":[8211,45,65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"fr\":[65374,126,65306,58,65281,33,8216,96,8245,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"it\":[160,32,8211,45,65374,126,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"ja\":[8211,45,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65292,44,65307,59],\"ko\":[8211,45,65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"pl\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"pt-BR\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"qps-ploc\":[160,32,8211,45,65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"ru\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,305,105,921,73,1009,112,215,120,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"tr\":[160,32,8211,45,65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"zh-hans\":[65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41],\"zh-hant\":[8211,45,65374,126,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65307,59]}" \ No newline at end of file +"{\"_common\":[8232,32,8233,32,5760,32,8192,32,8193,32,8194,32,8195,32,8196,32,8197,32,8198,32,8200,32,8201,32,8202,32,8287,32,8199,32,8239,32,2042,95,65101,95,65102,95,65103,95,8208,45,8209,45,8210,45,65112,45,1748,45,8259,45,727,45,8722,45,10134,45,11450,45,1549,44,1643,44,184,44,42233,44,894,59,2307,58,2691,58,1417,58,1795,58,1796,58,5868,58,65072,58,6147,58,6153,58,8282,58,1475,58,760,58,42889,58,8758,58,720,58,42237,58,451,33,11601,33,660,63,577,63,2429,63,5038,63,42731,63,119149,46,8228,46,1793,46,1794,46,42510,46,68176,46,1632,46,1776,46,42232,46,1373,96,65287,96,8219,96,1523,96,8242,96,1370,96,8175,96,65344,96,900,96,8189,96,8125,96,8127,96,8190,96,697,96,884,96,712,96,714,96,715,96,756,96,699,96,701,96,700,96,702,96,42892,96,1497,96,2036,96,2037,96,5194,96,5836,96,94033,96,94034,96,65339,91,10088,40,10098,40,12308,40,64830,40,65341,93,10089,41,10099,41,12309,41,64831,41,10100,123,119060,123,10101,125,65342,94,8270,42,1645,42,8727,42,66335,42,5941,47,8257,47,8725,47,8260,47,9585,47,10187,47,10744,47,119354,47,12755,47,12339,47,11462,47,20031,47,12035,47,65340,92,65128,92,8726,92,10189,92,10741,92,10745,92,119311,92,119355,92,12756,92,20022,92,12034,92,42872,38,708,94,710,94,5869,43,10133,43,66203,43,8249,60,10094,60,706,60,119350,60,5176,60,5810,60,5120,61,11840,61,12448,61,42239,61,8250,62,10095,62,707,62,119351,62,5171,62,94015,62,8275,126,732,126,8128,126,8764,126,65372,124,65293,45,118002,50,120784,50,120794,50,120804,50,120814,50,120824,50,130034,50,42842,50,423,50,1000,50,42564,50,5311,50,42735,50,119302,51,118003,51,120785,51,120795,51,120805,51,120815,51,120825,51,130035,51,42923,51,540,51,439,51,42858,51,11468,51,1248,51,94011,51,71882,51,118004,52,120786,52,120796,52,120806,52,120816,52,120826,52,130036,52,5070,52,71855,52,118005,53,120787,53,120797,53,120807,53,120817,53,120827,53,130037,53,444,53,71867,53,118006,54,120788,54,120798,54,120808,54,120818,54,120828,54,130038,54,11474,54,5102,54,71893,54,119314,55,118007,55,120789,55,120799,55,120809,55,120819,55,120829,55,130039,55,66770,55,71878,55,2819,56,2538,56,2666,56,125131,56,118008,56,120790,56,120800,56,120810,56,120820,56,120830,56,130040,56,547,56,546,56,66330,56,2663,57,2920,57,2541,57,3437,57,118009,57,120791,57,120801,57,120811,57,120821,57,120831,57,130041,57,42862,57,11466,57,71884,57,71852,57,71894,57,9082,97,65345,97,119834,97,119886,97,119938,97,119990,97,120042,97,120094,97,120146,97,120198,97,120250,97,120302,97,120354,97,120406,97,120458,97,593,97,945,97,120514,97,120572,97,120630,97,120688,97,120746,97,65313,65,117974,65,119808,65,119860,65,119912,65,119964,65,120016,65,120068,65,120120,65,120172,65,120224,65,120276,65,120328,65,120380,65,120432,65,913,65,120488,65,120546,65,120604,65,120662,65,120720,65,5034,65,5573,65,42222,65,94016,65,66208,65,119835,98,119887,98,119939,98,119991,98,120043,98,120095,98,120147,98,120199,98,120251,98,120303,98,120355,98,120407,98,120459,98,388,98,5071,98,5234,98,5551,98,65314,66,8492,66,117975,66,119809,66,119861,66,119913,66,120017,66,120069,66,120121,66,120173,66,120225,66,120277,66,120329,66,120381,66,120433,66,42932,66,914,66,120489,66,120547,66,120605,66,120663,66,120721,66,5108,66,5623,66,42192,66,66178,66,66209,66,66305,66,65347,99,8573,99,119836,99,119888,99,119940,99,119992,99,120044,99,120096,99,120148,99,120200,99,120252,99,120304,99,120356,99,120408,99,120460,99,7428,99,1010,99,11429,99,43951,99,66621,99,128844,67,71913,67,71922,67,65315,67,8557,67,8450,67,8493,67,117976,67,119810,67,119862,67,119914,67,119966,67,120018,67,120174,67,120226,67,120278,67,120330,67,120382,67,120434,67,1017,67,11428,67,5087,67,42202,67,66210,67,66306,67,66581,67,66844,67,8574,100,8518,100,119837,100,119889,100,119941,100,119993,100,120045,100,120097,100,120149,100,120201,100,120253,100,120305,100,120357,100,120409,100,120461,100,1281,100,5095,100,5231,100,42194,100,8558,68,8517,68,117977,68,119811,68,119863,68,119915,68,119967,68,120019,68,120071,68,120123,68,120175,68,120227,68,120279,68,120331,68,120383,68,120435,68,5024,68,5598,68,5610,68,42195,68,8494,101,65349,101,8495,101,8519,101,119838,101,119890,101,119942,101,120046,101,120098,101,120150,101,120202,101,120254,101,120306,101,120358,101,120410,101,120462,101,43826,101,1213,101,8959,69,65317,69,8496,69,117978,69,119812,69,119864,69,119916,69,120020,69,120072,69,120124,69,120176,69,120228,69,120280,69,120332,69,120384,69,120436,69,917,69,120492,69,120550,69,120608,69,120666,69,120724,69,11577,69,5036,69,42224,69,71846,69,71854,69,66182,69,119839,102,119891,102,119943,102,119995,102,120047,102,120099,102,120151,102,120203,102,120255,102,120307,102,120359,102,120411,102,120463,102,43829,102,42905,102,383,102,7837,102,1412,102,119315,70,8497,70,117979,70,119813,70,119865,70,119917,70,120021,70,120073,70,120125,70,120177,70,120229,70,120281,70,120333,70,120385,70,120437,70,42904,70,988,70,120778,70,5556,70,42205,70,71874,70,71842,70,66183,70,66213,70,66853,70,65351,103,8458,103,119840,103,119892,103,119944,103,120048,103,120100,103,120152,103,120204,103,120256,103,120308,103,120360,103,120412,103,120464,103,609,103,7555,103,397,103,1409,103,117980,71,119814,71,119866,71,119918,71,119970,71,120022,71,120074,71,120126,71,120178,71,120230,71,120282,71,120334,71,120386,71,120438,71,1292,71,5056,71,5107,71,42198,71,65352,104,8462,104,119841,104,119945,104,119997,104,120049,104,120101,104,120153,104,120205,104,120257,104,120309,104,120361,104,120413,104,120465,104,1211,104,1392,104,5058,104,65320,72,8459,72,8460,72,8461,72,117981,72,119815,72,119867,72,119919,72,120023,72,120179,72,120231,72,120283,72,120335,72,120387,72,120439,72,919,72,120494,72,120552,72,120610,72,120668,72,120726,72,11406,72,5051,72,5500,72,42215,72,66255,72,731,105,9075,105,65353,105,8560,105,8505,105,8520,105,119842,105,119894,105,119946,105,119998,105,120050,105,120102,105,120154,105,120206,105,120258,105,120310,105,120362,105,120414,105,120466,105,120484,105,618,105,617,105,953,105,8126,105,890,105,120522,105,120580,105,120638,105,120696,105,120754,105,1110,105,42567,105,1231,105,43893,105,5029,105,71875,105,65354,106,8521,106,119843,106,119895,106,119947,106,119999,106,120051,106,120103,106,120155,106,120207,106,120259,106,120311,106,120363,106,120415,106,120467,106,1011,106,1112,106,65322,74,117983,74,119817,74,119869,74,119921,74,119973,74,120025,74,120077,74,120129,74,120181,74,120233,74,120285,74,120337,74,120389,74,120441,74,42930,74,895,74,1032,74,5035,74,5261,74,42201,74,119844,107,119896,107,119948,107,120000,107,120052,107,120104,107,120156,107,120208,107,120260,107,120312,107,120364,107,120416,107,120468,107,8490,75,65323,75,117984,75,119818,75,119870,75,119922,75,119974,75,120026,75,120078,75,120130,75,120182,75,120234,75,120286,75,120338,75,120390,75,120442,75,922,75,120497,75,120555,75,120613,75,120671,75,120729,75,11412,75,5094,75,5845,75,42199,75,66840,75,1472,108,8739,73,9213,73,65512,73,1633,108,1777,73,66336,108,125127,108,118001,108,120783,73,120793,73,120803,73,120813,73,120823,73,130033,73,65321,73,8544,73,8464,73,8465,73,117982,108,119816,73,119868,73,119920,73,120024,73,120128,73,120180,73,120232,73,120284,73,120336,73,120388,73,120440,73,65356,108,8572,73,8467,108,119845,108,119897,108,119949,108,120001,108,120053,108,120105,73,120157,73,120209,73,120261,73,120313,73,120365,73,120417,73,120469,73,448,73,120496,73,120554,73,120612,73,120670,73,120728,73,11410,73,1030,73,1216,73,1493,108,1503,108,1575,108,126464,108,126592,108,65166,108,65165,108,1994,108,11599,73,5825,73,42226,73,93992,73,66186,124,66313,124,119338,76,8556,76,8466,76,117985,76,119819,76,119871,76,119923,76,120027,76,120079,76,120131,76,120183,76,120235,76,120287,76,120339,76,120391,76,120443,76,11472,76,5086,76,5290,76,42209,76,93974,76,71843,76,71858,76,66587,76,66854,76,65325,77,8559,77,8499,77,117986,77,119820,77,119872,77,119924,77,120028,77,120080,77,120132,77,120184,77,120236,77,120288,77,120340,77,120392,77,120444,77,924,77,120499,77,120557,77,120615,77,120673,77,120731,77,1018,77,11416,77,5047,77,5616,77,5846,77,42207,77,66224,77,66321,77,119847,110,119899,110,119951,110,120003,110,120055,110,120107,110,120159,110,120211,110,120263,110,120315,110,120367,110,120419,110,120471,110,1400,110,1404,110,65326,78,8469,78,117987,78,119821,78,119873,78,119925,78,119977,78,120029,78,120081,78,120185,78,120237,78,120289,78,120341,78,120393,78,120445,78,925,78,120500,78,120558,78,120616,78,120674,78,120732,78,11418,78,42208,78,66835,78,3074,111,3202,111,3330,111,3458,111,2406,111,2662,111,2790,111,3046,111,3174,111,3302,111,3430,111,3664,111,3792,111,4160,111,1637,111,1781,111,65359,111,8500,111,119848,111,119900,111,119952,111,120056,111,120108,111,120160,111,120212,111,120264,111,120316,111,120368,111,120420,111,120472,111,7439,111,7441,111,43837,111,959,111,120528,111,120586,111,120644,111,120702,111,120760,111,963,111,120532,111,120590,111,120648,111,120706,111,120764,111,11423,111,4351,111,1413,111,1505,111,1607,111,126500,111,126564,111,126596,111,65259,111,65260,111,65258,111,65257,111,1726,111,64428,111,64429,111,64427,111,64426,111,1729,111,64424,111,64425,111,64423,111,64422,111,1749,111,3360,111,4125,111,66794,111,71880,111,71895,111,66604,111,1984,79,2534,79,2918,79,12295,79,70864,79,71904,79,118000,79,120782,79,120792,79,120802,79,120812,79,120822,79,130032,79,65327,79,117988,79,119822,79,119874,79,119926,79,119978,79,120030,79,120082,79,120134,79,120186,79,120238,79,120290,79,120342,79,120394,79,120446,79,927,79,120502,79,120560,79,120618,79,120676,79,120734,79,11422,79,1365,79,11604,79,4816,79,2848,79,66754,79,42227,79,71861,79,66194,79,66219,79,66564,79,66838,79,9076,112,65360,112,119849,112,119901,112,119953,112,120005,112,120057,112,120109,112,120161,112,120213,112,120265,112,120317,112,120369,112,120421,112,120473,112,961,112,120530,112,120544,112,120588,112,120602,112,120646,112,120660,112,120704,112,120718,112,120762,112,120776,112,11427,112,65328,80,8473,80,117989,80,119823,80,119875,80,119927,80,119979,80,120031,80,120083,80,120187,80,120239,80,120291,80,120343,80,120395,80,120447,80,929,80,120504,80,120562,80,120620,80,120678,80,120736,80,11426,80,5090,80,5229,80,42193,80,66197,80,119850,113,119902,113,119954,113,120006,113,120058,113,120110,113,120162,113,120214,113,120266,113,120318,113,120370,113,120422,113,120474,113,1307,113,1379,113,1382,113,8474,81,117990,81,119824,81,119876,81,119928,81,119980,81,120032,81,120084,81,120188,81,120240,81,120292,81,120344,81,120396,81,120448,81,11605,81,119851,114,119903,114,119955,114,120007,114,120059,114,120111,114,120163,114,120215,114,120267,114,120319,114,120371,114,120423,114,120475,114,43847,114,43848,114,7462,114,11397,114,43905,114,119318,82,8475,82,8476,82,8477,82,117991,82,119825,82,119877,82,119929,82,120033,82,120189,82,120241,82,120293,82,120345,82,120397,82,120449,82,422,82,5025,82,5074,82,66740,82,5511,82,42211,82,94005,82,65363,115,119852,115,119904,115,119956,115,120008,115,120060,115,120112,115,120164,115,120216,115,120268,115,120320,115,120372,115,120424,115,120476,115,42801,115,445,115,1109,115,43946,115,71873,115,66632,115,65331,83,117992,83,119826,83,119878,83,119930,83,119982,83,120034,83,120086,83,120138,83,120190,83,120242,83,120294,83,120346,83,120398,83,120450,83,1029,83,1359,83,5077,83,5082,83,42210,83,94010,83,66198,83,66592,83,119853,116,119905,116,119957,116,120009,116,120061,116,120113,116,120165,116,120217,116,120269,116,120321,116,120373,116,120425,116,120477,116,8868,84,10201,84,128872,84,65332,84,117993,84,119827,84,119879,84,119931,84,119983,84,120035,84,120087,84,120139,84,120191,84,120243,84,120295,84,120347,84,120399,84,120451,84,932,84,120507,84,120565,84,120623,84,120681,84,120739,84,11430,84,5026,84,42196,84,93962,84,71868,84,66199,84,66225,84,66325,84,119854,117,119906,117,119958,117,120010,117,120062,117,120114,117,120166,117,120218,117,120270,117,120322,117,120374,117,120426,117,120478,117,42911,117,7452,117,43854,117,43858,117,651,117,965,117,120534,117,120592,117,120650,117,120708,117,120766,117,1405,117,66806,117,71896,117,8746,85,8899,85,117994,85,119828,85,119880,85,119932,85,119984,85,120036,85,120088,85,120140,85,120192,85,120244,85,120296,85,120348,85,120400,85,120452,85,1357,85,4608,85,66766,85,5196,85,42228,85,94018,85,71864,85,8744,118,8897,118,65366,118,8564,118,119855,118,119907,118,119959,118,120011,118,120063,118,120115,118,120167,118,120219,118,120271,118,120323,118,120375,118,120427,118,120479,118,7456,118,957,118,120526,118,120584,118,120642,118,120700,118,120758,118,1141,118,1496,118,71430,118,43945,118,71872,118,119309,86,1639,86,1783,86,8548,86,117995,86,119829,86,119881,86,119933,86,119985,86,120037,86,120089,86,120141,86,120193,86,120245,86,120297,86,120349,86,120401,86,120453,86,1140,86,11576,86,5081,86,5167,86,42719,86,42214,86,93960,86,71840,86,66845,86,623,119,119856,119,119908,119,119960,119,120012,119,120064,119,120116,119,120168,119,120220,119,120272,119,120324,119,120376,119,120428,119,120480,119,7457,119,1121,119,1309,119,1377,119,71434,119,71438,119,71439,119,43907,119,71910,87,71919,87,117996,87,119830,87,119882,87,119934,87,119986,87,120038,87,120090,87,120142,87,120194,87,120246,87,120298,87,120350,87,120402,87,120454,87,1308,87,5043,87,5076,87,42218,87,5742,120,10539,120,10540,120,10799,120,65368,120,8569,120,119857,120,119909,120,119961,120,120013,120,120065,120,120117,120,120169,120,120221,120,120273,120,120325,120,120377,120,120429,120,120481,120,5441,120,5501,120,5741,88,9587,88,66338,88,71916,88,65336,88,8553,88,117997,88,119831,88,119883,88,119935,88,119987,88,120039,88,120091,88,120143,88,120195,88,120247,88,120299,88,120351,88,120403,88,120455,88,42931,88,935,88,120510,88,120568,88,120626,88,120684,88,120742,88,11436,88,11613,88,5815,88,42219,88,66192,88,66228,88,66327,88,66855,88,611,121,7564,121,65369,121,119858,121,119910,121,119962,121,120014,121,120066,121,120118,121,120170,121,120222,121,120274,121,120326,121,120378,121,120430,121,120482,121,655,121,7935,121,43866,121,947,121,8509,121,120516,121,120574,121,120632,121,120690,121,120748,121,1199,121,4327,121,71900,121,65337,89,117998,89,119832,89,119884,89,119936,89,119988,89,120040,89,120092,89,120144,89,120196,89,120248,89,120300,89,120352,89,120404,89,120456,89,933,89,978,89,120508,89,120566,89,120624,89,120682,89,120740,89,11432,89,1198,89,5033,89,5053,89,42220,89,94019,89,71844,89,66226,89,119859,122,119911,122,119963,122,120015,122,120067,122,120119,122,120171,122,120223,122,120275,122,120327,122,120379,122,120431,122,120483,122,7458,122,43923,122,71876,122,71909,90,66293,90,65338,90,8484,90,8488,90,117999,90,119833,90,119885,90,119937,90,119989,90,120041,90,120197,90,120249,90,120301,90,120353,90,120405,90,120457,90,918,90,120493,90,120551,90,120609,90,120667,90,120725,90,5059,90,42204,90,71849,90,65282,34,65283,35,65284,36,65285,37,65286,38,65290,42,65291,43,65294,46,65295,47,65296,48,65298,50,65299,51,65300,52,65301,53,65302,54,65303,55,65304,56,65305,57,65308,60,65309,61,65310,62,65312,64,65316,68,65318,70,65319,71,65324,76,65329,81,65330,82,65333,85,65334,86,65335,87,65343,95,65346,98,65348,100,65350,102,65355,107,65357,109,65358,110,65361,113,65362,114,65364,116,65365,117,65367,119,65370,122,65371,123,65373,125,119846,109],\"_default\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"cs\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"de\":[65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"es\":[8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"fr\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"it\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"ja\":[8211,45,8218,44,65281,33,8216,96,8245,96,180,96,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65292,44,65297,49,65307,59],\"ko\":[8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"pl\":[65374,126,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"pt-BR\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"qps-ploc\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"ru\":[65374,126,8218,44,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,305,105,921,73,1009,112,215,120,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"tr\":[160,32,8211,45,65374,126,8218,44,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41,65292,44,65297,49,65307,59,65311,63],\"zh-hans\":[160,32,65374,126,8218,44,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65297,49],\"zh-hant\":[8211,45,65374,126,8218,44,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89]}" \ No newline at end of file diff --git a/modules/charset/ambiguous_gen.go b/modules/charset/ambiguous_gen.go index c88ffd5aa5..b0fa6099a7 100644 --- a/modules/charset/ambiguous_gen.go +++ b/modules/charset/ambiguous_gen.go @@ -19,8 +19,8 @@ type AmbiguousTable struct { // AmbiguousCharacters provides a map by locale name to the confusable characters in that locale var AmbiguousCharacters = map[string]*AmbiguousTable{ "_common": { - Confusable: []rune{184, 383, 388, 397, 422, 423, 439, 444, 445, 448, 451, 540, 546, 547, 577, 593, 609, 611, 617, 618, 623, 651, 655, 660, 697, 699, 700, 701, 702, 706, 707, 708, 710, 712, 714, 715, 720, 727, 731, 732, 756, 760, 884, 890, 894, 895, 900, 913, 914, 917, 918, 919, 922, 924, 925, 927, 929, 932, 933, 935, 945, 947, 953, 957, 959, 961, 963, 965, 978, 988, 1000, 1010, 1011, 1017, 1018, 1029, 1030, 1032, 1109, 1110, 1112, 1121, 1140, 1141, 1198, 1199, 1211, 1213, 1216, 1231, 1248, 1281, 1292, 1307, 1308, 1309, 1357, 1359, 1365, 1370, 1373, 1377, 1379, 1382, 1392, 1400, 1404, 1405, 1409, 1412, 1413, 1417, 1472, 1475, 1493, 1496, 1497, 1503, 1505, 1523, 1549, 1575, 1607, 1632, 1633, 1637, 1639, 1643, 1645, 1726, 1729, 1748, 1749, 1776, 1777, 1781, 1783, 1793, 1794, 1795, 1796, 1984, 1994, 2036, 2037, 2042, 2307, 2406, 2429, 2534, 2538, 2541, 2662, 2663, 2666, 2691, 2790, 2819, 2848, 2918, 2920, 3046, 3074, 3174, 3202, 3302, 3330, 3360, 3430, 3437, 3458, 3664, 3792, 4125, 4160, 4327, 4351, 4608, 4816, 5024, 5025, 5026, 5029, 5033, 5034, 5035, 5036, 5038, 5043, 5047, 5051, 5053, 5056, 5058, 5059, 5070, 5071, 5074, 5076, 5077, 5081, 5082, 5086, 5087, 5090, 5094, 5095, 5102, 5107, 5108, 5120, 5167, 5171, 5176, 5194, 5196, 5229, 5231, 5234, 5261, 5290, 5311, 5441, 5500, 5501, 5511, 5551, 5556, 5573, 5598, 5610, 5616, 5623, 5741, 5742, 5760, 5810, 5815, 5825, 5836, 5845, 5846, 5868, 5869, 5941, 6147, 6153, 7428, 7439, 7441, 7452, 7456, 7457, 7458, 7462, 7555, 7564, 7837, 7935, 8125, 8126, 8127, 8128, 8175, 8189, 8190, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8208, 8209, 8210, 8218, 8219, 8228, 8232, 8233, 8239, 8242, 8249, 8250, 8257, 8259, 8260, 8270, 8275, 8282, 8287, 8450, 8458, 8459, 8460, 8461, 8462, 8464, 8465, 8466, 8467, 8469, 8473, 8474, 8475, 8476, 8477, 8484, 8488, 8490, 8492, 8493, 8494, 8495, 8496, 8497, 8499, 8500, 8505, 8509, 8517, 8518, 8519, 8520, 8521, 8544, 8548, 8553, 8556, 8557, 8558, 8559, 8560, 8564, 8569, 8572, 8573, 8574, 8722, 8725, 8726, 8727, 8739, 8744, 8746, 8758, 8764, 8868, 8897, 8899, 8959, 9075, 9076, 9082, 9213, 9585, 9587, 10088, 10089, 10094, 10095, 10098, 10099, 10100, 10101, 10133, 10134, 10187, 10189, 10201, 10539, 10540, 10741, 10744, 10745, 10799, 11397, 11406, 11410, 11412, 11416, 11418, 11422, 11423, 11426, 11427, 11428, 11429, 11430, 11432, 11436, 11450, 11462, 11466, 11468, 11472, 11474, 11576, 11577, 11599, 11601, 11604, 11605, 11613, 11840, 12034, 12035, 12295, 12308, 12309, 12339, 12448, 12755, 12756, 20022, 20031, 42192, 42193, 42194, 42195, 42196, 42198, 42199, 42201, 42202, 42204, 42205, 42207, 42208, 42209, 42210, 42211, 42214, 42215, 42218, 42219, 42220, 42222, 42224, 42226, 42227, 42228, 42232, 42233, 42237, 42239, 42510, 42564, 42567, 42719, 42731, 42735, 42801, 42842, 42858, 42862, 42872, 42889, 42892, 42904, 42905, 42911, 42923, 42930, 42931, 42932, 43826, 43829, 43837, 43847, 43848, 43854, 43858, 43866, 43893, 43905, 43907, 43923, 43945, 43946, 43951, 64422, 64423, 64424, 64425, 64426, 64427, 64428, 64429, 64830, 64831, 65072, 65101, 65102, 65103, 65112, 65128, 65165, 65166, 65257, 65258, 65259, 65260, 65282, 65284, 65285, 65286, 65287, 65290, 65291, 65293, 65294, 65295, 65296, 65297, 65298, 65299, 65300, 65301, 65302, 65303, 65304, 65305, 65308, 65309, 65310, 65312, 65313, 65314, 65315, 65316, 65317, 65318, 65319, 65320, 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, 65329, 65330, 65331, 65332, 65333, 65334, 65335, 65336, 65337, 65338, 65339, 65340, 65341, 65342, 65343, 65344, 65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370, 65371, 65372, 65373, 65512, 66178, 66182, 66183, 66186, 66192, 66194, 66197, 66198, 66199, 66203, 66208, 66209, 66210, 66213, 66219, 66224, 66225, 66226, 66228, 66255, 66293, 66305, 66306, 66313, 66321, 66325, 66327, 66330, 66335, 66336, 66338, 66564, 66581, 66587, 66592, 66604, 66621, 66632, 66740, 66754, 66766, 66770, 66794, 66806, 66835, 66838, 66840, 66844, 66845, 66853, 66854, 66855, 68176, 70864, 71430, 71434, 71438, 71439, 71840, 71842, 71843, 71844, 71846, 71849, 71852, 71854, 71855, 71858, 71861, 71864, 71867, 71868, 71872, 71873, 71874, 71875, 71876, 71878, 71880, 71882, 71884, 71893, 71894, 71895, 71896, 71900, 71904, 71909, 71910, 71913, 71916, 71919, 71922, 93960, 93962, 93974, 93992, 94005, 94010, 94011, 94015, 94016, 94018, 94019, 94033, 94034, 119060, 119149, 119302, 119309, 119311, 119314, 119315, 119318, 119338, 119350, 119351, 119354, 119355, 119808, 119809, 119810, 119811, 119812, 119813, 119814, 119815, 119816, 119817, 119818, 119819, 119820, 119821, 119822, 119823, 119824, 119825, 119826, 119827, 119828, 119829, 119830, 119831, 119832, 119833, 119834, 119835, 119836, 119837, 119838, 119839, 119840, 119841, 119842, 119843, 119844, 119845, 119847, 119848, 119849, 119850, 119851, 119852, 119853, 119854, 119855, 119856, 119857, 119858, 119859, 119860, 119861, 119862, 119863, 119864, 119865, 119866, 119867, 119868, 119869, 119870, 119871, 119872, 119873, 119874, 119875, 119876, 119877, 119878, 119879, 119880, 119881, 119882, 119883, 119884, 119885, 119886, 119887, 119888, 119889, 119890, 119891, 119892, 119894, 119895, 119896, 119897, 119899, 119900, 119901, 119902, 119903, 119904, 119905, 119906, 119907, 119908, 119909, 119910, 119911, 119912, 119913, 119914, 119915, 119916, 119917, 119918, 119919, 119920, 119921, 119922, 119923, 119924, 119925, 119926, 119927, 119928, 119929, 119930, 119931, 119932, 119933, 119934, 119935, 119936, 119937, 119938, 119939, 119940, 119941, 119942, 119943, 119944, 119945, 119946, 119947, 119948, 119949, 119951, 119952, 119953, 119954, 119955, 119956, 119957, 119958, 119959, 119960, 119961, 119962, 119963, 119964, 119966, 119967, 119970, 119973, 119974, 119977, 119978, 119979, 119980, 119982, 119983, 119984, 119985, 119986, 119987, 119988, 119989, 119990, 119991, 119992, 119993, 119995, 119997, 119998, 119999, 120000, 120001, 120003, 120005, 120006, 120007, 120008, 120009, 120010, 120011, 120012, 120013, 120014, 120015, 120016, 120017, 120018, 120019, 120020, 120021, 120022, 120023, 120024, 120025, 120026, 120027, 120028, 120029, 120030, 120031, 120032, 120033, 120034, 120035, 120036, 120037, 120038, 120039, 120040, 120041, 120042, 120043, 120044, 120045, 120046, 120047, 120048, 120049, 120050, 120051, 120052, 120053, 120055, 120056, 120057, 120058, 120059, 120060, 120061, 120062, 120063, 120064, 120065, 120066, 120067, 120068, 120069, 120071, 120072, 120073, 120074, 120077, 120078, 120079, 120080, 120081, 120082, 120083, 120084, 120086, 120087, 120088, 120089, 120090, 120091, 120092, 120094, 120095, 120096, 120097, 120098, 120099, 120100, 120101, 120102, 120103, 120104, 120105, 120107, 120108, 120109, 120110, 120111, 120112, 120113, 120114, 120115, 120116, 120117, 120118, 120119, 120120, 120121, 120123, 120124, 120125, 120126, 120128, 120129, 120130, 120131, 120132, 120134, 120138, 120139, 120140, 120141, 120142, 120143, 120144, 120146, 120147, 120148, 120149, 120150, 120151, 120152, 120153, 120154, 120155, 120156, 120157, 120159, 120160, 120161, 120162, 120163, 120164, 120165, 120166, 120167, 120168, 120169, 120170, 120171, 120172, 120173, 120174, 120175, 120176, 120177, 120178, 120179, 120180, 120181, 120182, 120183, 120184, 120185, 120186, 120187, 120188, 120189, 120190, 120191, 120192, 120193, 120194, 120195, 120196, 120197, 120198, 120199, 120200, 120201, 120202, 120203, 120204, 120205, 120206, 120207, 120208, 120209, 120211, 120212, 120213, 120214, 120215, 120216, 120217, 120218, 120219, 120220, 120221, 120222, 120223, 120224, 120225, 120226, 120227, 120228, 120229, 120230, 120231, 120232, 120233, 120234, 120235, 120236, 120237, 120238, 120239, 120240, 120241, 120242, 120243, 120244, 120245, 120246, 120247, 120248, 120249, 120250, 120251, 120252, 120253, 120254, 120255, 120256, 120257, 120258, 120259, 120260, 120261, 120263, 120264, 120265, 120266, 120267, 120268, 120269, 120270, 120271, 120272, 120273, 120274, 120275, 120276, 120277, 120278, 120279, 120280, 120281, 120282, 120283, 120284, 120285, 120286, 120287, 120288, 120289, 120290, 120291, 120292, 120293, 120294, 120295, 120296, 120297, 120298, 120299, 120300, 120301, 120302, 120303, 120304, 120305, 120306, 120307, 120308, 120309, 120310, 120311, 120312, 120313, 120315, 120316, 120317, 120318, 120319, 120320, 120321, 120322, 120323, 120324, 120325, 120326, 120327, 120328, 120329, 120330, 120331, 120332, 120333, 120334, 120335, 120336, 120337, 120338, 120339, 120340, 120341, 120342, 120343, 120344, 120345, 120346, 120347, 120348, 120349, 120350, 120351, 120352, 120353, 120354, 120355, 120356, 120357, 120358, 120359, 120360, 120361, 120362, 120363, 120364, 120365, 120367, 120368, 120369, 120370, 120371, 120372, 120373, 120374, 120375, 120376, 120377, 120378, 120379, 120380, 120381, 120382, 120383, 120384, 120385, 120386, 120387, 120388, 120389, 120390, 120391, 120392, 120393, 120394, 120395, 120396, 120397, 120398, 120399, 120400, 120401, 120402, 120403, 120404, 120405, 120406, 120407, 120408, 120409, 120410, 120411, 120412, 120413, 120414, 120415, 120416, 120417, 120419, 120420, 120421, 120422, 120423, 120424, 120425, 120426, 120427, 120428, 120429, 120430, 120431, 120432, 120433, 120434, 120435, 120436, 120437, 120438, 120439, 120440, 120441, 120442, 120443, 120444, 120445, 120446, 120447, 120448, 120449, 120450, 120451, 120452, 120453, 120454, 120455, 120456, 120457, 120458, 120459, 120460, 120461, 120462, 120463, 120464, 120465, 120466, 120467, 120468, 120469, 120471, 120472, 120473, 120474, 120475, 120476, 120477, 120478, 120479, 120480, 120481, 120482, 120483, 120484, 120488, 120489, 120492, 120493, 120494, 120496, 120497, 120499, 120500, 120502, 120504, 120507, 120508, 120510, 120514, 120516, 120522, 120526, 120528, 120530, 120532, 120534, 120544, 120546, 120547, 120550, 120551, 120552, 120554, 120555, 120557, 120558, 120560, 120562, 120565, 120566, 120568, 120572, 120574, 120580, 120584, 120586, 120588, 120590, 120592, 120602, 120604, 120605, 120608, 120609, 120610, 120612, 120613, 120615, 120616, 120618, 120620, 120623, 120624, 120626, 120630, 120632, 120638, 120642, 120644, 120646, 120648, 120650, 120660, 120662, 120663, 120666, 120667, 120668, 120670, 120671, 120673, 120674, 120676, 120678, 120681, 120682, 120684, 120688, 120690, 120696, 120700, 120702, 120704, 120706, 120708, 120718, 120720, 120721, 120724, 120725, 120726, 120728, 120729, 120731, 120732, 120734, 120736, 120739, 120740, 120742, 120746, 120748, 120754, 120758, 120760, 120762, 120764, 120766, 120776, 120778, 120782, 120783, 120784, 120785, 120786, 120787, 120788, 120789, 120790, 120791, 120792, 120793, 120794, 120795, 120796, 120797, 120798, 120799, 120800, 120801, 120802, 120803, 120804, 120805, 120806, 120807, 120808, 120809, 120810, 120811, 120812, 120813, 120814, 120815, 120816, 120817, 120818, 120819, 120820, 120821, 120822, 120823, 120824, 120825, 120826, 120827, 120828, 120829, 120830, 120831, 125127, 125131, 126464, 126500, 126564, 126592, 126596, 128844, 128872, 130032, 130033, 130034, 130035, 130036, 130037, 130038, 130039, 130040, 130041}, - With: []rune{44, 102, 98, 103, 82, 50, 51, 53, 115, 73, 33, 51, 56, 56, 63, 97, 103, 121, 105, 105, 119, 117, 121, 63, 96, 96, 96, 96, 96, 60, 62, 94, 94, 96, 96, 96, 58, 45, 105, 126, 96, 58, 96, 105, 59, 74, 96, 65, 66, 69, 90, 72, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 89, 70, 50, 99, 106, 67, 77, 83, 73, 74, 115, 105, 106, 119, 86, 118, 89, 121, 104, 101, 73, 105, 51, 100, 71, 113, 87, 119, 85, 83, 79, 96, 96, 119, 113, 113, 104, 110, 110, 117, 103, 102, 111, 58, 108, 58, 108, 118, 96, 108, 111, 96, 44, 108, 111, 46, 108, 111, 86, 44, 42, 111, 111, 45, 111, 46, 73, 111, 86, 46, 46, 58, 58, 79, 108, 96, 96, 95, 58, 111, 63, 79, 56, 57, 111, 57, 56, 58, 111, 56, 79, 79, 57, 111, 111, 111, 111, 111, 111, 111, 111, 57, 111, 111, 111, 111, 111, 121, 111, 85, 79, 68, 82, 84, 105, 89, 65, 74, 69, 63, 87, 77, 72, 89, 71, 104, 90, 52, 98, 82, 87, 83, 86, 83, 76, 67, 80, 75, 100, 54, 71, 66, 61, 86, 62, 60, 96, 85, 80, 100, 98, 74, 76, 50, 120, 72, 120, 82, 98, 70, 65, 68, 68, 77, 66, 88, 120, 32, 60, 88, 73, 96, 75, 77, 58, 43, 47, 58, 58, 99, 111, 111, 117, 118, 119, 122, 114, 103, 121, 102, 121, 96, 105, 96, 126, 96, 96, 96, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 44, 96, 46, 32, 32, 32, 96, 60, 62, 47, 45, 47, 42, 126, 58, 32, 67, 103, 72, 72, 72, 104, 73, 73, 76, 108, 78, 80, 81, 82, 82, 82, 90, 90, 75, 66, 67, 101, 101, 69, 70, 77, 111, 105, 121, 68, 100, 101, 105, 106, 73, 86, 88, 76, 67, 68, 77, 105, 118, 120, 73, 99, 100, 45, 47, 92, 42, 73, 118, 85, 58, 126, 84, 118, 85, 69, 105, 112, 97, 73, 47, 88, 40, 41, 60, 62, 40, 41, 123, 125, 43, 45, 47, 92, 84, 120, 120, 92, 47, 92, 120, 114, 72, 73, 75, 77, 78, 79, 111, 80, 112, 67, 99, 84, 89, 88, 45, 47, 57, 51, 76, 54, 86, 69, 73, 33, 79, 81, 88, 61, 92, 47, 79, 40, 41, 47, 61, 47, 92, 92, 47, 66, 80, 100, 68, 84, 71, 75, 74, 67, 90, 70, 77, 78, 76, 83, 82, 86, 72, 87, 88, 89, 65, 69, 73, 79, 85, 46, 44, 58, 61, 46, 50, 105, 86, 63, 50, 115, 50, 51, 57, 38, 58, 96, 70, 102, 117, 51, 74, 88, 66, 101, 102, 111, 114, 114, 117, 117, 121, 105, 114, 119, 122, 118, 115, 99, 111, 111, 111, 111, 111, 111, 111, 111, 40, 41, 58, 95, 95, 95, 45, 92, 108, 108, 111, 111, 111, 111, 34, 36, 37, 38, 96, 42, 43, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61, 62, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 73, 66, 69, 70, 124, 88, 79, 80, 83, 84, 43, 65, 66, 67, 70, 79, 77, 84, 89, 88, 72, 90, 66, 67, 124, 77, 84, 88, 56, 42, 108, 88, 79, 67, 76, 83, 111, 99, 115, 82, 79, 85, 55, 111, 117, 78, 79, 75, 67, 86, 70, 76, 88, 46, 79, 118, 119, 119, 119, 86, 70, 76, 89, 69, 90, 57, 69, 52, 76, 79, 85, 53, 84, 118, 115, 70, 105, 122, 55, 111, 51, 57, 54, 57, 111, 117, 121, 79, 90, 87, 67, 88, 87, 67, 86, 84, 76, 73, 82, 83, 51, 62, 65, 85, 89, 96, 96, 123, 46, 51, 86, 92, 55, 70, 82, 76, 60, 62, 47, 92, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 67, 68, 71, 74, 75, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 102, 104, 105, 106, 107, 108, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 68, 69, 70, 71, 74, 75, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 68, 69, 70, 71, 73, 74, 75, 76, 77, 79, 83, 84, 85, 86, 87, 88, 89, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 105, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 70, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 108, 56, 108, 111, 111, 108, 111, 67, 84, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57}, + Confusable: []rune{184, 383, 388, 397, 422, 423, 439, 444, 445, 448, 451, 540, 546, 547, 577, 593, 609, 611, 617, 618, 623, 651, 655, 660, 697, 699, 700, 701, 702, 706, 707, 708, 710, 712, 714, 715, 720, 727, 731, 732, 756, 760, 884, 890, 894, 895, 900, 913, 914, 917, 918, 919, 922, 924, 925, 927, 929, 932, 933, 935, 945, 947, 953, 957, 959, 961, 963, 965, 978, 988, 1000, 1010, 1011, 1017, 1018, 1029, 1030, 1032, 1109, 1110, 1112, 1121, 1140, 1141, 1198, 1199, 1211, 1213, 1216, 1231, 1248, 1281, 1292, 1307, 1308, 1309, 1357, 1359, 1365, 1370, 1373, 1377, 1379, 1382, 1392, 1400, 1404, 1405, 1409, 1412, 1413, 1417, 1472, 1475, 1493, 1496, 1497, 1503, 1505, 1523, 1549, 1575, 1607, 1632, 1633, 1637, 1639, 1643, 1645, 1726, 1729, 1748, 1749, 1776, 1777, 1781, 1783, 1793, 1794, 1795, 1796, 1984, 1994, 2036, 2037, 2042, 2307, 2406, 2429, 2534, 2538, 2541, 2662, 2663, 2666, 2691, 2790, 2819, 2848, 2918, 2920, 3046, 3074, 3174, 3202, 3302, 3330, 3360, 3430, 3437, 3458, 3664, 3792, 4125, 4160, 4327, 4351, 4608, 4816, 5024, 5025, 5026, 5029, 5033, 5034, 5035, 5036, 5038, 5043, 5047, 5051, 5053, 5056, 5058, 5059, 5070, 5071, 5074, 5076, 5077, 5081, 5082, 5086, 5087, 5090, 5094, 5095, 5102, 5107, 5108, 5120, 5167, 5171, 5176, 5194, 5196, 5229, 5231, 5234, 5261, 5290, 5311, 5441, 5500, 5501, 5511, 5551, 5556, 5573, 5598, 5610, 5616, 5623, 5741, 5742, 5760, 5810, 5815, 5825, 5836, 5845, 5846, 5868, 5869, 5941, 6147, 6153, 7428, 7439, 7441, 7452, 7456, 7457, 7458, 7462, 7555, 7564, 7837, 7935, 8125, 8126, 8127, 8128, 8175, 8189, 8190, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8208, 8209, 8210, 8219, 8228, 8232, 8233, 8239, 8242, 8249, 8250, 8257, 8259, 8260, 8270, 8275, 8282, 8287, 8450, 8458, 8459, 8460, 8461, 8462, 8464, 8465, 8466, 8467, 8469, 8473, 8474, 8475, 8476, 8477, 8484, 8488, 8490, 8492, 8493, 8494, 8495, 8496, 8497, 8499, 8500, 8505, 8509, 8517, 8518, 8519, 8520, 8521, 8544, 8548, 8553, 8556, 8557, 8558, 8559, 8560, 8564, 8569, 8572, 8573, 8574, 8722, 8725, 8726, 8727, 8739, 8744, 8746, 8758, 8764, 8868, 8897, 8899, 8959, 9075, 9076, 9082, 9213, 9585, 9587, 10088, 10089, 10094, 10095, 10098, 10099, 10100, 10101, 10133, 10134, 10187, 10189, 10201, 10539, 10540, 10741, 10744, 10745, 10799, 11397, 11406, 11410, 11412, 11416, 11418, 11422, 11423, 11426, 11427, 11428, 11429, 11430, 11432, 11436, 11450, 11462, 11466, 11468, 11472, 11474, 11576, 11577, 11599, 11601, 11604, 11605, 11613, 11840, 12034, 12035, 12295, 12308, 12309, 12339, 12448, 12755, 12756, 20022, 20031, 42192, 42193, 42194, 42195, 42196, 42198, 42199, 42201, 42202, 42204, 42205, 42207, 42208, 42209, 42210, 42211, 42214, 42215, 42218, 42219, 42220, 42222, 42224, 42226, 42227, 42228, 42232, 42233, 42237, 42239, 42510, 42564, 42567, 42719, 42731, 42735, 42801, 42842, 42858, 42862, 42872, 42889, 42892, 42904, 42905, 42911, 42923, 42930, 42931, 42932, 43826, 43829, 43837, 43847, 43848, 43854, 43858, 43866, 43893, 43905, 43907, 43923, 43945, 43946, 43951, 64422, 64423, 64424, 64425, 64426, 64427, 64428, 64429, 64830, 64831, 65072, 65101, 65102, 65103, 65112, 65128, 65165, 65166, 65257, 65258, 65259, 65260, 65282, 65283, 65284, 65285, 65286, 65287, 65290, 65291, 65293, 65294, 65295, 65296, 65298, 65299, 65300, 65301, 65302, 65303, 65304, 65305, 65308, 65309, 65310, 65312, 65313, 65314, 65315, 65316, 65317, 65318, 65319, 65320, 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, 65329, 65330, 65331, 65332, 65333, 65334, 65335, 65336, 65337, 65338, 65339, 65340, 65341, 65342, 65343, 65344, 65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370, 65371, 65372, 65373, 65512, 66178, 66182, 66183, 66186, 66192, 66194, 66197, 66198, 66199, 66203, 66208, 66209, 66210, 66213, 66219, 66224, 66225, 66226, 66228, 66255, 66293, 66305, 66306, 66313, 66321, 66325, 66327, 66330, 66335, 66336, 66338, 66564, 66581, 66587, 66592, 66604, 66621, 66632, 66740, 66754, 66766, 66770, 66794, 66806, 66835, 66838, 66840, 66844, 66845, 66853, 66854, 66855, 68176, 70864, 71430, 71434, 71438, 71439, 71840, 71842, 71843, 71844, 71846, 71849, 71852, 71854, 71855, 71858, 71861, 71864, 71867, 71868, 71872, 71873, 71874, 71875, 71876, 71878, 71880, 71882, 71884, 71893, 71894, 71895, 71896, 71900, 71904, 71909, 71910, 71913, 71916, 71919, 71922, 93960, 93962, 93974, 93992, 94005, 94010, 94011, 94015, 94016, 94018, 94019, 94033, 94034, 117974, 117975, 117976, 117977, 117978, 117979, 117980, 117981, 117982, 117983, 117984, 117985, 117986, 117987, 117988, 117989, 117990, 117991, 117992, 117993, 117994, 117995, 117996, 117997, 117998, 117999, 118000, 118001, 118002, 118003, 118004, 118005, 118006, 118007, 118008, 118009, 119060, 119149, 119302, 119309, 119311, 119314, 119315, 119318, 119338, 119350, 119351, 119354, 119355, 119808, 119809, 119810, 119811, 119812, 119813, 119814, 119815, 119816, 119817, 119818, 119819, 119820, 119821, 119822, 119823, 119824, 119825, 119826, 119827, 119828, 119829, 119830, 119831, 119832, 119833, 119834, 119835, 119836, 119837, 119838, 119839, 119840, 119841, 119842, 119843, 119844, 119845, 119846, 119847, 119848, 119849, 119850, 119851, 119852, 119853, 119854, 119855, 119856, 119857, 119858, 119859, 119860, 119861, 119862, 119863, 119864, 119865, 119866, 119867, 119868, 119869, 119870, 119871, 119872, 119873, 119874, 119875, 119876, 119877, 119878, 119879, 119880, 119881, 119882, 119883, 119884, 119885, 119886, 119887, 119888, 119889, 119890, 119891, 119892, 119894, 119895, 119896, 119897, 119899, 119900, 119901, 119902, 119903, 119904, 119905, 119906, 119907, 119908, 119909, 119910, 119911, 119912, 119913, 119914, 119915, 119916, 119917, 119918, 119919, 119920, 119921, 119922, 119923, 119924, 119925, 119926, 119927, 119928, 119929, 119930, 119931, 119932, 119933, 119934, 119935, 119936, 119937, 119938, 119939, 119940, 119941, 119942, 119943, 119944, 119945, 119946, 119947, 119948, 119949, 119951, 119952, 119953, 119954, 119955, 119956, 119957, 119958, 119959, 119960, 119961, 119962, 119963, 119964, 119966, 119967, 119970, 119973, 119974, 119977, 119978, 119979, 119980, 119982, 119983, 119984, 119985, 119986, 119987, 119988, 119989, 119990, 119991, 119992, 119993, 119995, 119997, 119998, 119999, 120000, 120001, 120003, 120005, 120006, 120007, 120008, 120009, 120010, 120011, 120012, 120013, 120014, 120015, 120016, 120017, 120018, 120019, 120020, 120021, 120022, 120023, 120024, 120025, 120026, 120027, 120028, 120029, 120030, 120031, 120032, 120033, 120034, 120035, 120036, 120037, 120038, 120039, 120040, 120041, 120042, 120043, 120044, 120045, 120046, 120047, 120048, 120049, 120050, 120051, 120052, 120053, 120055, 120056, 120057, 120058, 120059, 120060, 120061, 120062, 120063, 120064, 120065, 120066, 120067, 120068, 120069, 120071, 120072, 120073, 120074, 120077, 120078, 120079, 120080, 120081, 120082, 120083, 120084, 120086, 120087, 120088, 120089, 120090, 120091, 120092, 120094, 120095, 120096, 120097, 120098, 120099, 120100, 120101, 120102, 120103, 120104, 120105, 120107, 120108, 120109, 120110, 120111, 120112, 120113, 120114, 120115, 120116, 120117, 120118, 120119, 120120, 120121, 120123, 120124, 120125, 120126, 120128, 120129, 120130, 120131, 120132, 120134, 120138, 120139, 120140, 120141, 120142, 120143, 120144, 120146, 120147, 120148, 120149, 120150, 120151, 120152, 120153, 120154, 120155, 120156, 120157, 120159, 120160, 120161, 120162, 120163, 120164, 120165, 120166, 120167, 120168, 120169, 120170, 120171, 120172, 120173, 120174, 120175, 120176, 120177, 120178, 120179, 120180, 120181, 120182, 120183, 120184, 120185, 120186, 120187, 120188, 120189, 120190, 120191, 120192, 120193, 120194, 120195, 120196, 120197, 120198, 120199, 120200, 120201, 120202, 120203, 120204, 120205, 120206, 120207, 120208, 120209, 120211, 120212, 120213, 120214, 120215, 120216, 120217, 120218, 120219, 120220, 120221, 120222, 120223, 120224, 120225, 120226, 120227, 120228, 120229, 120230, 120231, 120232, 120233, 120234, 120235, 120236, 120237, 120238, 120239, 120240, 120241, 120242, 120243, 120244, 120245, 120246, 120247, 120248, 120249, 120250, 120251, 120252, 120253, 120254, 120255, 120256, 120257, 120258, 120259, 120260, 120261, 120263, 120264, 120265, 120266, 120267, 120268, 120269, 120270, 120271, 120272, 120273, 120274, 120275, 120276, 120277, 120278, 120279, 120280, 120281, 120282, 120283, 120284, 120285, 120286, 120287, 120288, 120289, 120290, 120291, 120292, 120293, 120294, 120295, 120296, 120297, 120298, 120299, 120300, 120301, 120302, 120303, 120304, 120305, 120306, 120307, 120308, 120309, 120310, 120311, 120312, 120313, 120315, 120316, 120317, 120318, 120319, 120320, 120321, 120322, 120323, 120324, 120325, 120326, 120327, 120328, 120329, 120330, 120331, 120332, 120333, 120334, 120335, 120336, 120337, 120338, 120339, 120340, 120341, 120342, 120343, 120344, 120345, 120346, 120347, 120348, 120349, 120350, 120351, 120352, 120353, 120354, 120355, 120356, 120357, 120358, 120359, 120360, 120361, 120362, 120363, 120364, 120365, 120367, 120368, 120369, 120370, 120371, 120372, 120373, 120374, 120375, 120376, 120377, 120378, 120379, 120380, 120381, 120382, 120383, 120384, 120385, 120386, 120387, 120388, 120389, 120390, 120391, 120392, 120393, 120394, 120395, 120396, 120397, 120398, 120399, 120400, 120401, 120402, 120403, 120404, 120405, 120406, 120407, 120408, 120409, 120410, 120411, 120412, 120413, 120414, 120415, 120416, 120417, 120419, 120420, 120421, 120422, 120423, 120424, 120425, 120426, 120427, 120428, 120429, 120430, 120431, 120432, 120433, 120434, 120435, 120436, 120437, 120438, 120439, 120440, 120441, 120442, 120443, 120444, 120445, 120446, 120447, 120448, 120449, 120450, 120451, 120452, 120453, 120454, 120455, 120456, 120457, 120458, 120459, 120460, 120461, 120462, 120463, 120464, 120465, 120466, 120467, 120468, 120469, 120471, 120472, 120473, 120474, 120475, 120476, 120477, 120478, 120479, 120480, 120481, 120482, 120483, 120484, 120488, 120489, 120492, 120493, 120494, 120496, 120497, 120499, 120500, 120502, 120504, 120507, 120508, 120510, 120514, 120516, 120522, 120526, 120528, 120530, 120532, 120534, 120544, 120546, 120547, 120550, 120551, 120552, 120554, 120555, 120557, 120558, 120560, 120562, 120565, 120566, 120568, 120572, 120574, 120580, 120584, 120586, 120588, 120590, 120592, 120602, 120604, 120605, 120608, 120609, 120610, 120612, 120613, 120615, 120616, 120618, 120620, 120623, 120624, 120626, 120630, 120632, 120638, 120642, 120644, 120646, 120648, 120650, 120660, 120662, 120663, 120666, 120667, 120668, 120670, 120671, 120673, 120674, 120676, 120678, 120681, 120682, 120684, 120688, 120690, 120696, 120700, 120702, 120704, 120706, 120708, 120718, 120720, 120721, 120724, 120725, 120726, 120728, 120729, 120731, 120732, 120734, 120736, 120739, 120740, 120742, 120746, 120748, 120754, 120758, 120760, 120762, 120764, 120766, 120776, 120778, 120782, 120783, 120784, 120785, 120786, 120787, 120788, 120789, 120790, 120791, 120792, 120793, 120794, 120795, 120796, 120797, 120798, 120799, 120800, 120801, 120802, 120803, 120804, 120805, 120806, 120807, 120808, 120809, 120810, 120811, 120812, 120813, 120814, 120815, 120816, 120817, 120818, 120819, 120820, 120821, 120822, 120823, 120824, 120825, 120826, 120827, 120828, 120829, 120830, 120831, 125127, 125131, 126464, 126500, 126564, 126592, 126596, 128844, 128872, 130032, 130033, 130034, 130035, 130036, 130037, 130038, 130039, 130040, 130041}, + With: []rune{44, 102, 98, 103, 82, 50, 51, 53, 115, 73, 33, 51, 56, 56, 63, 97, 103, 121, 105, 105, 119, 117, 121, 63, 96, 96, 96, 96, 96, 60, 62, 94, 94, 96, 96, 96, 58, 45, 105, 126, 96, 58, 96, 105, 59, 74, 96, 65, 66, 69, 90, 72, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 89, 70, 50, 99, 106, 67, 77, 83, 73, 74, 115, 105, 106, 119, 86, 118, 89, 121, 104, 101, 73, 105, 51, 100, 71, 113, 87, 119, 85, 83, 79, 96, 96, 119, 113, 113, 104, 110, 110, 117, 103, 102, 111, 58, 108, 58, 108, 118, 96, 108, 111, 96, 44, 108, 111, 46, 108, 111, 86, 44, 42, 111, 111, 45, 111, 46, 73, 111, 86, 46, 46, 58, 58, 79, 108, 96, 96, 95, 58, 111, 63, 79, 56, 57, 111, 57, 56, 58, 111, 56, 79, 79, 57, 111, 111, 111, 111, 111, 111, 111, 111, 57, 111, 111, 111, 111, 111, 121, 111, 85, 79, 68, 82, 84, 105, 89, 65, 74, 69, 63, 87, 77, 72, 89, 71, 104, 90, 52, 98, 82, 87, 83, 86, 83, 76, 67, 80, 75, 100, 54, 71, 66, 61, 86, 62, 60, 96, 85, 80, 100, 98, 74, 76, 50, 120, 72, 120, 82, 98, 70, 65, 68, 68, 77, 66, 88, 120, 32, 60, 88, 73, 96, 75, 77, 58, 43, 47, 58, 58, 99, 111, 111, 117, 118, 119, 122, 114, 103, 121, 102, 121, 96, 105, 96, 126, 96, 96, 96, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 96, 46, 32, 32, 32, 96, 60, 62, 47, 45, 47, 42, 126, 58, 32, 67, 103, 72, 72, 72, 104, 73, 73, 76, 108, 78, 80, 81, 82, 82, 82, 90, 90, 75, 66, 67, 101, 101, 69, 70, 77, 111, 105, 121, 68, 100, 101, 105, 106, 73, 86, 88, 76, 67, 68, 77, 105, 118, 120, 73, 99, 100, 45, 47, 92, 42, 73, 118, 85, 58, 126, 84, 118, 85, 69, 105, 112, 97, 73, 47, 88, 40, 41, 60, 62, 40, 41, 123, 125, 43, 45, 47, 92, 84, 120, 120, 92, 47, 92, 120, 114, 72, 73, 75, 77, 78, 79, 111, 80, 112, 67, 99, 84, 89, 88, 45, 47, 57, 51, 76, 54, 86, 69, 73, 33, 79, 81, 88, 61, 92, 47, 79, 40, 41, 47, 61, 47, 92, 92, 47, 66, 80, 100, 68, 84, 71, 75, 74, 67, 90, 70, 77, 78, 76, 83, 82, 86, 72, 87, 88, 89, 65, 69, 73, 79, 85, 46, 44, 58, 61, 46, 50, 105, 86, 63, 50, 115, 50, 51, 57, 38, 58, 96, 70, 102, 117, 51, 74, 88, 66, 101, 102, 111, 114, 114, 117, 117, 121, 105, 114, 119, 122, 118, 115, 99, 111, 111, 111, 111, 111, 111, 111, 111, 40, 41, 58, 95, 95, 95, 45, 92, 108, 108, 111, 111, 111, 111, 34, 35, 36, 37, 38, 96, 42, 43, 45, 46, 47, 48, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61, 62, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 73, 66, 69, 70, 124, 88, 79, 80, 83, 84, 43, 65, 66, 67, 70, 79, 77, 84, 89, 88, 72, 90, 66, 67, 124, 77, 84, 88, 56, 42, 108, 88, 79, 67, 76, 83, 111, 99, 115, 82, 79, 85, 55, 111, 117, 78, 79, 75, 67, 86, 70, 76, 88, 46, 79, 118, 119, 119, 119, 86, 70, 76, 89, 69, 90, 57, 69, 52, 76, 79, 85, 53, 84, 118, 115, 70, 105, 122, 55, 111, 51, 57, 54, 57, 111, 117, 121, 79, 90, 87, 67, 88, 87, 67, 86, 84, 76, 73, 82, 83, 51, 62, 65, 85, 89, 96, 96, 65, 66, 67, 68, 69, 70, 71, 72, 108, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 79, 108, 50, 51, 52, 53, 54, 55, 56, 57, 123, 46, 51, 86, 92, 55, 70, 82, 76, 60, 62, 47, 92, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 67, 68, 71, 74, 75, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 102, 104, 105, 106, 107, 108, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 68, 69, 70, 71, 74, 75, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 68, 69, 70, 71, 73, 74, 75, 76, 77, 79, 83, 84, 85, 86, 87, 88, 89, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 105, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 70, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 108, 56, 108, 111, 111, 108, 111, 67, 84, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57}, Locale: "_common", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -152,16 +152,15 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 8190, Hi: 8192, Stride: 2}, {Lo: 8193, Hi: 8202, Stride: 1}, {Lo: 8208, Hi: 8210, Stride: 1}, - {Lo: 8218, Hi: 8219, Stride: 1}, - {Lo: 8228, Hi: 8232, Stride: 4}, - {Lo: 8233, Hi: 8239, Stride: 6}, - {Lo: 8242, Hi: 8249, Stride: 7}, - {Lo: 8250, Hi: 8257, Stride: 7}, - {Lo: 8259, Hi: 8260, Stride: 1}, - {Lo: 8270, Hi: 8275, Stride: 5}, - {Lo: 8282, Hi: 8287, Stride: 5}, - {Lo: 8450, Hi: 8458, Stride: 8}, - {Lo: 8459, Hi: 8462, Stride: 1}, + {Lo: 8219, Hi: 8228, Stride: 9}, + {Lo: 8232, Hi: 8233, Stride: 1}, + {Lo: 8239, Hi: 8242, Stride: 3}, + {Lo: 8249, Hi: 8250, Stride: 1}, + {Lo: 8257, Hi: 8259, Stride: 2}, + {Lo: 8260, Hi: 8270, Stride: 10}, + {Lo: 8275, Hi: 8282, Stride: 7}, + {Lo: 8287, Hi: 8450, Stride: 163}, + {Lo: 8458, Hi: 8462, Stride: 1}, {Lo: 8464, Hi: 8467, Stride: 1}, {Lo: 8469, Hi: 8473, Stride: 4}, {Lo: 8474, Hi: 8477, Stride: 1}, @@ -250,10 +249,10 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 65112, Hi: 65128, Stride: 16}, {Lo: 65165, Hi: 65166, Stride: 1}, {Lo: 65257, Hi: 65260, Stride: 1}, - {Lo: 65282, Hi: 65284, Stride: 2}, - {Lo: 65285, Hi: 65287, Stride: 1}, + {Lo: 65282, Hi: 65287, Stride: 1}, {Lo: 65290, Hi: 65291, Stride: 1}, - {Lo: 65293, Hi: 65305, Stride: 1}, + {Lo: 65293, Hi: 65296, Stride: 1}, + {Lo: 65298, Hi: 65305, Stride: 1}, {Lo: 65308, Hi: 65310, Stride: 1}, {Lo: 65312, Hi: 65373, Stride: 1}, {Lo: 65512, Hi: 65512, Stride: 1}, @@ -304,15 +303,16 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 94011, Hi: 94015, Stride: 4}, {Lo: 94016, Hi: 94018, Stride: 2}, {Lo: 94019, Hi: 94033, Stride: 14}, - {Lo: 94034, Hi: 119060, Stride: 25026}, - {Lo: 119149, Hi: 119302, Stride: 153}, - {Lo: 119309, Hi: 119311, Stride: 2}, - {Lo: 119314, Hi: 119315, Stride: 1}, - {Lo: 119318, Hi: 119338, Stride: 20}, - {Lo: 119350, Hi: 119351, Stride: 1}, - {Lo: 119354, Hi: 119355, Stride: 1}, - {Lo: 119808, Hi: 119845, Stride: 1}, - {Lo: 119847, Hi: 119892, Stride: 1}, + {Lo: 94034, Hi: 117974, Stride: 23940}, + {Lo: 117975, Hi: 118009, Stride: 1}, + {Lo: 119060, Hi: 119149, Stride: 89}, + {Lo: 119302, Hi: 119309, Stride: 7}, + {Lo: 119311, Hi: 119314, Stride: 3}, + {Lo: 119315, Hi: 119318, Stride: 3}, + {Lo: 119338, Hi: 119350, Stride: 12}, + {Lo: 119351, Hi: 119354, Stride: 3}, + {Lo: 119355, Hi: 119808, Stride: 453}, + {Lo: 119809, Hi: 119892, Stride: 1}, {Lo: 119894, Hi: 119897, Stride: 1}, {Lo: 119899, Hi: 119949, Stride: 1}, {Lo: 119951, Hi: 119964, Stride: 1}, @@ -405,8 +405,8 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ }, }, "_default": { - Confusable: []rune{160, 180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{32, 96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{160, 180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8217, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{32, 96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 96, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "_default", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -424,20 +424,21 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, {Lo: 8211, Hi: 8216, Stride: 5}, - {Lo: 8217, Hi: 8245, Stride: 28}, - {Lo: 12494, Hi: 65281, Stride: 52787}, - {Lo: 65283, Hi: 65288, Stride: 5}, + {Lo: 8217, Hi: 8218, Stride: 1}, + {Lo: 8245, Hi: 12494, Stride: 4249}, + {Lo: 65281, Hi: 65288, Stride: 7}, {Lo: 65289, Hi: 65292, Stride: 3}, - {Lo: 65306, Hi: 65307, Stride: 1}, - {Lo: 65311, Hi: 65374, Stride: 63}, + {Lo: 65297, Hi: 65306, Stride: 9}, + {Lo: 65307, Hi: 65311, Stride: 4}, + {Lo: 65374, Hi: 65374, Stride: 1}, }, R32: []unicode.Range32{}, LatinOffset: 1, }, }, "cs": { - Confusable: []rune{180, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{96, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{180, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{96, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "cs", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -453,11 +454,11 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8216, Hi: 8217, Stride: 1}, + {Lo: 8216, Hi: 8218, Stride: 2}, {Lo: 8245, Hi: 12494, Stride: 4249}, - {Lo: 65281, Hi: 65283, Stride: 2}, - {Lo: 65288, Hi: 65289, Stride: 1}, - {Lo: 65292, Hi: 65306, Stride: 14}, + {Lo: 65281, Hi: 65288, Stride: 7}, + {Lo: 65289, Hi: 65292, Stride: 3}, + {Lo: 65297, Hi: 65306, Stride: 9}, {Lo: 65307, Hi: 65311, Stride: 4}, {Lo: 65374, Hi: 65374, Stride: 1}, }, @@ -466,8 +467,8 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ }, }, "de": { - Confusable: []rune{180, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{96, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{180, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{96, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "de", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -483,11 +484,10 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8216, Hi: 8217, Stride: 1}, {Lo: 8245, Hi: 12494, Stride: 4249}, - {Lo: 65281, Hi: 65283, Stride: 2}, - {Lo: 65288, Hi: 65289, Stride: 1}, - {Lo: 65292, Hi: 65306, Stride: 14}, + {Lo: 65281, Hi: 65288, Stride: 7}, + {Lo: 65289, Hi: 65292, Stride: 3}, + {Lo: 65297, Hi: 65306, Stride: 9}, {Lo: 65307, Hi: 65311, Stride: 4}, {Lo: 65374, Hi: 65374, Stride: 1}, }, @@ -496,8 +496,8 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ }, }, "es": { - Confusable: []rune{180, 215, 305, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{96, 120, 105, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{180, 215, 305, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{96, 120, 105, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "es", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -513,20 +513,21 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8211, Hi: 8245, Stride: 34}, - {Lo: 12494, Hi: 65281, Stride: 52787}, - {Lo: 65283, Hi: 65288, Stride: 5}, + {Lo: 8211, Hi: 8218, Stride: 7}, + {Lo: 8245, Hi: 12494, Stride: 4249}, + {Lo: 65281, Hi: 65288, Stride: 7}, {Lo: 65289, Hi: 65292, Stride: 3}, - {Lo: 65306, Hi: 65307, Stride: 1}, - {Lo: 65311, Hi: 65374, Stride: 63}, + {Lo: 65297, Hi: 65306, Stride: 9}, + {Lo: 65307, Hi: 65311, Stride: 4}, + {Lo: 65374, Hi: 65374, Stride: 1}, }, R32: []unicode.Range32{}, LatinOffset: 1, }, }, "fr": { - Confusable: []rune{215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "fr", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -542,20 +543,21 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8216, Hi: 8245, Stride: 29}, - {Lo: 12494, Hi: 65281, Stride: 52787}, - {Lo: 65283, Hi: 65288, Stride: 5}, + {Lo: 8216, Hi: 8218, Stride: 2}, + {Lo: 8245, Hi: 12494, Stride: 4249}, + {Lo: 65281, Hi: 65288, Stride: 7}, {Lo: 65289, Hi: 65292, Stride: 3}, - {Lo: 65306, Hi: 65307, Stride: 1}, - {Lo: 65311, Hi: 65374, Stride: 63}, + {Lo: 65297, Hi: 65306, Stride: 9}, + {Lo: 65307, Hi: 65311, Stride: 4}, + {Lo: 65374, Hi: 65374, Stride: 1}, }, R32: []unicode.Range32{}, LatinOffset: 0, }, }, "it": { - Confusable: []rune{160, 180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{32, 96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{160, 180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{32, 96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "it", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -572,11 +574,11 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8211, Hi: 8216, Stride: 5}, + {Lo: 8211, Hi: 8218, Stride: 7}, {Lo: 8245, Hi: 12494, Stride: 4249}, - {Lo: 65281, Hi: 65283, Stride: 2}, - {Lo: 65288, Hi: 65289, Stride: 1}, - {Lo: 65292, Hi: 65306, Stride: 14}, + {Lo: 65281, Hi: 65288, Stride: 7}, + {Lo: 65289, Hi: 65292, Stride: 3}, + {Lo: 65297, Hi: 65306, Stride: 9}, {Lo: 65307, Hi: 65311, Stride: 4}, {Lo: 65374, Hi: 65374, Stride: 1}, }, @@ -585,8 +587,8 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ }, }, "ja": { - Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8217, 8245, 65281, 65283, 65292, 65306, 65307}, - With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 96, 96, 33, 35, 44, 58, 59}, + Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8218, 8245, 65281, 65292, 65297, 65307}, + With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 44, 96, 33, 44, 49, 59}, Locale: "ja", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -603,18 +605,17 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, {Lo: 8211, Hi: 8216, Stride: 5}, - {Lo: 8217, Hi: 8245, Stride: 28}, - {Lo: 65281, Hi: 65283, Stride: 2}, - {Lo: 65292, Hi: 65306, Stride: 14}, - {Lo: 65307, Hi: 65307, Stride: 1}, + {Lo: 8218, Hi: 8245, Stride: 27}, + {Lo: 65281, Hi: 65292, Stride: 11}, + {Lo: 65297, Hi: 65307, Stride: 10}, }, R32: []unicode.Range32{}, LatinOffset: 1, }, }, "ko": { - Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "ko", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -630,20 +631,21 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8211, Hi: 8245, Stride: 34}, - {Lo: 12494, Hi: 65281, Stride: 52787}, - {Lo: 65283, Hi: 65288, Stride: 5}, + {Lo: 8211, Hi: 8218, Stride: 7}, + {Lo: 8245, Hi: 12494, Stride: 4249}, + {Lo: 65281, Hi: 65288, Stride: 7}, {Lo: 65289, Hi: 65292, Stride: 3}, - {Lo: 65306, Hi: 65307, Stride: 1}, - {Lo: 65311, Hi: 65374, Stride: 63}, + {Lo: 65297, Hi: 65306, Stride: 9}, + {Lo: 65307, Hi: 65311, Stride: 4}, + {Lo: 65374, Hi: 65374, Stride: 1}, }, R32: []unicode.Range32{}, LatinOffset: 1, }, }, "pl": { - Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "pl", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -659,21 +661,20 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8216, Hi: 8217, Stride: 1}, - {Lo: 8245, Hi: 12494, Stride: 4249}, - {Lo: 65281, Hi: 65283, Stride: 2}, + {Lo: 8216, Hi: 8245, Stride: 29}, + {Lo: 12494, Hi: 65281, Stride: 52787}, {Lo: 65288, Hi: 65289, Stride: 1}, - {Lo: 65292, Hi: 65306, Stride: 14}, - {Lo: 65307, Hi: 65311, Stride: 4}, - {Lo: 65374, Hi: 65374, Stride: 1}, + {Lo: 65292, Hi: 65297, Stride: 5}, + {Lo: 65306, Hi: 65307, Stride: 1}, + {Lo: 65311, Hi: 65374, Stride: 63}, }, R32: []unicode.Range32{}, LatinOffset: 1, }, }, "pt-BR": { - Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "pt-BR", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -689,11 +690,11 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8216, Hi: 8217, Stride: 1}, + {Lo: 8216, Hi: 8218, Stride: 2}, {Lo: 8245, Hi: 12494, Stride: 4249}, - {Lo: 65281, Hi: 65283, Stride: 2}, - {Lo: 65288, Hi: 65289, Stride: 1}, - {Lo: 65292, Hi: 65306, Stride: 14}, + {Lo: 65281, Hi: 65288, Stride: 7}, + {Lo: 65289, Hi: 65292, Stride: 3}, + {Lo: 65297, Hi: 65306, Stride: 9}, {Lo: 65307, Hi: 65311, Stride: 4}, {Lo: 65374, Hi: 65374, Stride: 1}, }, @@ -702,8 +703,8 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ }, }, "qps-ploc": { - Confusable: []rune{160, 180, 215, 305, 921, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{32, 96, 120, 105, 73, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{160, 180, 215, 305, 921, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{32, 96, 120, 105, 73, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "qps-ploc", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -720,10 +721,10 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, {Lo: 8211, Hi: 8216, Stride: 5}, - {Lo: 8217, Hi: 8245, Stride: 28}, + {Lo: 8218, Hi: 8245, Stride: 27}, {Lo: 12494, Hi: 65281, Stride: 52787}, - {Lo: 65283, Hi: 65288, Stride: 5}, - {Lo: 65289, Hi: 65292, Stride: 3}, + {Lo: 65288, Hi: 65289, Stride: 1}, + {Lo: 65292, Hi: 65297, Stride: 5}, {Lo: 65306, Hi: 65307, Stride: 1}, {Lo: 65311, Hi: 65374, Stride: 63}, }, @@ -732,18 +733,18 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ }, }, "ru": { - Confusable: []rune{180, 215, 305, 921, 1009, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{96, 120, 105, 73, 112, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{180, 215, 305, 921, 1009, 8216, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{96, 120, 105, 73, 112, 96, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "ru", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ {Lo: 180, Hi: 215, Stride: 35}, {Lo: 305, Hi: 921, Stride: 616}, {Lo: 1009, Hi: 8216, Stride: 7207}, - {Lo: 8217, Hi: 8245, Stride: 28}, + {Lo: 8218, Hi: 8245, Stride: 27}, {Lo: 12494, Hi: 65281, Stride: 52787}, - {Lo: 65283, Hi: 65288, Stride: 5}, - {Lo: 65289, Hi: 65292, Stride: 3}, + {Lo: 65288, Hi: 65289, Stride: 1}, + {Lo: 65292, Hi: 65297, Stride: 5}, {Lo: 65306, Hi: 65307, Stride: 1}, {Lo: 65311, Hi: 65374, Stride: 63}, }, @@ -752,8 +753,8 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ }, }, "tr": { - Confusable: []rune{160, 180, 215, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374}, - With: []rune{32, 96, 120, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126}, + Confusable: []rune{160, 180, 215, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8218, 8245, 12494, 65281, 65288, 65289, 65292, 65297, 65306, 65307, 65311, 65374}, + With: []rune{32, 96, 120, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 44, 96, 47, 33, 40, 41, 44, 49, 58, 59, 63, 126}, Locale: "tr", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -769,38 +770,39 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8211, Hi: 8245, Stride: 34}, - {Lo: 12494, Hi: 65281, Stride: 52787}, - {Lo: 65283, Hi: 65288, Stride: 5}, + {Lo: 8211, Hi: 8218, Stride: 7}, + {Lo: 8245, Hi: 12494, Stride: 4249}, + {Lo: 65281, Hi: 65288, Stride: 7}, {Lo: 65289, Hi: 65292, Stride: 3}, - {Lo: 65306, Hi: 65307, Stride: 1}, - {Lo: 65311, Hi: 65374, Stride: 63}, + {Lo: 65297, Hi: 65306, Stride: 9}, + {Lo: 65307, Hi: 65311, Stride: 4}, + {Lo: 65374, Hi: 65374, Stride: 1}, }, R32: []unicode.Range32{}, LatinOffset: 1, }, }, "zh-hans": { - Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8245, 12494, 65281, 65288, 65289, 65306, 65374}, - With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 47, 33, 40, 41, 58, 126}, + Confusable: []rune{160, 180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8218, 8245, 12494, 65297, 65374}, + With: []rune{32, 96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 44, 96, 47, 49, 126}, Locale: "zh-hans", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ - {Lo: 180, Hi: 215, Stride: 35}, - {Lo: 305, Hi: 921, Stride: 616}, - {Lo: 1009, Hi: 1040, Stride: 31}, - {Lo: 1042, Hi: 1045, Stride: 3}, - {Lo: 1047, Hi: 1050, Stride: 3}, - {Lo: 1052, Hi: 1054, Stride: 1}, + {Lo: 160, Hi: 180, Stride: 20}, + {Lo: 215, Hi: 305, Stride: 90}, + {Lo: 921, Hi: 1009, Stride: 88}, + {Lo: 1040, Hi: 1042, Stride: 2}, + {Lo: 1045, Hi: 1047, Stride: 2}, + {Lo: 1050, Hi: 1052, Stride: 2}, + {Lo: 1053, Hi: 1054, Stride: 1}, {Lo: 1056, Hi: 1059, Stride: 1}, {Lo: 1061, Hi: 1068, Stride: 7}, {Lo: 1072, Hi: 1073, Stride: 1}, {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8245, Hi: 12494, Stride: 4249}, - {Lo: 65281, Hi: 65288, Stride: 7}, - {Lo: 65289, Hi: 65306, Stride: 17}, + {Lo: 8218, Hi: 8245, Stride: 27}, + {Lo: 12494, Hi: 65297, Stride: 52803}, {Lo: 65374, Hi: 65374, Stride: 1}, }, R32: []unicode.Range32{}, @@ -808,8 +810,8 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ }, }, "zh-hant": { - Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 12494, 65283, 65307, 65374}, - With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 47, 35, 59, 126}, + Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8218, 12494, 65374}, + With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 44, 47, 126}, Locale: "zh-hant", RangeTable: &unicode.RangeTable{ R16: []unicode.Range16{ @@ -825,9 +827,8 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ {Lo: 1075, Hi: 1077, Stride: 2}, {Lo: 1086, Hi: 1088, Stride: 2}, {Lo: 1089, Hi: 1093, Stride: 2}, - {Lo: 8211, Hi: 12494, Stride: 4283}, - {Lo: 65283, Hi: 65307, Stride: 24}, - {Lo: 65374, Hi: 65374, Stride: 1}, + {Lo: 8211, Hi: 8218, Stride: 7}, + {Lo: 12494, Hi: 65374, Stride: 52880}, }, R32: []unicode.Range32{}, LatinOffset: 1, From 25f3f8e1d27c74ffaebeddbc47ff5636b30abca2 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 29 May 2025 10:06:30 +0200 Subject: [PATCH 47/94] feat: make Forgejo Actions server logs less noisy (#7986) - The `/api/actions/runner.v1.RunnerService/FetchTask` route is continuously polling for its next task, because long-polling is not implemented this request is made every second. The `/api/actions/runner.v1.RunnerService/UpdateLog` route is used to send new logs of the CI runs that are currently happening. - Just like the assets requests, they spam the logs and should only be logged at a lower log level. ## Release notes - Features - [PR](https://codeberg.org/forgejo/forgejo/pulls/7986): make Forgejo Actions server logs less noisy Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7986 Reviewed-by: Beowulf Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/web/routing/logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/web/routing/logger.go b/modules/web/routing/logger.go index 760e092914..8fd24c9733 100644 --- a/modules/web/routing/logger.go +++ b/modules/web/routing/logger.go @@ -90,7 +90,7 @@ func logPrinter(logger log.Logger) func(trigger Event, record *requestRecord) { status = v.WrittenStatus() } logf := logger.Info - if strings.HasPrefix(req.RequestURI, "/assets/") { + if strings.HasPrefix(req.RequestURI, "/assets/") || req.RequestURI == "/api/actions/runner.v1.RunnerService/FetchTask" || req.RequestURI == "/api/actions/runner.v1.RunnerService/UpdateLog" { logf = logger.Trace } message := completedMessage From 99d697263f2ac367b1cc0a553b85242a2215ad37 Mon Sep 17 00:00:00 2001 From: chavacava Date: Thu, 29 May 2025 17:34:29 +0200 Subject: [PATCH 48/94] chore(cleanup): replaces unnecessary calls to formatting functions by non-formatting equivalents (#7994) This PR replaces unnecessary calls to formatting functions (`fmt.Printf`, `fmt.Errorf`, ...) by non-formatting equivalents. Resolves #7967 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7994 Reviewed-by: Gusted Co-authored-by: chavacava Co-committed-by: chavacava --- cmd/admin_auth.go | 2 +- cmd/admin_user_create.go | 2 +- cmd/admin_user_list.go | 4 ++-- cmd/dump.go | 5 +++-- cmd/forgejo/actions.go | 5 +++-- cmd/generate.go | 6 +++--- cmd/hook.go | 10 +++++----- cmd/main_test.go | 3 ++- models/actions/run.go | 3 ++- models/activities/action.go | 3 ++- models/asymkey/gpg_key.go | 3 ++- models/asymkey/gpg_key_common.go | 9 +++++---- models/asymkey/gpg_key_object_verification.go | 3 ++- models/asymkey/ssh_key_parse.go | 3 ++- models/forgefed/federationhost_test.go | 10 +++++----- models/forgefed/nodeinfo_test.go | 8 ++++---- models/forgejo_migrations/migrate.go | 3 ++- models/issues/issue.go | 3 ++- models/issues/issue_update.go | 5 +++-- models/issues/pull.go | 3 ++- models/issues/review.go | 9 +++++---- models/migrations/base/db.go | 2 +- models/migrations/migrations.go | 3 ++- models/migrations/v1_13/v151.go | 3 ++- models/migrations/v1_14/v158.go | 4 ++-- models/migrations/v1_14/v177_test.go | 4 ++-- models/migrations/v1_17/v222.go | 3 ++- models/migrations/v1_21/v264.go | 4 ++-- models/project/column.go | 4 ++-- models/project/issue.go | 6 +++--- models/repo/attachment.go | 3 ++- models/repo/following_repo_test.go | 2 +- models/repo/repo.go | 3 ++- models/user/federated_user_test.go | 2 +- models/user/setting.go | 5 +++-- modules/auth/openid/discovery_cache_test.go | 4 ++-- modules/avatar/identicon/identicon.go | 3 ++- modules/cache/cache.go | 7 ++++--- modules/cache/cache_test.go | 14 +++++++------- modules/emoji/emoji_test.go | 14 +++++++------- modules/forgefed/activity_like_test.go | 4 ++-- modules/forgefed/activity_undo_like_test.go | 4 ++-- modules/forgefed/actor_test.go | 14 +++++++------- modules/git/commit.go | 3 +-- modules/git/repo_attribute.go | 3 ++- modules/git/repo_branch.go | 2 +- modules/indexer/code/internal/indexer.go | 8 ++++---- modules/indexer/internal/bleve/indexer.go | 10 +++++----- .../indexer/internal/elasticsearch/indexer.go | 9 +++++---- modules/indexer/internal/indexer.go | 6 +++--- modules/indexer/internal/meilisearch/indexer.go | 9 +++++---- modules/indexer/issues/internal/indexer.go | 8 ++++---- modules/indexer/stats/queue.go | 4 ++-- modules/issue/template/template.go | 7 ++++--- modules/markup/markdown/markdown.go | 4 ++-- modules/setting/config_provider.go | 2 +- modules/setting/incoming_email.go | 3 ++- modules/templates/util_dict.go | 3 ++- modules/validation/validatable_test.go | 6 +++--- routers/api/actions/artifacts_chunks.go | 6 +++--- routers/api/packages/chef/auth.go | 7 ++++--- routers/api/v1/activitypub/reqsignature.go | 3 ++- routers/api/v1/admin/quota_rule.go | 4 ++-- routers/api/v1/admin/user.go | 4 ++-- routers/api/v1/repo/branch.go | 7 +++---- routers/api/v1/repo/file.go | 2 +- routers/api/v1/repo/issue_label.go | 6 +++--- routers/api/v1/repo/issue_tracked_time.go | 7 ++++--- routers/api/v1/repo/migrate.go | 4 ++-- routers/api/v1/repo/notes.go | 3 ++- routers/api/v1/repo/pull.go | 6 +++--- routers/api/v1/repo/pull_review.go | 9 +++++---- routers/api/v1/repo/release.go | 3 ++- routers/api/v1/repo/repo.go | 11 ++++++----- routers/api/v1/repo/transfer.go | 2 +- routers/api/v1/user/gpg_key.go | 5 +++-- routers/api/v1/user/key.go | 6 +++--- routers/api/v1/utils/git.go | 3 ++- routers/common/db.go | 4 ++-- routers/private/manager_process.go | 4 ++-- routers/web/auth/openid.go | 5 +++-- routers/web/repo/action_aggregator_test.go | 2 +- routers/web/repo/actions/view.go | 2 +- routers/web/user/setting/keys.go | 12 ++++++------ services/actions/auth.go | 5 +++-- services/actions/commit_status.go | 7 ++++--- services/actions/schedule_tasks.go | 3 ++- services/actions/task.go | 3 ++- services/auth/httpsign.go | 6 +++--- services/auth/source/oauth2/token.go | 5 +++-- services/automerge/automerge.go | 2 +- services/context/api.go | 5 +++-- services/doctor/authorizedkeys.go | 3 ++- services/doctor/lfs.go | 4 ++-- services/externalaccount/link.go | 4 ++-- services/federation/federation_service.go | 3 ++- services/issue/comments.go | 3 ++- services/issue/issue.go | 5 +++-- services/issue/milestone.go | 3 ++- services/lfs/server.go | 12 ++++++------ services/mailer/mail_team_invite.go | 3 ++- services/mailer/mailer.go | 5 +++-- services/markup/processorhelper.go | 4 ++-- services/migrations/gogs_test.go | 2 +- services/migrations/migrate.go | 3 ++- services/mirror/mirror.go | 6 +++--- services/packages/alpine/repository.go | 2 +- services/packages/auth.go | 5 +++-- services/pull/check.go | 2 +- services/pull/merge.go | 9 +++++---- services/pull/review.go | 5 +++-- services/pull/update.go | 3 ++- services/release/release.go | 16 ++++++++-------- services/repository/branch.go | 2 +- services/repository/contributors_graph.go | 2 +- services/repository/files/cherry_pick.go | 5 +++-- services/repository/files/file.go | 6 +++--- services/repository/files/temp_repo.go | 5 +++-- services/repository/push.go | 2 +- services/repository/repository.go | 5 +++-- services/task/task.go | 3 ++- services/webhook/deliver.go | 3 ++- services/wiki/wiki_test.go | 4 ++-- tests/integration/api_actions_artifact_test.go | 6 +++--- .../integration/api_actions_artifact_v4_test.go | 10 +++++----- tests/integration/repo_settings_test.go | 2 +- 126 files changed, 340 insertions(+), 281 deletions(-) diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index b5e0212df7..13cae76705 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -81,7 +81,7 @@ func runListAuth(c *cli.Context) error { // loop through each source and print w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags) - fmt.Fprintf(w, "ID\tName\tType\tEnabled\n") + fmt.Fprint(w, "ID\tName\tType\tEnabled\n") for _, source := range authSources { fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive) } diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index f84254f39c..8434dc241d 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -108,7 +108,7 @@ func runCreateUser(c *cli.Context) error { username = c.String("username") } else { username = c.String("name") - _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n") + _, _ = fmt.Fprint(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n") } ctx, cancel := installSignals() diff --git a/cmd/admin_user_list.go b/cmd/admin_user_list.go index 6044ce7c3f..4ca81babbb 100644 --- a/cmd/admin_user_list.go +++ b/cmd/admin_user_list.go @@ -41,7 +41,7 @@ func runListUsers(c *cli.Context) error { w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0) if c.IsSet("admin") { - fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n") + fmt.Fprint(w, "ID\tUsername\tEmail\tIsActive\n") for _, u := range users { if u.IsAdmin { fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive) @@ -49,7 +49,7 @@ func runListUsers(c *cli.Context) error { } } else { twofa := user_model.UserList(users).GetTwoFaStatus(ctx) - fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n") + fmt.Fprint(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n") for _, u := range users { fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID]) } diff --git a/cmd/dump.go b/cmd/dump.go index bc0b269924..16ad412fda 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -5,6 +5,7 @@ package cmd import ( + "errors" "fmt" "io" "os" @@ -212,13 +213,13 @@ func runDump(ctx *cli.Context) error { if !setting.InstallLock { log.Error("Is '%s' really the right config path?\n", setting.CustomConf) - return fmt.Errorf("forgejo is not initialized") + return errors.New("forgejo is not initialized") } setting.LoadSettings() // cannot access session settings otherwise verbose := ctx.Bool("verbose") if verbose && ctx.Bool("quiet") { - return fmt.Errorf("--quiet and --verbose cannot both be set") + return errors.New("--quiet and --verbose cannot both be set") } stdCtx, cancel := installSignals() diff --git a/cmd/forgejo/actions.go b/cmd/forgejo/actions.go index dbe7398bcf..ea6db91f6d 100644 --- a/cmd/forgejo/actions.go +++ b/cmd/forgejo/actions.go @@ -6,6 +6,7 @@ package forgejo import ( "context" "encoding/hex" + "errors" "fmt" "io" "os" @@ -124,7 +125,7 @@ func readSecret(ctx context.Context, cliCtx *cli.Context) (string, error) { } return string(buf), nil } - return "", fmt.Errorf("at least one of the --secret, --secret-stdin, --secret-file options is required") + return "", errors.New("at least one of the --secret, --secret-stdin, --secret-file options is required") } func validateSecret(secret string) error { @@ -144,7 +145,7 @@ func getLabels(cliCtx *cli.Context) (*[]string, error) { return &lblValue, nil } if cliCtx.String("labels") != "" { - return nil, fmt.Errorf("--labels and --keep-labels should not be used together") + return nil, errors.New("--labels and --keep-labels should not be used together") } return nil, nil } diff --git a/cmd/generate.go b/cmd/generate.go index dcbdcd0353..fd8b50a0c1 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -63,7 +63,7 @@ func runGenerateInternalToken(c *cli.Context) error { fmt.Printf("%s", internalToken) if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Printf("\n") + fmt.Println() } return nil @@ -75,7 +75,7 @@ func runGenerateLfsJwtSecret(c *cli.Context) error { fmt.Printf("%s", jwtSecretBase64) if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Printf("\n") + fmt.Print("\n") } return nil @@ -90,7 +90,7 @@ func runGenerateSecretKey(c *cli.Context) error { fmt.Printf("%s", secretKey) if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Printf("\n") + fmt.Print("\n") } return nil diff --git a/cmd/hook.go b/cmd/hook.go index 935c1b08ea..1c8d4e499b 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -247,7 +247,7 @@ Forgejo or set your environment appropriately.`, "") newCommitIDs[count] = newCommitID refFullNames[count] = refFullName count++ - fmt.Fprintf(out, "*") + fmt.Fprint(out, "*") if count >= hookBatchSize { fmt.Fprintf(out, " Checking %d references\n", count) @@ -263,10 +263,10 @@ Forgejo or set your environment appropriately.`, "") lastline = 0 } } else { - fmt.Fprintf(out, ".") + fmt.Fprint(out, ".") } if lastline >= hookBatchSize { - fmt.Fprintf(out, "\n") + fmt.Fprint(out, "\n") lastline = 0 } } @@ -283,7 +283,7 @@ Forgejo or set your environment appropriately.`, "") return fail(ctx, extra.UserMsg, "HookPreReceive(last) failed: %v", extra.Error) } } else if lastline > 0 { - fmt.Fprintf(out, "\n") + fmt.Fprint(out, "\n") } fmt.Fprintf(out, "Checked %d references in total\n", total) @@ -399,7 +399,7 @@ Forgejo or set your environment appropriately.`, "") continue } - fmt.Fprintf(out, ".") + fmt.Fprint(out, ".") oldCommitIDs[count] = string(fields[0]) newCommitIDs[count] = string(fields[1]) refFullNames[count] = git.RefName(fields[2]) diff --git a/cmd/main_test.go b/cmd/main_test.go index 1ff71005e3..f2d393313a 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -4,6 +4,7 @@ package cmd import ( + "errors" "fmt" "io" "path/filepath" @@ -128,7 +129,7 @@ func TestCliCmd(t *testing.T) { } func TestCliCmdError(t *testing.T) { - app := newTestApp(func(ctx *cli.Context) error { return fmt.Errorf("normal error") }) + app := newTestApp(func(ctx *cli.Context) error { return errors.New("normal error") }) r, err := runTestApp(app, "./gitea", "test-cmd") require.Error(t, err) assert.Equal(t, 1, r.ExitCode) diff --git a/models/actions/run.go b/models/actions/run.go index 006f401053..48756b7a08 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -5,6 +5,7 @@ package actions import ( "context" + "errors" "fmt" "slices" "strings" @@ -355,7 +356,7 @@ func UpdateRunWithoutNotification(ctx context.Context, run *ActionRun, cols ...s return err } if affected == 0 { - return fmt.Errorf("run has changed") + return errors.New("run has changed") // It's impossible that the run is not found, since Gitea never deletes runs. } diff --git a/models/activities/action.go b/models/activities/action.go index f4be7d23e2..a309637d04 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -7,6 +7,7 @@ package activities import ( "context" + "errors" "fmt" "net/url" "path" @@ -458,7 +459,7 @@ type GetFeedsOptions struct { // GetFeeds returns actions according to the provided options func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, error) { if opts.RequestedUser == nil && opts.RequestedTeam == nil && opts.RequestedRepo == nil { - return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo") + return nil, 0, errors.New("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo") } cond, err := activityQueryCondition(ctx, opts) diff --git a/models/asymkey/gpg_key.go b/models/asymkey/gpg_key.go index b7e10ce85c..64866da076 100644 --- a/models/asymkey/gpg_key.go +++ b/models/asymkey/gpg_key.go @@ -5,6 +5,7 @@ package asymkey import ( "context" + "errors" "fmt" "strings" "time" @@ -209,7 +210,7 @@ func parseGPGKey(ctx context.Context, ownerID int64, e *openpgp.Entity, verified // deleteGPGKey does the actual key deletion func deleteGPGKey(ctx context.Context, keyID string) (int64, error) { if keyID == "" { - return 0, fmt.Errorf("empty KeyId forbidden") // Should never happen but just to be sure + return 0, errors.New("empty KeyId forbidden") // Should never happen but just to be sure } // Delete imported key n, err := db.GetEngine(ctx).Where("key_id=?", keyID).Delete(new(GPGKeyImport)) diff --git a/models/asymkey/gpg_key_common.go b/models/asymkey/gpg_key_common.go index db1912c316..5b8a22fe63 100644 --- a/models/asymkey/gpg_key_common.go +++ b/models/asymkey/gpg_key_common.go @@ -7,6 +7,7 @@ import ( "bytes" "crypto" "encoding/base64" + "errors" "fmt" "hash" "io" @@ -75,7 +76,7 @@ func base64DecPubKey(content string) (*packet.PublicKey, error) { // Check type pkey, ok := p.(*packet.PublicKey) if !ok { - return nil, fmt.Errorf("key is not a public key") + return nil, errors.New("key is not a public key") } return pkey, nil } @@ -122,15 +123,15 @@ func readArmoredSign(r io.Reader) (body io.Reader, err error) { func extractSignature(s string) (*packet.Signature, error) { r, err := readArmoredSign(strings.NewReader(s)) if err != nil { - return nil, fmt.Errorf("Failed to read signature armor") + return nil, errors.New("Failed to read signature armor") } p, err := packet.Read(r) if err != nil { - return nil, fmt.Errorf("Failed to read signature packet") + return nil, errors.New("Failed to read signature packet") } sig, ok := p.(*packet.Signature) if !ok { - return nil, fmt.Errorf("Packet is not a signature") + return nil, errors.New("Packet is not a signature") } return sig, nil } diff --git a/models/asymkey/gpg_key_object_verification.go b/models/asymkey/gpg_key_object_verification.go index ccd31a38b1..745ed04869 100644 --- a/models/asymkey/gpg_key_object_verification.go +++ b/models/asymkey/gpg_key_object_verification.go @@ -6,6 +6,7 @@ package asymkey import ( "context" + "errors" "fmt" "hash" "strings" @@ -316,7 +317,7 @@ func verifyWithGPGSettings(ctx context.Context, gpgSettings *git.GPGSettings, si func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error { // Check if key can sign if !k.CanSign { - return fmt.Errorf("key can not sign") + return errors.New("key can not sign") } // Decode key pkey, err := base64DecPubKey(k.Content) diff --git a/models/asymkey/ssh_key_parse.go b/models/asymkey/ssh_key_parse.go index 305e464b4b..7321553eb2 100644 --- a/models/asymkey/ssh_key_parse.go +++ b/models/asymkey/ssh_key_parse.go @@ -10,6 +10,7 @@ import ( "encoding/base64" "encoding/binary" "encoding/pem" + "errors" "fmt" "math/big" "os" @@ -93,7 +94,7 @@ func parseKeyString(content string) (string, error) { block, _ := pem.Decode([]byte(content)) if block == nil { - return "", fmt.Errorf("failed to parse PEM block containing the public key") + return "", errors.New("failed to parse PEM block containing the public key") } if strings.Contains(block.Type, "PRIVATE") { return "", ErrKeyIsPrivate diff --git a/models/forgefed/federationhost_test.go b/models/forgefed/federationhost_test.go index 824495c9cb..d11affbae0 100644 --- a/models/forgefed/federationhost_test.go +++ b/models/forgefed/federationhost_test.go @@ -35,7 +35,7 @@ func Test_FederationHostValidation(t *testing.T) { HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: HostFqdn empty") + t.Error("sut should be invalid: HostFqdn empty") } sut = FederationHost{ @@ -48,7 +48,7 @@ func Test_FederationHostValidation(t *testing.T) { HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: HostFqdn too long (len=256)") + t.Error("sut should be invalid: HostFqdn too long (len=256)") } sut = FederationHost{ @@ -59,7 +59,7 @@ func Test_FederationHostValidation(t *testing.T) { HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: NodeInfo invalid") + t.Error("sut should be invalid: NodeInfo invalid") } sut = FederationHost{ @@ -72,7 +72,7 @@ func Test_FederationHostValidation(t *testing.T) { HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: Future timestamp") + t.Error("sut should be invalid: Future timestamp") } sut = FederationHost{ @@ -85,6 +85,6 @@ func Test_FederationHostValidation(t *testing.T) { HostSchema: "https", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid: HostFqdn lower case") + t.Error("sut should be invalid: HostFqdn lower case") } } diff --git a/models/forgefed/nodeinfo_test.go b/models/forgefed/nodeinfo_test.go index 9e37e77100..a0c9781b90 100644 --- a/models/forgefed/nodeinfo_test.go +++ b/models/forgefed/nodeinfo_test.go @@ -4,7 +4,7 @@ package forgefed import ( - "fmt" + "errors" "reflect" "strings" "testing" @@ -28,7 +28,7 @@ func Test_NodeInfoWellKnownUnmarshalJSON(t *testing.T) { }, "empty": { item: []byte(``), - wantErr: fmt.Errorf("cannot parse JSON: cannot parse empty string; unparsed tail: \"\""), + wantErr: errors.New("cannot parse JSON: cannot parse empty string; unparsed tail: \"\""), }, } @@ -74,7 +74,7 @@ func Test_NewNodeInfoWellKnown(t *testing.T) { _, err := NewNodeInfoWellKnown([]byte(`invalid`)) if err == nil { - t.Errorf("error was expected here") + t.Error("error was expected here") } } @@ -87,6 +87,6 @@ func Test_NewNodeInfo(t *testing.T) { _, err := NewNodeInfo([]byte(`invalid`)) if err == nil { - t.Errorf("error was expected here") + t.Error("error was expected here") } } diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 73226c525f..d79bb1e455 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -5,6 +5,7 @@ package forgejo_migrations //nolint:revive import ( "context" + "errors" "fmt" "os" @@ -130,7 +131,7 @@ func EnsureUpToDate(x *xorm.Engine) error { } if currentDB < 0 { - return fmt.Errorf("database has not been initialized") + return errors.New("database has not been initialized") } expected := ExpectedVersion() diff --git a/models/issues/issue.go b/models/issues/issue.go index d6a0029638..5edebb4105 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -6,6 +6,7 @@ package issues import ( "context" + "errors" "fmt" "html/template" "regexp" @@ -804,7 +805,7 @@ func (issue *Issue) MovePin(ctx context.Context, newPosition int) error { } if newPosition < 1 { - return fmt.Errorf("The Position can't be lower than 1") + return errors.New("The Position can't be lower than 1") } dbctx, committer, err := db.TxContext(ctx) diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index d15533390e..aca9069205 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -6,6 +6,7 @@ package issues import ( "context" + "errors" "fmt" "strings" @@ -338,10 +339,10 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue } if opts.Issue.Index <= 0 { - return fmt.Errorf("no issue index provided") + return errors.New("no issue index provided") } if opts.Issue.ID > 0 { - return fmt.Errorf("issue exist") + return errors.New("issue exist") } opts.Issue.Created = timeutil.TimeStampNanoNow() diff --git a/models/issues/pull.go b/models/issues/pull.go index c46961447c..0781fd0a2d 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -6,6 +6,7 @@ package issues import ( "context" + "errors" "fmt" "io" "regexp" @@ -795,7 +796,7 @@ func (pr *PullRequest) GetWorkInProgressPrefix(ctx context.Context) string { // UpdateCommitDivergence update Divergence of a pull request func (pr *PullRequest) UpdateCommitDivergence(ctx context.Context, ahead, behind int) error { if pr.ID == 0 { - return fmt.Errorf("pull ID is 0") + return errors.New("pull ID is 0") } pr.CommitsAhead = ahead pr.CommitsBehind = behind diff --git a/models/issues/review.go b/models/issues/review.go index db5cd65e2e..584704d3e8 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -5,6 +5,7 @@ package issues import ( "context" + "errors" "fmt" "slices" "strings" @@ -349,7 +350,7 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error review.Type = ReviewTypeRequest review.ReviewerTeamID = opts.ReviewerTeam.ID } else { - return nil, fmt.Errorf("provide either reviewer or reviewer team") + return nil, errors.New("provide either reviewer or reviewer team") } if _, err := sess.Insert(review); err != nil { @@ -908,7 +909,7 @@ func MarkConversation(ctx context.Context, comment *Comment, doer *user_model.Us // the PR writer , offfcial reviewer and poster can do it func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.User) (permResult bool, err error) { if doer == nil || issue == nil { - return false, fmt.Errorf("issue or doer is nil") + return false, errors.New("issue or doer is nil") } if doer.ID != issue.PosterID { @@ -945,11 +946,11 @@ func DeleteReview(ctx context.Context, r *Review) error { defer committer.Close() if r.ID == 0 { - return fmt.Errorf("review is not allowed to be 0") + return errors.New("review is not allowed to be 0") } if r.Type == ReviewTypeRequest { - return fmt.Errorf("review request can not be deleted using this method") + return errors.New("review request can not be deleted using this method") } opts := FindCommentsOptions{ diff --git a/models/migrations/base/db.go b/models/migrations/base/db.go index 897ad016ab..7f7edda53b 100644 --- a/models/migrations/base/db.go +++ b/models/migrations/base/db.go @@ -74,7 +74,7 @@ func RecreateTable(sess *xorm.Session, bean any) error { } newTableColumns := table.Columns() if len(newTableColumns) == 0 { - return fmt.Errorf("no columns in new table") + return errors.New("no columns in new table") } hasID := false for _, column := range newTableColumns { diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index aea9b593bd..2a5b97f519 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -6,6 +6,7 @@ package migrations import ( "context" + "errors" "fmt" "forgejo.org/models/db" @@ -412,7 +413,7 @@ func EnsureUpToDate(x *xorm.Engine) error { } if currentDB < 0 { - return fmt.Errorf("database has not been initialized") + return errors.New("database has not been initialized") } if minDBVersion > currentDB { diff --git a/models/migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go index 60339962cb..ff584fff67 100644 --- a/models/migrations/v1_13/v151.go +++ b/models/migrations/v1_13/v151.go @@ -5,6 +5,7 @@ package v1_13 //nolint import ( "context" + "errors" "fmt" "strings" @@ -83,7 +84,7 @@ func SetDefaultPasswordToArgon2(x *xorm.Engine) error { newTableColumns := table.Columns() if len(newTableColumns) == 0 { - return fmt.Errorf("no columns in new table") + return errors.New("no columns in new table") } hasID := false for _, column := range newTableColumns { diff --git a/models/migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go index 9849d5a9ea..3fa27cfecd 100644 --- a/models/migrations/v1_14/v158.go +++ b/models/migrations/v1_14/v158.go @@ -4,7 +4,7 @@ package v1_14 //nolint import ( - "fmt" + "errors" "strconv" "forgejo.org/modules/log" @@ -72,7 +72,7 @@ func UpdateCodeCommentReplies(x *xorm.Engine) error { case setting.Database.Type.IsSQLite3(): sqlCmd = sqlSelect + sqlTail + " LIMIT " + strconv.Itoa(batchSize) + " OFFSET " + strconv.Itoa(start) default: - return fmt.Errorf("Unsupported database type") + return errors.New("Unsupported database type") } if err := sess.SQL(sqlCmd).Find(&comments); err != nil { diff --git a/models/migrations/v1_14/v177_test.go b/models/migrations/v1_14/v177_test.go index 199a71186a..bffc6f92e3 100644 --- a/models/migrations/v1_14/v177_test.go +++ b/models/migrations/v1_14/v177_test.go @@ -73,12 +73,12 @@ func Test_DeleteOrphanedIssueLabels(t *testing.T) { // Now test what is left if _, ok := postMigration[2]; ok { - t.Errorf("Orphaned Label[2] survived the migration") + t.Error("Orphaned Label[2] survived the migration") return } if _, ok := postMigration[5]; ok { - t.Errorf("Orphaned Label[5] survived the migration") + t.Error("Orphaned Label[5] survived the migration") return } diff --git a/models/migrations/v1_17/v222.go b/models/migrations/v1_17/v222.go index c9a33f007d..ae910cbcb6 100644 --- a/models/migrations/v1_17/v222.go +++ b/models/migrations/v1_17/v222.go @@ -5,6 +5,7 @@ package v1_17 //nolint import ( "context" + "errors" "fmt" "forgejo.org/models/migrations/base" @@ -29,7 +30,7 @@ func DropOldCredentialIDColumn(x *xorm.Engine) error { } if !credentialIDBytesExists { // looks like 221 hasn't properly run - return fmt.Errorf("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration") + return errors.New("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration") } // Create webauthnCredential table diff --git a/models/migrations/v1_21/v264.go b/models/migrations/v1_21/v264.go index 88eaf0d918..5615600072 100644 --- a/models/migrations/v1_21/v264.go +++ b/models/migrations/v1_21/v264.go @@ -5,7 +5,7 @@ package v1_21 //nolint import ( "context" - "fmt" + "errors" "forgejo.org/models/db" "forgejo.org/modules/timeutil" @@ -57,7 +57,7 @@ func AddBranchTable(x *xorm.Engine) error { if err != nil { return err } else if !has { - return fmt.Errorf("no admin user found") + return errors.New("no admin user found") } branches := make([]Branch, 0, 100) diff --git a/models/project/column.go b/models/project/column.go index 52917cb9fd..20de39357b 100644 --- a/models/project/column.go +++ b/models/project/column.go @@ -145,7 +145,7 @@ func NewColumn(ctx context.Context, column *Column) error { return err } if res.ColumnCount >= maxProjectColumns { - return fmt.Errorf("NewBoard: maximum number of columns reached") + return errors.New("NewBoard: maximum number of columns reached") } column.Sorting = int8(util.Iif(res.ColumnCount > 0, res.MaxSorting+1, 0)) _, err := db.GetEngine(ctx).Insert(column) @@ -170,7 +170,7 @@ func deleteColumnByID(ctx context.Context, columnID int64) error { } if column.Default { - return fmt.Errorf("deleteColumnByID: cannot delete default column") + return errors.New("deleteColumnByID: cannot delete default column") } // move all issues to the default column diff --git a/models/project/issue.go b/models/project/issue.go index 9e9db19004..d404033446 100644 --- a/models/project/issue.go +++ b/models/project/issue.go @@ -5,7 +5,7 @@ package project import ( "context" - "fmt" + "errors" "forgejo.org/models/db" "forgejo.org/modules/log" @@ -73,7 +73,7 @@ func MoveIssuesOnProjectColumn(ctx context.Context, column *Column, sortedIssueI return err } if int(count) != len(sortedIssueIDs) { - return fmt.Errorf("all issues have to be added to a project first") + return errors.New("all issues have to be added to a project first") } for sorting, issueID := range sortedIssueIDs { @@ -88,7 +88,7 @@ func MoveIssuesOnProjectColumn(ctx context.Context, column *Column, sortedIssueI func (c *Column) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Column) error { if c.ProjectID != newColumn.ProjectID { - return fmt.Errorf("columns have to be in the same project") + return errors.New("columns have to be in the same project") } if c.ID == newColumn.ID { diff --git a/models/repo/attachment.go b/models/repo/attachment.go index 3bf51e80ca..4a09ef14f2 100644 --- a/models/repo/attachment.go +++ b/models/repo/attachment.go @@ -5,6 +5,7 @@ package repo import ( "context" + "errors" "fmt" "net/url" "path" @@ -232,7 +233,7 @@ func DeleteAttachmentsByComment(ctx context.Context, commentID int64, remove boo // UpdateAttachmentByUUID Updates attachment via uuid func UpdateAttachmentByUUID(ctx context.Context, attach *Attachment, cols ...string) error { if attach.UUID == "" { - return fmt.Errorf("attachment uuid should be not blank") + return errors.New("attachment uuid should be not blank") } if attach.ExternalURL != "" && !validation.IsValidExternalURL(attach.ExternalURL) { return ErrInvalidExternalURL{ExternalURL: attach.ExternalURL} diff --git a/models/repo/following_repo_test.go b/models/repo/following_repo_test.go index cff125dabe..4bac23e77a 100644 --- a/models/repo/following_repo_test.go +++ b/models/repo/following_repo_test.go @@ -26,6 +26,6 @@ func Test_FollowingRepoValidation(t *testing.T) { URI: "http://localhost:3000/api/v1/activitypub/repo-id/1", } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid") + t.Error("sut should be invalid") } } diff --git a/models/repo/repo.go b/models/repo/repo.go index 9db1fd9039..6bf03d724c 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -6,6 +6,7 @@ package repo import ( "context" + "errors" "fmt" "html/template" "net" @@ -820,7 +821,7 @@ func GetRepositoryByURL(ctx context.Context, repoURL string) (*Repository, error pathSegments := getRepositoryURLPathSegments(repoURL) if len(pathSegments) != 2 { - return nil, fmt.Errorf("unknown or malformed repository URL") + return nil, errors.New("unknown or malformed repository URL") } ownerName := pathSegments[0] diff --git a/models/user/federated_user_test.go b/models/user/federated_user_test.go index 374236f6d3..542798c9bc 100644 --- a/models/user/federated_user_test.go +++ b/models/user/federated_user_test.go @@ -24,6 +24,6 @@ func Test_FederatedUserValidation(t *testing.T) { FederationHostID: 1, } if res, _ := validation.IsValid(sut); res { - t.Errorf("sut should be invalid") + t.Error("sut should be invalid") } } diff --git a/models/user/setting.go b/models/user/setting.go index a915119ad2..e547d24e9f 100644 --- a/models/user/setting.go +++ b/models/user/setting.go @@ -5,6 +5,7 @@ package user import ( "context" + "errors" "fmt" "strings" @@ -114,10 +115,10 @@ func GetUserAllSettings(ctx context.Context, uid int64) (map[string]*Setting, er func validateUserSettingKey(key string) error { if len(key) == 0 { - return fmt.Errorf("setting key must be set") + return errors.New("setting key must be set") } if strings.ToLower(key) != key { - return fmt.Errorf("setting key should be lowercase") + return errors.New("setting key should be lowercase") } return nil } diff --git a/modules/auth/openid/discovery_cache_test.go b/modules/auth/openid/discovery_cache_test.go index 3c59bd5ba5..77ce46c4d5 100644 --- a/modules/auth/openid/discovery_cache_test.go +++ b/modules/auth/openid/discovery_cache_test.go @@ -30,7 +30,7 @@ func TestTimedDiscoveryCache(t *testing.T) { // Make sure we can retrieve them if di := dc.Get("foo"); di == nil { - t.Errorf("Expected a result, got nil") + t.Error("Expected a result, got nil") } else if di.OpEndpoint() != "opEndpoint" || di.OpLocalID() != "opLocalID" || di.ClaimedID() != "claimedID" { t.Errorf("Expected opEndpoint opLocalID claimedID, got %v %v %v", di.OpEndpoint(), di.OpLocalID(), di.ClaimedID()) } @@ -44,6 +44,6 @@ func TestTimedDiscoveryCache(t *testing.T) { time.Sleep(100 * time.Millisecond) if di := dc.Get("foo"); di != nil { - t.Errorf("Expected a nil, got a result") + t.Error("Expected a nil, got a result") } } diff --git a/modules/avatar/identicon/identicon.go b/modules/avatar/identicon/identicon.go index 40471565d6..13e8ec88e6 100644 --- a/modules/avatar/identicon/identicon.go +++ b/modules/avatar/identicon/identicon.go @@ -8,6 +8,7 @@ package identicon import ( "crypto/sha256" + "errors" "fmt" "image" "image/color" @@ -29,7 +30,7 @@ type Identicon struct { // fore all possible foreground colors. only one foreground color will be picked randomly for one image func New(size int, back color.Color, fore ...color.Color) (*Identicon, error) { if len(fore) == 0 { - return nil, fmt.Errorf("foreground is not set") + return nil, errors.New("foreground is not set") } if size < minImageSize { diff --git a/modules/cache/cache.go b/modules/cache/cache.go index 9ad4b5cd90..aa69a0d3a7 100644 --- a/modules/cache/cache.go +++ b/modules/cache/cache.go @@ -4,6 +4,7 @@ package cache import ( + "errors" "fmt" "strconv" "time" @@ -48,7 +49,7 @@ const ( func Test() (time.Duration, error) { if conn == nil { - return 0, fmt.Errorf("default cache not initialized") + return 0, errors.New("default cache not initialized") } testData := fmt.Sprintf("%x", make([]byte, 500)) @@ -63,10 +64,10 @@ func Test() (time.Duration, error) { } testVal := conn.Get(testCacheKey) if testVal == nil { - return 0, fmt.Errorf("expect cache hit but got none") + return 0, errors.New("expect cache hit but got none") } if testVal != testData { - return 0, fmt.Errorf("expect cache to return same value as stored but got other") + return 0, errors.New("expect cache to return same value as stored but got other") } return time.Since(start), nil diff --git a/modules/cache/cache_test.go b/modules/cache/cache_test.go index f7bc400598..aa28c98452 100644 --- a/modules/cache/cache_test.go +++ b/modules/cache/cache_test.go @@ -4,7 +4,7 @@ package cache import ( - "fmt" + "errors" "testing" "time" @@ -45,7 +45,7 @@ func TestGetString(t *testing.T) { createTestCache() data, err := GetString("key", func() (string, error) { - return "", fmt.Errorf("some error") + return "", errors.New("some error") }) require.Error(t, err) assert.Empty(t, data) @@ -70,7 +70,7 @@ func TestGetString(t *testing.T) { assert.Equal(t, "some data", data) data, err = GetString("key", func() (string, error) { - return "", fmt.Errorf("some error") + return "", errors.New("some error") }) require.NoError(t, err) assert.Equal(t, "some data", data) @@ -81,7 +81,7 @@ func TestGetInt(t *testing.T) { createTestCache() data, err := GetInt("key", func() (int, error) { - return 0, fmt.Errorf("some error") + return 0, errors.New("some error") }) require.Error(t, err) assert.Equal(t, 0, data) @@ -106,7 +106,7 @@ func TestGetInt(t *testing.T) { assert.Equal(t, 100, data) data, err = GetInt("key", func() (int, error) { - return 0, fmt.Errorf("some error") + return 0, errors.New("some error") }) require.NoError(t, err) assert.Equal(t, 100, data) @@ -117,7 +117,7 @@ func TestGetInt64(t *testing.T) { createTestCache() data, err := GetInt64("key", func() (int64, error) { - return 0, fmt.Errorf("some error") + return 0, errors.New("some error") }) require.Error(t, err) assert.EqualValues(t, 0, data) @@ -142,7 +142,7 @@ func TestGetInt64(t *testing.T) { assert.EqualValues(t, 100, data) data, err = GetInt64("key", func() (int64, error) { - return 0, fmt.Errorf("some error") + return 0, errors.New("some error") }) require.NoError(t, err) assert.EqualValues(t, 100, data) diff --git a/modules/emoji/emoji_test.go b/modules/emoji/emoji_test.go index 2526cd121e..ad3db10fb4 100644 --- a/modules/emoji/emoji_test.go +++ b/modules/emoji/emoji_test.go @@ -23,16 +23,16 @@ func TestLookup(t *testing.T) { d := FromAlias("beer") if !reflect.DeepEqual(a, b) { - t.Errorf("a and b should equal") + t.Error("a and b should equal") } if !reflect.DeepEqual(b, c) { - t.Errorf("b and c should equal") + t.Error("b and c should equal") } if !reflect.DeepEqual(c, d) { - t.Errorf("c and d should equal") + t.Error("c and d should equal") } if !reflect.DeepEqual(a, d) { - t.Errorf("a and d should equal") + t.Error("a and d should equal") } m := FromCode("\U0001f44d") @@ -40,13 +40,13 @@ func TestLookup(t *testing.T) { o := FromAlias("+1") if !reflect.DeepEqual(m, n) { - t.Errorf("m and n should equal") + t.Error("m and n should equal") } if !reflect.DeepEqual(n, o) { - t.Errorf("n and o should equal") + t.Error("n and o should equal") } if !reflect.DeepEqual(m, o) { - t.Errorf("m and o should equal") + t.Error("m and o should equal") } } diff --git a/modules/forgefed/activity_like_test.go b/modules/forgefed/activity_like_test.go index 815b0e02f3..f4cb7e4e00 100644 --- a/modules/forgefed/activity_like_test.go +++ b/modules/forgefed/activity_like_test.go @@ -4,7 +4,7 @@ package forgefed import ( - "fmt" + "errors" "reflect" "strings" "testing" @@ -99,7 +99,7 @@ func Test_LikeUnmarshalJSON(t *testing.T) { "invalid": { item: []byte(`{"type":"Invalid","actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1","object":"https://codeberg.org/api/activitypub/repository-id/1"`), want: &ForgeLike{}, - wantErr: fmt.Errorf("cannot parse JSON"), + wantErr: errors.New("cannot parse JSON"), }, } diff --git a/modules/forgefed/activity_undo_like_test.go b/modules/forgefed/activity_undo_like_test.go index 1b77369b67..5867a84e7b 100644 --- a/modules/forgefed/activity_undo_like_test.go +++ b/modules/forgefed/activity_undo_like_test.go @@ -4,7 +4,7 @@ package forgefed import ( - "fmt" + "errors" "reflect" "strings" "testing" @@ -125,7 +125,7 @@ func Test_UndoLikeUnmarshalJSON(t *testing.T) { "invalid": { item: []byte(`invalid JSON`), want: nil, - wantErr: fmt.Errorf("cannot parse JSON"), + wantErr: errors.New("cannot parse JSON"), }, } diff --git a/modules/forgefed/actor_test.go b/modules/forgefed/actor_test.go index 5315d0b4de..10b6578953 100644 --- a/modules/forgefed/actor_test.go +++ b/modules/forgefed/actor_test.go @@ -166,31 +166,31 @@ func TestShouldThrowErrorOnInvalidInput(t *testing.T) { var err any _, err = NewPersonID("", "forgejo") if err == nil { - t.Errorf("empty input should be invalid.") + t.Error("empty input should be invalid.") } _, err = NewPersonID("http://localhost:3000/api/v1/something", "forgejo") if err == nil { - t.Errorf("localhost uris are not external") + t.Error("localhost uris are not external") } _, err = NewPersonID("./api/v1/something", "forgejo") if err == nil { - t.Errorf("relative uris are not allowed") + t.Error("relative uris are not allowed") } _, err = NewPersonID("http://1.2.3.4/api/v1/something", "forgejo") if err == nil { - t.Errorf("uri may not be ip-4 based") + t.Error("uri may not be ip-4 based") } _, err = NewPersonID("http:///[fe80::1ff:fe23:4567:890a%25eth0]/api/v1/something", "forgejo") if err == nil { - t.Errorf("uri may not be ip-6 based") + t.Error("uri may not be ip-6 based") } _, err = NewPersonID("https://codeberg.org/api/v1/activitypub/../activitypub/user-id/12345", "forgejo") if err == nil { - t.Errorf("uri may not contain relative path elements") + t.Error("uri may not contain relative path elements") } _, err = NewPersonID("https://myuser@an.other.host/api/v1/activitypub/user-id/1", "forgejo") if err == nil { - t.Errorf("uri may not contain unparsed elements") + t.Error("uri may not contain unparsed elements") } _, err = NewPersonID("https://an.other.host/api/v1/activitypub/user-id/1", "forgejo") if err != nil { diff --git a/modules/git/commit.go b/modules/git/commit.go index a8ebed4968..96831e3ae4 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -9,7 +9,6 @@ import ( "bytes" "context" "errors" - "fmt" "io" "os/exec" "strconv" @@ -386,7 +385,7 @@ func parseSubmoduleContent(bs []byte) (*ObjectCache, error) { } submoduleCache := newObjectCache() if len(cfg.Submodules) == 0 { - return nil, fmt.Errorf("no submodules found") + return nil, errors.New("no submodules found") } for _, subModule := range cfg.Submodules { submoduleCache.Set(subModule.Path, subModule.URL) diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go index 2154467332..2b07513162 100644 --- a/modules/git/repo_attribute.go +++ b/modules/git/repo_attribute.go @@ -7,6 +7,7 @@ import ( "bufio" "bytes" "context" + "errors" "fmt" "io" "os" @@ -116,7 +117,7 @@ func (ca GitAttribute) Bool() optional.Option[bool] { // instantiation. func (repo *Repository) gitCheckAttrCommand(treeish string, attributes ...string) (*Command, *RunOpts, context.CancelFunc, error) { if len(attributes) == 0 { - return nil, nil, nil, fmt.Errorf("no provided attributes to check-attr") + return nil, nil, nil, errors.New("no provided attributes to check-attr") } env := os.Environ() diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go index 1992060351..3a9aa3e4e6 100644 --- a/modules/git/repo_branch.go +++ b/modules/git/repo_branch.go @@ -41,7 +41,7 @@ type Branch struct { // GetHEADBranch returns corresponding branch of HEAD. func (repo *Repository) GetHEADBranch() (*Branch, error) { if repo == nil { - return nil, fmt.Errorf("nil repo") + return nil, errors.New("nil repo") } stdout, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").RunStdString(&RunOpts{Dir: repo.Path}) if err != nil { diff --git a/modules/indexer/code/internal/indexer.go b/modules/indexer/code/internal/indexer.go index cc2c2aaf06..73662b1dda 100644 --- a/modules/indexer/code/internal/indexer.go +++ b/modules/indexer/code/internal/indexer.go @@ -5,7 +5,7 @@ package internal import ( "context" - "fmt" + "errors" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" @@ -57,13 +57,13 @@ type dummyIndexer struct { } func (d *dummyIndexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *RepoChanges) error { - return fmt.Errorf("indexer is not ready") + return errors.New("indexer is not ready") } func (d *dummyIndexer) Delete(ctx context.Context, repoID int64) error { - return fmt.Errorf("indexer is not ready") + return errors.New("indexer is not ready") } func (d *dummyIndexer) Search(ctx context.Context, opts *SearchOptions) (int64, []*SearchResult, []*SearchResultLanguages, error) { - return 0, nil, nil, fmt.Errorf("indexer is not ready") + return 0, nil, nil, errors.New("indexer is not ready") } diff --git a/modules/indexer/internal/bleve/indexer.go b/modules/indexer/internal/bleve/indexer.go index 4a61a80765..669417bb4b 100644 --- a/modules/indexer/internal/bleve/indexer.go +++ b/modules/indexer/internal/bleve/indexer.go @@ -5,7 +5,7 @@ package bleve import ( "context" - "fmt" + "errors" "forgejo.org/modules/indexer/internal" "forgejo.org/modules/log" @@ -38,11 +38,11 @@ func NewIndexer(indexDir string, version int, mappingGetter func() (mapping.Inde // Init initializes the indexer func (i *Indexer) Init(_ context.Context) (bool, error) { if i == nil { - return false, fmt.Errorf("cannot init nil indexer") + return false, errors.New("cannot init nil indexer") } if i.Indexer != nil { - return false, fmt.Errorf("indexer is already initialized") + return false, errors.New("indexer is already initialized") } indexer, version, err := openIndexer(i.indexDir, i.version) @@ -82,10 +82,10 @@ func (i *Indexer) Init(_ context.Context) (bool, error) { // Ping checks if the indexer is available func (i *Indexer) Ping(_ context.Context) error { if i == nil { - return fmt.Errorf("cannot ping nil indexer") + return errors.New("cannot ping nil indexer") } if i.Indexer == nil { - return fmt.Errorf("indexer is not initialized") + return errors.New("indexer is not initialized") } return nil } diff --git a/modules/indexer/internal/elasticsearch/indexer.go b/modules/indexer/internal/elasticsearch/indexer.go index 9cd29f3e49..a7a710588c 100644 --- a/modules/indexer/internal/elasticsearch/indexer.go +++ b/modules/indexer/internal/elasticsearch/indexer.go @@ -5,6 +5,7 @@ package elasticsearch import ( "context" + "errors" "fmt" "forgejo.org/modules/indexer/internal" @@ -36,10 +37,10 @@ func NewIndexer(url, indexName string, version int, mapping string) *Indexer { // Init initializes the indexer func (i *Indexer) Init(ctx context.Context) (bool, error) { if i == nil { - return false, fmt.Errorf("cannot init nil indexer") + return false, errors.New("cannot init nil indexer") } if i.Client != nil { - return false, fmt.Errorf("indexer is already initialized") + return false, errors.New("indexer is already initialized") } client, err := i.initClient() @@ -66,10 +67,10 @@ func (i *Indexer) Init(ctx context.Context) (bool, error) { // Ping checks if the indexer is available func (i *Indexer) Ping(ctx context.Context) error { if i == nil { - return fmt.Errorf("cannot ping nil indexer") + return errors.New("cannot ping nil indexer") } if i.Client == nil { - return fmt.Errorf("indexer is not initialized") + return errors.New("indexer is not initialized") } resp, err := i.Client.ClusterHealth().Do(ctx) diff --git a/modules/indexer/internal/indexer.go b/modules/indexer/internal/indexer.go index c7f356da1e..3442bbaff2 100644 --- a/modules/indexer/internal/indexer.go +++ b/modules/indexer/internal/indexer.go @@ -5,7 +5,7 @@ package internal import ( "context" - "fmt" + "errors" ) // Indexer defines an basic indexer interface @@ -27,11 +27,11 @@ func NewDummyIndexer() Indexer { type dummyIndexer struct{} func (d *dummyIndexer) Init(ctx context.Context) (bool, error) { - return false, fmt.Errorf("indexer is not ready") + return false, errors.New("indexer is not ready") } func (d *dummyIndexer) Ping(ctx context.Context) error { - return fmt.Errorf("indexer is not ready") + return errors.New("indexer is not ready") } func (d *dummyIndexer) Close() {} diff --git a/modules/indexer/internal/meilisearch/indexer.go b/modules/indexer/internal/meilisearch/indexer.go index feac1d052c..b21605772c 100644 --- a/modules/indexer/internal/meilisearch/indexer.go +++ b/modules/indexer/internal/meilisearch/indexer.go @@ -5,6 +5,7 @@ package meilisearch import ( "context" + "errors" "fmt" "github.com/meilisearch/meilisearch-go" @@ -33,11 +34,11 @@ func NewIndexer(url, apiKey, indexName string, version int, settings *meilisearc // Init initializes the indexer func (i *Indexer) Init(_ context.Context) (bool, error) { if i == nil { - return false, fmt.Errorf("cannot init nil indexer") + return false, errors.New("cannot init nil indexer") } if i.Client != nil { - return false, fmt.Errorf("indexer is already initialized") + return false, errors.New("indexer is already initialized") } i.Client = meilisearch.New(i.url, meilisearch.WithAPIKey(i.apiKey)) @@ -63,10 +64,10 @@ func (i *Indexer) Init(_ context.Context) (bool, error) { // Ping checks if the indexer is available func (i *Indexer) Ping(ctx context.Context) error { if i == nil { - return fmt.Errorf("cannot ping nil indexer") + return errors.New("cannot ping nil indexer") } if i.Client == nil { - return fmt.Errorf("indexer is not initialized") + return errors.New("indexer is not initialized") } resp, err := i.Client.Health() if err != nil { diff --git a/modules/indexer/issues/internal/indexer.go b/modules/indexer/issues/internal/indexer.go index 2f3b4029dc..1b9428b889 100644 --- a/modules/indexer/issues/internal/indexer.go +++ b/modules/indexer/issues/internal/indexer.go @@ -5,7 +5,7 @@ package internal import ( "context" - "fmt" + "errors" "forgejo.org/modules/indexer/internal" ) @@ -30,13 +30,13 @@ type dummyIndexer struct { } func (d *dummyIndexer) Index(_ context.Context, _ ...*IndexerData) error { - return fmt.Errorf("indexer is not ready") + return errors.New("indexer is not ready") } func (d *dummyIndexer) Delete(_ context.Context, _ ...int64) error { - return fmt.Errorf("indexer is not ready") + return errors.New("indexer is not ready") } func (d *dummyIndexer) Search(_ context.Context, _ *SearchOptions) (*SearchResult, error) { - return nil, fmt.Errorf("indexer is not ready") + return nil, errors.New("indexer is not ready") } diff --git a/modules/indexer/stats/queue.go b/modules/indexer/stats/queue.go index 2403eb8dca..e4eac70d95 100644 --- a/modules/indexer/stats/queue.go +++ b/modules/indexer/stats/queue.go @@ -4,7 +4,7 @@ package stats import ( - "fmt" + "errors" repo_model "forgejo.org/models/repo" "forgejo.org/modules/graceful" @@ -31,7 +31,7 @@ func handler(items ...int64) []int64 { func initStatsQueue() error { statsQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "repo_stats_update", handler) if statsQueue == nil { - return fmt.Errorf("unable to create repo_stats_update queue") + return errors.New("unable to create repo_stats_update queue") } go graceful.GetManager().RunWithCancel(statsQueue) return nil diff --git a/modules/issue/template/template.go b/modules/issue/template/template.go index 859d27cda6..08c1b21c26 100644 --- a/modules/issue/template/template.go +++ b/modules/issue/template/template.go @@ -4,6 +4,7 @@ package template import ( + "errors" "fmt" "net/url" "regexp" @@ -31,17 +32,17 @@ func Validate(template *api.IssueTemplate) error { func validateMetadata(template *api.IssueTemplate) error { if strings.TrimSpace(template.Name) == "" { - return fmt.Errorf("'name' is required") + return errors.New("'name' is required") } if strings.TrimSpace(template.About) == "" { - return fmt.Errorf("'about' is required") + return errors.New("'about' is required") } return nil } func validateYaml(template *api.IssueTemplate) error { if len(template.Fields) == 0 { - return fmt.Errorf("'body' is required") + return errors.New("'body' is required") } ids := make(container.Set[string]) for idx, field := range template.Fields { diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index db92631acc..717da464b9 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -5,7 +5,7 @@ package markdown import ( - "fmt" + "errors" "html/template" "io" "strings" @@ -54,7 +54,7 @@ func (l *limitWriter) Write(data []byte) (int, error) { if err != nil { return n, err } - return n, fmt.Errorf("rendered content too large - truncating render") + return n, errors.New("rendered content too large - truncating render") } n, err := l.w.Write(data) l.sum += int64(n) diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go index e93b21abda..19f3b9008a 100644 --- a/modules/setting/config_provider.go +++ b/modules/setting/config_provider.go @@ -262,7 +262,7 @@ func (p *iniConfigProvider) Save() error { } filename := p.file if filename == "" { - return fmt.Errorf("config file path must not be empty") + return errors.New("config file path must not be empty") } if p.loadedFromEmpty { if err := os.MkdirAll(filepath.Dir(filename), os.ModePerm); err != nil { diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index 502be159a1..e592220de6 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -4,6 +4,7 @@ package setting import ( + "errors" "fmt" "net/mail" "strings" @@ -68,7 +69,7 @@ func checkReplyToAddress() error { } if parsed.Name != "" { - return fmt.Errorf("name must not be set") + return errors.New("name must not be set") } c := strings.Count(IncomingEmail.ReplyToAddress, IncomingEmail.TokenPlaceholder) diff --git a/modules/templates/util_dict.go b/modules/templates/util_dict.go index 9d9af77fad..16f722e61b 100644 --- a/modules/templates/util_dict.go +++ b/modules/templates/util_dict.go @@ -4,6 +4,7 @@ package templates import ( + "errors" "fmt" "html" "html/template" @@ -33,7 +34,7 @@ func dictMerge(base map[string]any, arg any) bool { // The dot syntax is highly discouraged because it might cause unclear key conflicts. It's always good to use explicit keys. func dict(args ...any) (map[string]any, error) { if len(args)%2 != 0 { - return nil, fmt.Errorf("invalid dict constructor syntax: must have key-value pairs") + return nil, errors.New("invalid dict constructor syntax: must have key-value pairs") } m := make(map[string]any, len(args)/2) for i := 0; i < len(args); i += 2 { diff --git a/modules/validation/validatable_test.go b/modules/validation/validatable_test.go index 0802d5cc92..c843afe702 100644 --- a/modules/validation/validatable_test.go +++ b/modules/validation/validatable_test.go @@ -38,7 +38,7 @@ func Test_IsValid(t *testing.T) { func Test_ValidateNotEmpty_ForString(t *testing.T) { sut := "" if len(ValidateNotEmpty(sut, "dummyField")) == 0 { - t.Errorf("sut should be invalid") + t.Error("sut should be invalid") } sut = "not empty" if res := ValidateNotEmpty(sut, "dummyField"); len(res) > 0 { @@ -49,7 +49,7 @@ func Test_ValidateNotEmpty_ForString(t *testing.T) { func Test_ValidateNotEmpty_ForTimestamp(t *testing.T) { sut := timeutil.TimeStamp(0) if res := ValidateNotEmpty(sut, "dummyField"); len(res) == 0 { - t.Errorf("sut should be invalid") + t.Error("sut should be invalid") } sut = timeutil.TimeStampNow() if res := ValidateNotEmpty(sut, "dummyField"); len(res) > 0 { @@ -60,7 +60,7 @@ func Test_ValidateNotEmpty_ForTimestamp(t *testing.T) { func Test_ValidateMaxLen(t *testing.T) { sut := "0123456789" if len(ValidateMaxLen(sut, 9, "dummyField")) == 0 { - t.Errorf("sut should be invalid") + t.Error("sut should be invalid") } sut = "0123456789" if res := ValidateMaxLen(sut, 11, "dummyField"); len(res) > 0 { diff --git a/routers/api/actions/artifacts_chunks.go b/routers/api/actions/artifacts_chunks.go index a15fa4fd1e..c0af750d7b 100644 --- a/routers/api/actions/artifacts_chunks.go +++ b/routers/api/actions/artifacts_chunks.go @@ -51,11 +51,11 @@ func saveUploadChunkBase(st storage.ObjectStorage, ctx *ArtifactContext, log.Info("[artifact] check chunk md5, sum: %s, header: %s", chunkMd5String, reqMd5String) // if md5 not match, delete the chunk if reqMd5String != chunkMd5String { - checkErr = fmt.Errorf("md5 not match") + checkErr = errors.New("md5 not match") } } if writtenSize != contentSize { - checkErr = errors.Join(checkErr, fmt.Errorf("contentSize not match body size")) + checkErr = errors.Join(checkErr, errors.New("contentSize not match body size")) } if checkErr != nil { if err := st.Delete(storagePath); err != nil { @@ -261,7 +261,7 @@ func mergeChunksForArtifact(ctx *ArtifactContext, chunks []*chunkFileItem, st st return fmt.Errorf("save merged file error: %v", err) } if written != artifact.FileCompressedSize { - return fmt.Errorf("merged file size is not equal to chunk length") + return errors.New("merged file size is not equal to chunk length") } defer func() { diff --git a/routers/api/packages/chef/auth.go b/routers/api/packages/chef/auth.go index bbd5ce860f..7263cf13bb 100644 --- a/routers/api/packages/chef/auth.go +++ b/routers/api/packages/chef/auth.go @@ -12,6 +12,7 @@ import ( "crypto/x509" "encoding/base64" "encoding/pem" + "errors" "fmt" "hash" "math/big" @@ -121,7 +122,7 @@ func verifyTimestamp(req *http.Request) error { } if diff > maxTimeDifference { - return fmt.Errorf("time difference") + return errors.New("time difference") } return nil @@ -190,7 +191,7 @@ func getAuthorizationData(req *http.Request) ([]byte, error) { tmp := make([]string, len(valueList)) for k, v := range valueList { if k > len(tmp) { - return nil, fmt.Errorf("invalid X-Ops-Authorization headers") + return nil, errors.New("invalid X-Ops-Authorization headers") } tmp[k-1] = v } @@ -267,7 +268,7 @@ func verifyDataOld(signature, data []byte, pub *rsa.PublicKey) error { } if !slices.Equal(out[skip:], data) { - return fmt.Errorf("could not verify signature") + return errors.New("could not verify signature") } return nil diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index 2d9cd3b1bb..b84fbe05fa 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -8,6 +8,7 @@ import ( "crypto/x509" "database/sql" "encoding/pem" + "errors" "fmt" "net/http" "net/url" @@ -29,7 +30,7 @@ import ( 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") + return nil, errors.New("could not decode publicKeyPem to PUBLIC KEY pem block type") } return block.Bytes, nil diff --git a/routers/api/v1/admin/quota_rule.go b/routers/api/v1/admin/quota_rule.go index ea188107fa..c2bc6843e4 100644 --- a/routers/api/v1/admin/quota_rule.go +++ b/routers/api/v1/admin/quota_rule.go @@ -4,7 +4,7 @@ package admin import ( - "fmt" + "errors" "net/http" quota_model "forgejo.org/models/quota" @@ -83,7 +83,7 @@ func CreateQuotaRule(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.CreateQuotaRuleOptions) if form.Limit == nil { - ctx.Error(http.StatusUnprocessableEntity, "quota_model.ParseLimitSubject", fmt.Errorf("[Limit]: Required")) + ctx.Error(http.StatusUnprocessableEntity, "quota_model.ParseLimitSubject", errors.New("[Limit]: Required")) return } diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 3b0a4410ed..8aa67b3b0a 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -196,7 +196,7 @@ func EditUser(ctx *context.APIContext) { // If either LoginSource or LoginName is given, the other must be present too. if form.SourceID != nil || form.LoginName != nil { if form.SourceID == nil || form.LoginName == nil { - ctx.Error(http.StatusUnprocessableEntity, "LoginSourceAndLoginName", fmt.Errorf("source_id and login_name must be specified together")) + ctx.Error(http.StatusUnprocessableEntity, "LoginSourceAndLoginName", errors.New("source_id and login_name must be specified together")) return } } @@ -304,7 +304,7 @@ func DeleteUser(ctx *context.APIContext) { // admin should not delete themself if ctx.ContextUser.ID == ctx.Doer.ID { - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("you cannot delete yourself")) + ctx.Error(http.StatusUnprocessableEntity, "", errors.New("you cannot delete yourself")) return } diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 7474df4fd9..7c9593d625 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -6,7 +6,6 @@ package repo import ( "errors" - "fmt" "net/http" "forgejo.org/models" @@ -151,7 +150,7 @@ func DeleteBranch(ctx *context.APIContext) { } if ctx.Repo.Repository.IsMirror { - ctx.Error(http.StatusForbidden, "IsMirrored", fmt.Errorf("can not delete branch of an mirror repository")) + ctx.Error(http.StatusForbidden, "IsMirrored", errors.New("can not delete branch of an mirror repository")) return } @@ -160,9 +159,9 @@ func DeleteBranch(ctx *context.APIContext) { case git.IsErrBranchNotExist(err): ctx.NotFound(err) case errors.Is(err, repo_service.ErrBranchIsDefault): - ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("can not delete default branch")) + ctx.Error(http.StatusForbidden, "DefaultBranch", errors.New("can not delete default branch")) case errors.Is(err, git_model.ErrBranchIsProtected): - ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("branch protected")) + ctx.Error(http.StatusForbidden, "IsProtectedBranch", errors.New("branch protected")) default: ctx.Error(http.StatusInternalServerError, "DeleteBranch", err) } diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index dd1d7b3a9a..3408f88dd1 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -692,7 +692,7 @@ func UpdateFile(ctx *context.APIContext) { // "$ref": "#/responses/repoArchivedError" apiOpts := web.GetForm(ctx).(*api.UpdateFileOptions) if ctx.Repo.Repository.IsEmpty { - ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty")) + ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", errors.New("repo is empty")) return } diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 85af1149ff..3b2935305c 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -5,7 +5,7 @@ package repo import ( - "fmt" + "errors" "net/http" "reflect" @@ -352,12 +352,12 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) labelNames = append(labelNames, rv.String()) default: ctx.Error(http.StatusBadRequest, "InvalidLabel", "a label must be an integer or a string") - return nil, nil, fmt.Errorf("invalid label") + return nil, nil, errors.New("invalid label") } } if len(labelIDs) > 0 && len(labelNames) > 0 { ctx.Error(http.StatusBadRequest, "InvalidLabels", "labels should be an array of strings or integers") - return nil, nil, fmt.Errorf("invalid labels") + return nil, nil, errors.New("invalid labels") } if len(labelNames) > 0 { repoLabelIDs, err := issues_model.GetLabelIDsInRepoByNames(ctx, ctx.Repo.Repository.ID, labelNames) diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index 7d88b1b2cd..61875b577c 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -4,6 +4,7 @@ package repo import ( + "errors" "fmt" "net/http" "time" @@ -116,7 +117,7 @@ func ListTrackedTimes(ctx *context.APIContext) { if opts.UserID == 0 { opts.UserID = ctx.Doer.ID } else { - ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights")) + ctx.Error(http.StatusForbidden, "", errors.New("query by user not allowed; not enough rights")) return } } @@ -437,7 +438,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) { } if !ctx.IsUserRepoAdmin() && !ctx.Doer.IsAdmin && ctx.Doer.ID != user.ID { - ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights")) + ctx.Error(http.StatusForbidden, "", errors.New("query by user not allowed; not enough rights")) return } @@ -545,7 +546,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { if opts.UserID == 0 { opts.UserID = ctx.Doer.ID } else { - ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights")) + ctx.Error(http.StatusForbidden, "", errors.New("query by user not allowed; not enough rights")) return } } diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index d0ab5e270e..4ee7aa58e4 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -123,12 +123,12 @@ func Migrate(ctx *context.APIContext) { gitServiceType := convert.ToGitServiceType(form.Service) if form.Mirror && setting.Mirror.DisableNewPull { - ctx.Error(http.StatusForbidden, "MirrorsGlobalDisabled", fmt.Errorf("the site administrator has disabled the creation of new pull mirrors")) + ctx.Error(http.StatusForbidden, "MirrorsGlobalDisabled", errors.New("the site administrator has disabled the creation of new pull mirrors")) return } if setting.Repository.DisableMigrations { - ctx.Error(http.StatusForbidden, "MigrationsGlobalDisabled", fmt.Errorf("the site administrator has disabled migrations")) + ctx.Error(http.StatusForbidden, "MigrationsGlobalDisabled", errors.New("the site administrator has disabled migrations")) return } diff --git a/routers/api/v1/repo/notes.go b/routers/api/v1/repo/notes.go index 87903d9f36..f3ceeaeacf 100644 --- a/routers/api/v1/repo/notes.go +++ b/routers/api/v1/repo/notes.go @@ -4,6 +4,7 @@ package repo import ( + "errors" "fmt" "net/http" @@ -63,7 +64,7 @@ func GetNote(ctx *context.APIContext) { func getNote(ctx *context.APIContext, identifier string) { if ctx.Repo.GitRepo == nil { - ctx.InternalServerError(fmt.Errorf("no open git repo")) + ctx.InternalServerError(errors.New("no open git repo")) return } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index b0bc7be90f..75b4870e51 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1050,11 +1050,11 @@ func MergePullRequest(ctx *context.APIContext) { if err := repo_service.DeleteBranchAfterMerge(ctx, ctx.Doer, pr, headRepo); err != nil { switch { case errors.Is(err, repo_service.ErrBranchIsDefault): - ctx.Error(http.StatusForbidden, "DefaultBranch", fmt.Errorf("the head branch is the default branch")) + ctx.Error(http.StatusForbidden, "DefaultBranch", errors.New("the head branch is the default branch")) case errors.Is(err, git_model.ErrBranchIsProtected): - ctx.Error(http.StatusForbidden, "IsProtectedBranch", fmt.Errorf("the head branch is protected")) + ctx.Error(http.StatusForbidden, "IsProtectedBranch", errors.New("the head branch is protected")) case errors.Is(err, util.ErrPermissionDenied): - ctx.Error(http.StatusForbidden, "HeadBranch", fmt.Errorf("insufficient permission to delete head branch")) + ctx.Error(http.StatusForbidden, "HeadBranch", errors.New("insufficient permission to delete head branch")) default: ctx.Error(http.StatusInternalServerError, "DeleteBranchAfterMerge", err) } diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index aa4c7318a2..830a62bf54 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -4,6 +4,7 @@ package repo import ( + "errors" "fmt" "net/http" "strings" @@ -581,7 +582,7 @@ func SubmitPullReview(ctx *context.APIContext) { } if review.Type != issues_model.ReviewTypePending { - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("only a pending review can be submitted")) + ctx.Error(http.StatusUnprocessableEntity, "", errors.New("only a pending review can be submitted")) return } @@ -593,7 +594,7 @@ func SubmitPullReview(ctx *context.APIContext) { // if review stay pending return if reviewType == issues_model.ReviewTypePending { - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("review stay pending")) + ctx.Error(http.StatusUnprocessableEntity, "", errors.New("review stay pending")) return } @@ -634,7 +635,7 @@ func preparePullReviewType(ctx *context.APIContext, pr *issues_model.PullRequest case api.ReviewStateApproved: // can not approve your own PR if pr.Issue.IsPoster(ctx.Doer.ID) { - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("approve your own pull is not allowed")) + ctx.Error(http.StatusUnprocessableEntity, "", errors.New("approve your own pull is not allowed")) return -1, true } reviewType = issues_model.ReviewTypeApprove @@ -643,7 +644,7 @@ func preparePullReviewType(ctx *context.APIContext, pr *issues_model.PullRequest case api.ReviewStateRequestChanges: // can not reject your own PR if pr.Issue.IsPoster(ctx.Doer.ID) { - ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("reject your own pull is not allowed")) + ctx.Error(http.StatusUnprocessableEntity, "", errors.New("reject your own pull is not allowed")) return -1, true } reviewType = issues_model.ReviewTypeReject diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 336741b932..0bf958b523 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -4,6 +4,7 @@ package repo import ( + "errors" "fmt" "net/http" @@ -226,7 +227,7 @@ func CreateRelease(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.CreateReleaseOption) if ctx.Repo.Repository.IsEmpty { - ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty")) + ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", errors.New("repo is empty")) return } rel, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, form.TagName) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 50f628e035..63100bca06 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -5,6 +5,7 @@ package repo import ( + "errors" "fmt" "net/http" "slices" @@ -723,7 +724,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err visibilityChanged = repo.IsPrivate != *opts.Private // when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public if visibilityChanged && setting.Repository.ForcePrivate && !*opts.Private && !ctx.Doer.IsAdmin { - err := fmt.Errorf("cannot change private repository to public") + err := errors.New("cannot change private repository to public") ctx.Error(http.StatusUnprocessableEntity, "Force Private enabled", err) return err } @@ -794,12 +795,12 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if newHasIssues && opts.ExternalTracker != nil && !unit_model.TypeExternalTracker.UnitGlobalDisabled() { // Check that values are valid if !validation.IsValidExternalURL(opts.ExternalTracker.ExternalTrackerURL) { - err := fmt.Errorf("External tracker URL not valid") + err := errors.New("External tracker URL not valid") ctx.Error(http.StatusUnprocessableEntity, "Invalid external tracker URL", err) return err } if len(opts.ExternalTracker.ExternalTrackerFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(opts.ExternalTracker.ExternalTrackerFormat) { - err := fmt.Errorf("External tracker URL format not valid") + err := errors.New("External tracker URL format not valid") ctx.Error(http.StatusUnprocessableEntity, "Invalid external tracker URL format", err) return err } @@ -870,7 +871,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if newHasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() { // Check that values are valid if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) { - err := fmt.Errorf("External wiki URL not valid") + err := errors.New("External wiki URL not valid") ctx.Error(http.StatusUnprocessableEntity, "", "Invalid external wiki URL") return err } @@ -1054,7 +1055,7 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e // archive / un-archive if opts.Archived != nil { if repo.IsMirror { - err := fmt.Errorf("repo is a mirror, cannot archive/un-archive") + err := errors.New("repo is a mirror, cannot archive/un-archive") ctx.Error(http.StatusUnprocessableEntity, err.Error(), err) return err } diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index 3b6cb4d3f2..72cfeaf902 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -238,7 +238,7 @@ func acceptOrRejectRepoTransfer(ctx *context.APIContext, accept bool) error { if !repoTransfer.CanUserAcceptTransfer(ctx, ctx.Doer) { ctx.Error(http.StatusForbidden, "CanUserAcceptTransfer", nil) - return fmt.Errorf("user does not have permissions to do this") + return errors.New("user does not have permissions to do this") } if accept { diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go index 1581358b66..886e33b205 100644 --- a/routers/api/v1/user/gpg_key.go +++ b/routers/api/v1/user/gpg_key.go @@ -4,6 +4,7 @@ package user import ( + "errors" "fmt" "net/http" "strings" @@ -143,7 +144,7 @@ func GetGPGKey(ctx *context.APIContext) { // CreateUserGPGKey creates new GPG key to given user by ID. func CreateUserGPGKey(ctx *context.APIContext, form api.CreateGPGKeyOption, uid int64) { if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) { - ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited")) + ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited")) return } @@ -298,7 +299,7 @@ func DeleteGPGKey(ctx *context.APIContext) { // "$ref": "#/responses/notFound" if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) { - ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited")) + ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited")) return } diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index 7119de9b2e..d8b5dfdfe9 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -5,7 +5,7 @@ package user import ( std_ctx "context" - "fmt" + "errors" "net/http" asymkey_model "forgejo.org/models/asymkey" @@ -209,7 +209,7 @@ func GetPublicKey(ctx *context.APIContext) { // CreateUserPublicKey creates new public key to given user by ID. func CreateUserPublicKey(ctx *context.APIContext, form api.CreateKeyOption, uid int64) { if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { - ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited")) return } @@ -285,7 +285,7 @@ func DeletePublicKey(ctx *context.APIContext) { // "$ref": "#/responses/notFound" if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { - ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited")) return } diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go index 5359a54899..65a8994405 100644 --- a/routers/api/v1/utils/git.go +++ b/routers/api/v1/utils/git.go @@ -5,6 +5,7 @@ package utils import ( gocontext "context" + "errors" "fmt" "net/http" @@ -50,7 +51,7 @@ func ResolveRefOrSha(ctx *context.APIContext, ref string) string { // GetGitRefs return git references based on filter func GetGitRefs(ctx *context.APIContext, filter string) ([]*git.Reference, string, error) { if ctx.Repo.GitRepo == nil { - return nil, "", fmt.Errorf("no open git repo found in context") + return nil, "", errors.New("no open git repo found in context") } if len(filter) > 0 { filter = "refs/" + filter diff --git a/routers/common/db.go b/routers/common/db.go index 0f78d8debc..ec31ced1bf 100644 --- a/routers/common/db.go +++ b/routers/common/db.go @@ -5,7 +5,7 @@ package common import ( "context" - "fmt" + "errors" "time" "forgejo.org/models/db" @@ -24,7 +24,7 @@ func InitDBEngine(ctx context.Context) (err error) { for i := 0; i < setting.Database.DBConnectRetries; i++ { select { case <-ctx.Done(): - return fmt.Errorf("Aborted due to shutdown:\nin retry ORM engine initialization") + return errors.New("Aborted due to shutdown:\nin retry ORM engine initialization") default: } log.Info("ORM engine initialization attempt #%d/%d...", i+1, setting.Database.DBConnectRetries) diff --git a/routers/private/manager_process.go b/routers/private/manager_process.go index 87447da2be..e60ed04879 100644 --- a/routers/private/manager_process.go +++ b/routers/private/manager_process.go @@ -122,7 +122,7 @@ func writeProcess(out io.Writer, process *process_module.Process, indent string, if stack.Count > 1 { _, _ = fmt.Fprintf(sb, "* %d", stack.Count) } - _, _ = fmt.Fprintf(sb, "\n") + _, _ = fmt.Fprintln(sb) indent += "| " if len(stack.Labels) > 0 { _, _ = fmt.Fprintf(sb, "%sLabels: %q:%q", indent, stack.Labels[0].Name, stack.Labels[0].Value) @@ -132,7 +132,7 @@ func writeProcess(out io.Writer, process *process_module.Process, indent string, _, _ = fmt.Fprintf(sb, ", %q:%q", label.Name, label.Value) } } - _, _ = fmt.Fprintf(sb, "\n") + _, _ = fmt.Fprintln(sb) } _, _ = fmt.Fprintf(sb, "%sStack:\n", indent) indent += " " diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go index b12dea84ea..fcb2155953 100644 --- a/routers/web/auth/openid.go +++ b/routers/web/auth/openid.go @@ -4,6 +4,7 @@ package auth import ( + "errors" "fmt" "net/http" "net/url" @@ -55,13 +56,13 @@ func allowedOpenIDURI(uri string) (err error) { } } // must match one of this or be refused - return fmt.Errorf("URI not allowed by whitelist") + return errors.New("URI not allowed by whitelist") } // A blacklist match expliclty forbids for _, pat := range setting.Service.OpenIDBlacklist { if pat.MatchString(uri) { - return fmt.Errorf("URI forbidden by blacklist") + return errors.New("URI forbidden by blacklist") } } diff --git a/routers/web/repo/action_aggregator_test.go b/routers/web/repo/action_aggregator_test.go index e1c6d37fd0..385cf5cbe5 100644 --- a/routers/web/repo/action_aggregator_test.go +++ b/routers/web/repo/action_aggregator_test.go @@ -179,7 +179,7 @@ func (kase *testCase) doTest(t *testing.T) { if len(after) != len(issue.Comments) { t.Logf("Expected %v comments, got %v", len(after), len(issue.Comments)) - t.Logf("Comments got after combination:") + t.Log("Comments got after combination:") for c := 0; c < len(issue.Comments); c++ { cmt := issue.Comments[c] t.Logf("%v %v %v\n", cmt.Type, cmt.CreatedUnix, cmt.Content) diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 364bbf34a8..260468f207 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -517,7 +517,7 @@ func Cancel(ctx *context_module.Context) { return err } if n == 0 { - return fmt.Errorf("job has changed, try again") + return errors.New("job has changed, try again") } continue } diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go index 94d32b730f..935efd7ba7 100644 --- a/routers/web/user/setting/keys.go +++ b/routers/web/user/setting/keys.go @@ -5,7 +5,7 @@ package setting import ( - "fmt" + "errors" "net/http" asymkey_model "forgejo.org/models/asymkey" @@ -80,7 +80,7 @@ func KeysPost(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/user/settings/keys") case "gpg": if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) { - ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited")) + ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited")) return } @@ -161,7 +161,7 @@ func KeysPost(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/user/settings/keys") case "ssh": if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { - ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited")) return } @@ -205,7 +205,7 @@ func KeysPost(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/user/settings/keys") case "verify_ssh": if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { - ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited")) return } @@ -242,7 +242,7 @@ func DeleteKey(ctx *context.Context) { switch ctx.FormString("type") { case "gpg": if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageGPGKeys) { - ctx.NotFound("Not Found", fmt.Errorf("gpg keys setting is not allowed to be visited")) + ctx.NotFound("Not Found", errors.New("gpg keys setting is not allowed to be visited")) return } if err := asymkey_model.DeleteGPGKey(ctx, ctx.Doer, ctx.FormInt64("id")); err != nil { @@ -252,7 +252,7 @@ func DeleteKey(ctx *context.Context) { } case "ssh": if user_model.IsFeatureDisabledWithLoginType(ctx.Doer, setting.UserFeatureManageSSHKeys) { - ctx.NotFound("Not Found", fmt.Errorf("ssh keys setting is not allowed to be visited")) + ctx.NotFound("Not Found", errors.New("ssh keys setting is not allowed to be visited")) return } diff --git a/services/actions/auth.go b/services/actions/auth.go index 4dc86a35f3..98b618aeba 100644 --- a/services/actions/auth.go +++ b/services/actions/auth.go @@ -4,6 +4,7 @@ package actions import ( + "errors" "fmt" "net/http" "strings" @@ -80,7 +81,7 @@ func ParseAuthorizationToken(req *http.Request) (int64, error) { parts := strings.SplitN(h, " ", 2) if len(parts) != 2 { log.Error("split token failed: %s", h) - return 0, fmt.Errorf("split token failed") + return 0, errors.New("split token failed") } return TokenToTaskID(parts[1]) @@ -100,7 +101,7 @@ func TokenToTaskID(token string) (int64, error) { c, ok := parsedToken.Claims.(*actionsClaims) if !parsedToken.Valid || !ok { - return 0, fmt.Errorf("invalid token claim") + return 0, errors.New("invalid token claim") } return c.TaskID, nil diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 1fffa6852f..755fa648dc 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -5,6 +5,7 @@ package actions import ( "context" + "errors" "fmt" "path" @@ -50,7 +51,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er return fmt.Errorf("GetPushEventPayload: %w", err) } if payload.HeadCommit == nil { - return fmt.Errorf("head commit is missing in event payload") + return errors.New("head commit is missing in event payload") } sha = payload.HeadCommit.ID case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync, webhook_module.HookEventPullRequestLabel, webhook_module.HookEventPullRequestAssign, webhook_module.HookEventPullRequestMilestone: @@ -64,9 +65,9 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er return fmt.Errorf("GetPullRequestEventPayload: %w", err) } if payload.PullRequest == nil { - return fmt.Errorf("pull request is missing in event payload") + return errors.New("pull request is missing in event payload") } else if payload.PullRequest.Head == nil { - return fmt.Errorf("head of pull request is missing in event payload") + return errors.New("head of pull request is missing in event payload") } sha = payload.PullRequest.Head.Sha case webhook_module.HookEventRelease: diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index 9bfa7d3e1d..3ec0807d5f 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -5,6 +5,7 @@ package actions import ( "context" + "errors" "fmt" "time" @@ -205,7 +206,7 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin // If the update affected 0 rows, it means the job has changed in the meantime, so we need to try again. if n == 0 { - return fmt.Errorf("job has changed, try again") + return errors.New("job has changed, try again") } // Continue with the next job. diff --git a/services/actions/task.go b/services/actions/task.go index 4e885f69cc..bb319c7d05 100644 --- a/services/actions/task.go +++ b/services/actions/task.go @@ -5,6 +5,7 @@ package actions import ( "context" + "errors" "fmt" actions_model "forgejo.org/models/actions" @@ -183,7 +184,7 @@ func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.Task } else if !has { return nil, util.ErrNotExist } else if runnerID != task.RunnerID { - return nil, fmt.Errorf("invalid runner for task") + return nil, errors.New("invalid runner for task") } if task.Status.IsDone() { diff --git a/services/auth/httpsign.go b/services/auth/httpsign.go index d3cbb8aa60..e776ccbbed 100644 --- a/services/auth/httpsign.go +++ b/services/auth/httpsign.go @@ -134,7 +134,7 @@ func VerifyCert(r *http.Request) (*asymkey_model.PublicKey, error) { // Check if it's really a ssh certificate cert, ok := pk.(*ssh.Certificate) if !ok { - return nil, fmt.Errorf("no certificate found") + return nil, errors.New("no certificate found") } c := &ssh.CertChecker{ @@ -153,7 +153,7 @@ func VerifyCert(r *http.Request) (*asymkey_model.PublicKey, error) { // check the CA of the cert if !c.IsUserAuthority(cert.SignatureKey) { - return nil, fmt.Errorf("CA check failed") + return nil, errors.New("CA check failed") } // Create a verifier @@ -191,7 +191,7 @@ func VerifyCert(r *http.Request) (*asymkey_model.PublicKey, error) { } // No public key matching a principal in the certificate is registered in gitea - return nil, fmt.Errorf("no valid principal found") + return nil, errors.New("no valid principal found") } // doVerify iterates across the provided public keys attempting the verify the current request against each key in turn diff --git a/services/auth/source/oauth2/token.go b/services/auth/source/oauth2/token.go index fba1fd8a01..b060b6b746 100644 --- a/services/auth/source/oauth2/token.go +++ b/services/auth/source/oauth2/token.go @@ -4,6 +4,7 @@ package oauth2 import ( + "errors" "fmt" "time" @@ -51,12 +52,12 @@ func ParseToken(jwtToken string, signingKey JWTSigningKey) (*Token, error) { return nil, err } if !parsedToken.Valid { - return nil, fmt.Errorf("invalid token") + return nil, errors.New("invalid token") } var token *Token var ok bool if token, ok = parsedToken.Claims.(*Token); !ok || !parsedToken.Valid { - return nil, fmt.Errorf("invalid token") + return nil, errors.New("invalid token") } return token, nil } diff --git a/services/automerge/automerge.go b/services/automerge/automerge.go index 51a14edd9a..f183136907 100644 --- a/services/automerge/automerge.go +++ b/services/automerge/automerge.go @@ -32,7 +32,7 @@ func Init() error { shared_automerge.PRAutoMergeQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_auto_merge", handler) if shared_automerge.PRAutoMergeQueue == nil { - return fmt.Errorf("unable to create pr_auto_merge queue") + return errors.New("unable to create pr_auto_merge queue") } go graceful.GetManager().RunWithCancel(shared_automerge.PRAutoMergeQueue) return nil diff --git a/services/context/api.go b/services/context/api.go index 0a3cb04399..e9f67c720d 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -6,6 +6,7 @@ package context import ( "context" + "errors" "fmt" "net/http" "net/url" @@ -365,12 +366,12 @@ func RepoRefForAPI(next http.Handler) http.Handler { ctx := GetAPIContext(req) if ctx.Repo.Repository.IsEmpty { - ctx.NotFound(fmt.Errorf("repository is empty")) + ctx.NotFound(errors.New("repository is empty")) return } if ctx.Repo.GitRepo == nil { - ctx.InternalServerError(fmt.Errorf("no open git repo")) + ctx.InternalServerError(errors.New("no open git repo")) return } diff --git a/services/doctor/authorizedkeys.go b/services/doctor/authorizedkeys.go index 04a3680ff5..465a3fc7c0 100644 --- a/services/doctor/authorizedkeys.go +++ b/services/doctor/authorizedkeys.go @@ -7,6 +7,7 @@ import ( "bufio" "bytes" "context" + "errors" "fmt" "os" "path/filepath" @@ -77,7 +78,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e fPath, "forgejo admin regenerate keys", "forgejo doctor check --run authorized-keys --fix") - return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "forgejo admin regenerate keys" or "forgejo doctor check --run authorized-keys --fix"`) + return errors.New(`authorized_keys is out of date and should be regenerated with "forgejo admin regenerate keys" or "forgejo doctor check --run authorized-keys --fix"`) } logger.Warn("authorized_keys is out of date. Attempting rewrite...") err = asymkey_model.RewriteAllPublicKeys(ctx) diff --git a/services/doctor/lfs.go b/services/doctor/lfs.go index fed127de5d..fe858605f4 100644 --- a/services/doctor/lfs.go +++ b/services/doctor/lfs.go @@ -5,7 +5,7 @@ package doctor import ( "context" - "fmt" + "errors" "time" "forgejo.org/modules/log" @@ -27,7 +27,7 @@ func init() { func garbageCollectLFSCheck(ctx context.Context, logger log.Logger, autofix bool) error { if !setting.LFS.StartServer { - return fmt.Errorf("LFS support is disabled") + return errors.New("LFS support is disabled") } if err := repository.GarbageCollectLFSMetaObjects(ctx, repository.GarbageCollectLFSMetaObjectsOptions{ diff --git a/services/externalaccount/link.go b/services/externalaccount/link.go index f5d29b5ce5..5672313181 100644 --- a/services/externalaccount/link.go +++ b/services/externalaccount/link.go @@ -5,7 +5,7 @@ package externalaccount import ( "context" - "fmt" + "errors" user_model "forgejo.org/models/user" @@ -23,7 +23,7 @@ type Store interface { func LinkAccountFromStore(ctx context.Context, store Store, user *user_model.User) error { gothUser := store.Get("linkAccountGothUser") if gothUser == nil { - return fmt.Errorf("not in LinkAccount session") + return errors.New("not in LinkAccount session") } return LinkAccountToUser(ctx, user, gothUser.(goth.User)) diff --git a/services/federation/federation_service.go b/services/federation/federation_service.go index 3d6e219ffc..174c175f86 100644 --- a/services/federation/federation_service.go +++ b/services/federation/federation_service.go @@ -5,6 +5,7 @@ package federation import ( "context" + "errors" "fmt" "net/http" "net/url" @@ -46,7 +47,7 @@ func ProcessLikeActivity(ctx context.Context, form any, repositoryID int64) (int return http.StatusInternalServerError, "Wrong FederationHost", err } if !activity.IsNewer(federationHost.LatestActivity) { - return http.StatusNotAcceptable, "Activity out of order.", fmt.Errorf("Activity already processed") + return http.StatusNotAcceptable, "Activity out of order.", errors.New("Activity already processed") } actorID, err := fm.NewPersonID(actorURI, string(federationHost.NodeInfo.SoftwareName)) if err != nil { diff --git a/services/issue/comments.go b/services/issue/comments.go index 2993d38003..2cac900d41 100644 --- a/services/issue/comments.go +++ b/services/issue/comments.go @@ -5,6 +5,7 @@ package issue import ( "context" + "errors" "fmt" "forgejo.org/models/db" @@ -18,7 +19,7 @@ import ( // CreateRefComment creates a commit reference comment to issue. func CreateRefComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content, commitSHA string) error { if len(commitSHA) == 0 { - return fmt.Errorf("cannot create reference with empty commit SHA") + return errors.New("cannot create reference with empty commit SHA") } // Check if same reference from same commit has already existed. diff --git a/services/issue/issue.go b/services/issue/issue.go index 1ec41476b4..7071a912b0 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -6,6 +6,7 @@ package issue import ( "context" + "errors" "fmt" "time" @@ -345,13 +346,13 @@ func SetIssueUpdateDate(ctx context.Context, issue *issues_model.Issue, updated return err } if !perm.IsAdmin() && !perm.IsOwner() { - return fmt.Errorf("user needs to have admin or owner right") + return errors.New("user needs to have admin or owner right") } // A simple guard against potential inconsistent calls updatedUnix := timeutil.TimeStamp(updated.Unix()) if updatedUnix < issue.CreatedUnix || updatedUnix > timeutil.TimeStampNow() { - return fmt.Errorf("unallowed update date") + return errors.New("unallowed update date") } issue.UpdatedUnix = updatedUnix diff --git a/services/issue/milestone.go b/services/issue/milestone.go index 3fa7083812..a561bf8eee 100644 --- a/services/issue/milestone.go +++ b/services/issue/milestone.go @@ -5,6 +5,7 @@ package issue import ( "context" + "errors" "fmt" "forgejo.org/models/db" @@ -47,7 +48,7 @@ func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *is return fmt.Errorf("HasMilestoneByRepoID: %w", err) } if !has { - return fmt.Errorf("HasMilestoneByRepoID: issue doesn't exist") + return errors.New("HasMilestoneByRepoID: issue doesn't exist") } } diff --git a/services/lfs/server.go b/services/lfs/server.go index 8b2d5e66e1..17e6d0eec7 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -595,15 +595,15 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo claims, claimsOk := token.Claims.(*Claims) if !token.Valid || !claimsOk { - return nil, fmt.Errorf("invalid token claim") + return nil, errors.New("invalid token claim") } if claims.RepoID != target.ID { - return nil, fmt.Errorf("invalid token claim") + return nil, errors.New("invalid token claim") } if mode == perm.AccessModeWrite && claims.Op != "upload" { - return nil, fmt.Errorf("invalid token claim") + return nil, errors.New("invalid token claim") } u, err := user_model.GetUserByID(ctx, claims.UserID) @@ -616,12 +616,12 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo func parseToken(ctx stdCtx.Context, authorization string, target *repo_model.Repository, mode perm.AccessMode) (*user_model.User, error) { if authorization == "" { - return nil, fmt.Errorf("no token") + return nil, errors.New("no token") } parts := strings.SplitN(authorization, " ", 2) if len(parts) != 2 { - return nil, fmt.Errorf("no token") + return nil, errors.New("no token") } tokenSHA := parts[1] switch strings.ToLower(parts[0]) { @@ -630,7 +630,7 @@ func parseToken(ctx stdCtx.Context, authorization string, target *repo_model.Rep case "token": return handleLFSToken(ctx, tokenSHA, target, mode) } - return nil, fmt.Errorf("token not found") + return nil, errors.New("token not found") } func requireAuth(ctx *context.Context) { diff --git a/services/mailer/mail_team_invite.go b/services/mailer/mail_team_invite.go index a2a871d3c3..5375133415 100644 --- a/services/mailer/mail_team_invite.go +++ b/services/mailer/mail_team_invite.go @@ -6,6 +6,7 @@ package mailer import ( "bytes" "context" + "errors" "fmt" "net/url" @@ -39,7 +40,7 @@ func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_mod if err != nil && !user_model.IsErrUserNotExist(err) { return err } else if user != nil && user.ProhibitLogin { - return fmt.Errorf("login is prohibited for the invited user") + return errors.New("login is prohibited for the invited user") } inviteRedirect := url.QueryEscape(fmt.Sprintf("/org/invite/%s", invite.Token)) diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go index 4561240df5..ca5c645e0c 100644 --- a/services/mailer/mailer.go +++ b/services/mailer/mailer.go @@ -8,6 +8,7 @@ import ( "bytes" "context" "crypto/tls" + "errors" "fmt" "hash/fnv" "io" @@ -176,7 +177,7 @@ func (a *ntlmAuth) Start(server *smtp.ServerInfo) (string, []byte, error) { func (a *ntlmAuth) Next(fromServer []byte, more bool) ([]byte, error) { if more { if len(fromServer) == 0 { - return nil, fmt.Errorf("ntlm ChallengeMessage is empty") + return nil, errors.New("ntlm ChallengeMessage is empty") } authenticateMessage, err := ntlmssp.ProcessChallenge(fromServer, a.username, a.password, a.domainNeeded) return authenticateMessage, err @@ -264,7 +265,7 @@ func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error { canAuth, options := client.Extension("AUTH") if len(opts.User) > 0 { if !canAuth { - return fmt.Errorf("SMTP server does not support AUTH, but credentials provided") + return errors.New("SMTP server does not support AUTH, but credentials provided") } var auth smtp.Auth diff --git a/services/markup/processorhelper.go b/services/markup/processorhelper.go index b5fcd78cb7..2f1b1e738c 100644 --- a/services/markup/processorhelper.go +++ b/services/markup/processorhelper.go @@ -5,7 +5,7 @@ package markup import ( "context" - "fmt" + "errors" "forgejo.org/models/perm/access" "forgejo.org/models/repo" @@ -55,7 +55,7 @@ func ProcessorHelper() *markup.ProcessorHelper { return nil, err } if !perms.CanRead(unit.TypeCode) { - return nil, fmt.Errorf("cannot access repository code") + return nil, errors.New("cannot access repository code") } gitRepo, err := gitrepo.OpenRepository(ctx, repo) diff --git a/services/migrations/gogs_test.go b/services/migrations/gogs_test.go index 6780ad2923..bf0d063ca4 100644 --- a/services/migrations/gogs_test.go +++ b/services/migrations/gogs_test.go @@ -25,7 +25,7 @@ func TestGogsDownloadRepo(t *testing.T) { resp, err := http.Get("https://try.gogs.io/lunnytest/TESTREPO") if err != nil || resp.StatusCode/100 != 2 { // skip and don't run test - t.Skipf("visit test repo failed, ignored") + t.Skip("visit test repo failed, ignored") return } diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go index 81d1c203fe..61630d9c6d 100644 --- a/services/migrations/migrate.go +++ b/services/migrations/migrate.go @@ -6,6 +6,7 @@ package migrations import ( "context" + "errors" "fmt" "net" "net/url" @@ -227,7 +228,7 @@ func migrateRepository(_ context.Context, doer *user_model.User, downloader base if cloneURL.Scheme == "file" || cloneURL.Scheme == "" { if cloneAddrURL.Scheme != "file" && cloneAddrURL.Scheme != "" { - return fmt.Errorf("repo info has changed from external to local filesystem") + return errors.New("repo info has changed from external to local filesystem") } } diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go index 6d871ad5ff..514b7c3969 100644 --- a/services/mirror/mirror.go +++ b/services/mirror/mirror.go @@ -5,7 +5,7 @@ package mirror import ( "context" - "fmt" + "errors" quota_model "forgejo.org/models/quota" repo_model "forgejo.org/models/repo" @@ -31,7 +31,7 @@ func doMirrorSync(ctx context.Context, req *SyncRequest) { } } -var errLimit = fmt.Errorf("reached limit") +var errLimit = errors.New("reached limit") // Update checks and updates mirror repositories. func Update(ctx context.Context, pullLimit, pushLimit int) error { @@ -70,7 +70,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { // Check we've not been cancelled select { case <-ctx.Done(): - return fmt.Errorf("aborted") + return errors.New("aborted") default: } diff --git a/services/packages/alpine/repository.go b/services/packages/alpine/repository.go index 9435887a46..dd66c7d74e 100644 --- a/services/packages/alpine/repository.go +++ b/services/packages/alpine/repository.go @@ -258,7 +258,7 @@ func buildPackagesIndex(ctx context.Context, ownerID int64, repoVersion *package privPem, _ := pem.Decode([]byte(priv)) if privPem == nil { - return fmt.Errorf("failed to decode private key pem") + return errors.New("failed to decode private key pem") } privKey, err := x509.ParsePKCS1PrivateKey(privPem.Bytes) diff --git a/services/packages/auth.go b/services/packages/auth.go index ab2c347bc9..205125cf8b 100644 --- a/services/packages/auth.go +++ b/services/packages/auth.go @@ -4,6 +4,7 @@ package packages import ( + "errors" "fmt" "net/http" "strings" @@ -53,7 +54,7 @@ func ParseAuthorizationToken(req *http.Request) (int64, auth_model.AccessTokenSc parts := strings.SplitN(h, " ", 2) if len(parts) != 2 { log.Error("split token failed: %s", h) - return 0, "", fmt.Errorf("split token failed") + return 0, "", errors.New("split token failed") } token, err := jwt.ParseWithClaims(parts[1], &packageClaims{}, func(t *jwt.Token) (any, error) { @@ -68,7 +69,7 @@ func ParseAuthorizationToken(req *http.Request) (int64, auth_model.AccessTokenSc c, ok := token.Claims.(*packageClaims) if !token.Valid || !ok { - return 0, "", fmt.Errorf("invalid token claim") + return 0, "", errors.New("invalid token claim") } return c.UserID, c.Scope, nil diff --git a/services/pull/check.go b/services/pull/check.go index d038b3d829..afeb7e675e 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -395,7 +395,7 @@ func Init() error { prPatchCheckerQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_patch_checker", handler) if prPatchCheckerQueue == nil { - return fmt.Errorf("unable to create pr_patch_checker queue") + return errors.New("unable to create pr_patch_checker queue") } go graceful.GetManager().RunWithCancel(prPatchCheckerQueue) diff --git a/services/pull/merge.go b/services/pull/merge.go index 9b0d632377..3f07c3df9b 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -6,6 +6,7 @@ package pull import ( "context" + "errors" "fmt" "net/url" "os" @@ -518,13 +519,13 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) if len(commitID) != objectFormat.FullLength() { - return fmt.Errorf("Wrong commit ID") + return errors.New("Wrong commit ID") } commit, err := baseGitRepo.GetCommit(commitID) if err != nil { if git.IsErrNotExist(err) { - return fmt.Errorf("Wrong commit ID") + return errors.New("Wrong commit ID") } return err } @@ -535,7 +536,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use return err } if !ok { - return fmt.Errorf("Wrong commit ID") + return errors.New("Wrong commit ID") } pr.MergedCommitID = commitID @@ -548,7 +549,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use if merged, err = pr.SetMerged(ctx); err != nil { return err } else if !merged { - return fmt.Errorf("SetMerged failed") + return errors.New("SetMerged failed") } return nil }); err != nil { diff --git a/services/pull/review.go b/services/pull/review.go index 18c27de5a7..c740328e4c 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -6,6 +6,7 @@ package pull import ( "context" + "errors" "fmt" "io" "regexp" @@ -387,7 +388,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, } if review.Type != issues_model.ReviewTypeApprove && review.Type != issues_model.ReviewTypeReject { - return nil, fmt.Errorf("not need to dismiss this review because it's type is not Approve or change request") + return nil, errors.New("not need to dismiss this review because it's type is not Approve or change request") } // load data for notify @@ -397,7 +398,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, // Check if the review's repoID is the one we're currently expecting. if review.Issue.RepoID != repoID { - return nil, fmt.Errorf("reviews's repository is not the same as the one we expect") + return nil, errors.New("reviews's repository is not the same as the one we expect") } issue := review.Issue diff --git a/services/pull/update.go b/services/pull/update.go index 470abbd6dd..563c11fff3 100644 --- a/services/pull/update.go +++ b/services/pull/update.go @@ -5,6 +5,7 @@ package pull import ( "context" + "errors" "fmt" git_model "forgejo.org/models/git" @@ -22,7 +23,7 @@ import ( func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, message string, rebase bool) error { if pr.Flow == issues_model.PullRequestFlowAGit { // TODO: update of agit flow pull request's head branch is unsupported - return fmt.Errorf("update of agit flow pull request's head branch is unsupported") + return errors.New("update of agit flow pull request's head branch is unsupported") } pullWorkingPool.CheckIn(fmt.Sprint(pr.ID)) diff --git a/services/release/release.go b/services/release/release.go index f0682c4dca..90eb1320ed 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -161,17 +161,17 @@ func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, msg string, for _, attachmentChange := range attachmentChanges { if attachmentChange.Action != "add" { - return fmt.Errorf("can only create new attachments when creating release") + return errors.New("can only create new attachments when creating release") } switch attachmentChange.Type { case "attachment": if attachmentChange.UUID == "" { - return fmt.Errorf("new attachment should have a uuid") + return errors.New("new attachment should have a uuid") } addAttachmentUUIDs.Add(attachmentChange.UUID) case "external": if attachmentChange.Name == "" || attachmentChange.ExternalURL == "" { - return fmt.Errorf("new external attachment should have a name and external url") + return errors.New("new external attachment should have a name and external url") } _, err = attachment.NewExternalAttachment(gitRepo.Ctx, &repo_model.Attachment{ @@ -186,7 +186,7 @@ func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, msg string, } default: if attachmentChange.Type == "" { - return fmt.Errorf("missing attachment type") + return errors.New("missing attachment type") } return fmt.Errorf("unknown attachment type: '%q'", attachmentChange.Type) } @@ -280,7 +280,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo addAttachmentUUIDs.Add(attachmentChange.UUID) case "external": if attachmentChange.Name == "" || attachmentChange.ExternalURL == "" { - return fmt.Errorf("new external attachment should have a name and external url") + return errors.New("new external attachment should have a name and external url") } _, err := attachment.NewExternalAttachment(ctx, &repo_model.Attachment{ Name: attachmentChange.Name, @@ -294,13 +294,13 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo } default: if attachmentChange.Type == "" { - return fmt.Errorf("missing attachment type") + return errors.New("missing attachment type") } return fmt.Errorf("unknown attachment type: %q", attachmentChange.Type) } case "delete": if attachmentChange.UUID == "" { - return fmt.Errorf("attachment deletion should have a uuid") + return errors.New("attachment deletion should have a uuid") } delAttachmentUUIDs.Add(attachmentChange.UUID) case "update": @@ -308,7 +308,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo updateAttachments.Add(attachmentChange) default: if attachmentChange.Action == "" { - return fmt.Errorf("missing attachment action") + return errors.New("missing attachment action") } return fmt.Errorf("unknown attachment action: %q", attachmentChange.Action) } diff --git a/services/repository/branch.go b/services/repository/branch.go index d5b68c1fa7..bc739825a5 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -251,7 +251,7 @@ func SyncBranchesToDB(ctx context.Context, repoID, pusherID int64, branchNames, // For other batches, it will hit optimization 4. if len(branchNames) != len(commitIDs) { - return fmt.Errorf("branchNames and commitIDs length not match") + return errors.New("branchNames and commitIDs length not match") } return db.WithTx(ctx, func(ctx context.Context) error { diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go index ad4cc400cb..1805bd5960 100644 --- a/services/repository/contributors_graph.go +++ b/services/repository/contributors_graph.go @@ -111,7 +111,7 @@ func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_mode var cachedStats map[string]*ContributorData return cachedStats, json.Unmarshal([]byte(v), &cachedStats) default: - return nil, fmt.Errorf("unexpected type in cache detected") + return nil, errors.New("unexpected type in cache detected") } } diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go index 838891813e..0e88a29230 100644 --- a/services/repository/files/cherry_pick.go +++ b/services/repository/files/cherry_pick.go @@ -5,6 +5,7 @@ package files import ( "context" + "errors" "fmt" "strings" @@ -88,7 +89,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod } if conflict { - return nil, fmt.Errorf("failed to merge due to conflicts") + return nil, errors.New("failed to merge due to conflicts") } } else { description := fmt.Sprintf("CherryPick %s onto %s", right, opts.OldBranch) @@ -98,7 +99,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod } if conflict { - return nil, fmt.Errorf("failed to merge due to conflicts") + return nil, errors.New("failed to merge due to conflicts") } treeHash, err = t.WriteTree() diff --git a/services/repository/files/file.go b/services/repository/files/file.go index 810c60163d..ef9a87dbcf 100644 --- a/services/repository/files/file.go +++ b/services/repository/files/file.go @@ -5,7 +5,7 @@ package files import ( "context" - "fmt" + "errors" "net/url" "strings" "time" @@ -50,10 +50,10 @@ func GetFileResponseFromFilesResponse(filesResponse *api.FilesResponse, index in // GetFileCommitResponse Constructs a FileCommitResponse from a Commit object func GetFileCommitResponse(repo *repo_model.Repository, commit *git.Commit) (*api.FileCommitResponse, error) { if repo == nil { - return nil, fmt.Errorf("repo cannot be nil") + return nil, errors.New("repo cannot be nil") } if commit == nil { - return nil, fmt.Errorf("commit cannot be nil") + return nil, errors.New("commit cannot be nil") } commitURL, _ := url.Parse(repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String())) commitTreeURL, _ := url.Parse(repo.APIURL() + "/git/trees/" + url.PathEscape(commit.Tree.ID.String())) diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index b3aadbc6cb..64d3e5887d 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -6,6 +6,7 @@ package files import ( "bytes" "context" + "errors" "fmt" "io" "os" @@ -368,7 +369,7 @@ func (t *TemporaryUploadRepository) DiffIndex() (*gitdiff.Diff, error) { // GetBranchCommit Gets the commit object of the given branch func (t *TemporaryUploadRepository) GetBranchCommit(branch string) (*git.Commit, error) { if t.gitRepo == nil { - return nil, fmt.Errorf("repository has not been cloned") + return nil, errors.New("repository has not been cloned") } return t.gitRepo.GetBranchCommit(branch) } @@ -376,7 +377,7 @@ func (t *TemporaryUploadRepository) GetBranchCommit(branch string) (*git.Commit, // GetCommit Gets the commit object of the given commit ID func (t *TemporaryUploadRepository) GetCommit(commitID string) (*git.Commit, error) { if t.gitRepo == nil { - return nil, fmt.Errorf("repository has not been cloned") + return nil, errors.New("repository has not been cloned") } return t.gitRepo.GetCommit(commitID) } diff --git a/services/repository/push.go b/services/repository/push.go index 53574a7d93..eaedd80e1f 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -66,7 +66,7 @@ func PushUpdates(opts []*repo_module.PushUpdateOptions) error { for _, opt := range opts { if opt.IsNewRef() && opt.IsDelRef() { - return fmt.Errorf("Old and new revisions are both NULL") + return errors.New("Old and new revisions are both NULL") } } diff --git a/services/repository/repository.go b/services/repository/repository.go index a2620740b1..41f3a96dd1 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -6,6 +6,7 @@ package repository import ( "context" + "errors" "fmt" "forgejo.org/models/db" @@ -72,10 +73,10 @@ func PushCreateRepo(ctx context.Context, authUser, owner *user_model.User, repoN if ok, err := organization.CanCreateOrgRepo(ctx, owner.ID, authUser.ID); err != nil { return nil, err } else if !ok { - return nil, fmt.Errorf("cannot push-create repository for org") + return nil, errors.New("cannot push-create repository for org") } } else if authUser.ID != owner.ID { - return nil, fmt.Errorf("cannot push-create repository for another user") + return nil, errors.New("cannot push-create repository for another user") } } diff --git a/services/task/task.go b/services/task/task.go index 3181fc79d7..f030bdb38c 100644 --- a/services/task/task.go +++ b/services/task/task.go @@ -5,6 +5,7 @@ package task import ( "context" + "errors" "fmt" admin_model "forgejo.org/models/admin" @@ -41,7 +42,7 @@ func Run(ctx context.Context, t *admin_model.Task) error { func Init() error { taskQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "task", handler) if taskQueue == nil { - return fmt.Errorf("unable to create task queue") + return errors.New("unable to create task queue") } go graceful.GetManager().RunWithCancel(taskQueue) return nil diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index 0c7c039f10..23aca80345 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -6,6 +6,7 @@ package webhook import ( "context" "crypto/tls" + "errors" "fmt" "io" "net/http" @@ -218,7 +219,7 @@ func Init() error { hookQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "webhook_sender", handler) if hookQueue == nil { - return fmt.Errorf("unable to create webhook_sender queue") + return errors.New("unable to create webhook_sender queue") } go graceful.GetManager().RunWithCancel(hookQueue) diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go index ff5141a1fd..cb984425af 100644 --- a/services/wiki/wiki_test.go +++ b/services/wiki/wiki_test.go @@ -284,9 +284,9 @@ func TestPrepareWikiFileName(t *testing.T) { } if existence != tt.existence { if existence { - t.Errorf("expect to find no escaped file but we detect one") + t.Error("expect to find no escaped file but we detect one") } else { - t.Errorf("expect to find an escaped file but we could not detect one") + t.Error("expect to find an escaped file but we could not detect one") } } assert.Equal(t, tt.wikiPath, newWikiPath) diff --git a/tests/integration/api_actions_artifact_test.go b/tests/integration/api_actions_artifact_test.go index dc2b86d28b..d6db22700a 100644 --- a/tests/integration/api_actions_artifact_test.go +++ b/tests/integration/api_actions_artifact_test.go @@ -56,7 +56,7 @@ func TestActionsArtifactUploadSingleFile(t *testing.T) { SetHeader("x-actions-results-md5", "XVlf820rMInUi64wmMi6EA==") // base64(md5(body)) MakeRequest(t, req, http.StatusOK) - t.Logf("Create artifact confirm") + t.Log("Create artifact confirm") // confirm artifact upload req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact-single"). @@ -206,7 +206,7 @@ func TestActionsArtifactUploadMultipleFile(t *testing.T) { MakeRequest(t, req, http.StatusOK) } - t.Logf("Create artifact confirm") + t.Log("Create artifact confirm") // confirm artifact upload req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName="+testArtifactName). @@ -297,7 +297,7 @@ func TestActionsArtifactUploadWithRetentionDays(t *testing.T) { SetHeader("x-actions-results-md5", "1HsSe8LeLWh93ILaw1TEFQ==") // base64(md5(body)) MakeRequest(t, req, http.StatusOK) - t.Logf("Create artifact confirm") + t.Log("Create artifact confirm") // confirm artifact upload req = NewRequest(t, "PATCH", "/api/actions_pipeline/_apis/pipelines/workflows/791/artifacts?artifactName=artifact-retention-days"). diff --git a/tests/integration/api_actions_artifact_v4_test.go b/tests/integration/api_actions_artifact_v4_test.go index 9fa7590620..af0dcb98a9 100644 --- a/tests/integration/api_actions_artifact_v4_test.go +++ b/tests/integration/api_actions_artifact_v4_test.go @@ -59,7 +59,7 @@ func uploadArtifact(t *testing.T, body string) string { req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)) MakeRequest(t, req, http.StatusCreated) - t.Logf("Create artifact confirm") + t.Log("Create artifact confirm") sha := sha256.Sum256([]byte(body)) @@ -113,7 +113,7 @@ func TestActionsArtifactV4UploadSingleFileWrongChecksum(t *testing.T) { req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)) MakeRequest(t, req, http.StatusCreated) - t.Logf("Create artifact confirm") + t.Log("Create artifact confirm") sha := sha256.Sum256([]byte(strings.Repeat("A", 1024))) @@ -158,7 +158,7 @@ func TestActionsArtifactV4UploadSingleFileWithRetentionDays(t *testing.T) { req = NewRequestWithBody(t, "PUT", url, strings.NewReader(body)) MakeRequest(t, req, http.StatusCreated) - t.Logf("Create artifact confirm") + t.Log("Create artifact confirm") sha := sha256.Sum256([]byte(body)) @@ -221,7 +221,7 @@ func TestActionsArtifactV4UploadSingleFileWithPotentialHarmfulBlockID(t *testing req = NewRequestWithBody(t, "PUT", blockListURL, bytes.NewReader(rawBlockList)) MakeRequest(t, req, http.StatusCreated) - t.Logf("Create artifact confirm") + t.Log("Create artifact confirm") sha := sha256.Sum256([]byte(body)) @@ -286,7 +286,7 @@ func TestActionsArtifactV4UploadSingleFileWithChunksOutOfOrder(t *testing.T) { req = NewRequestWithBody(t, "PUT", blockListURL, bytes.NewReader(rawBlockList)) MakeRequest(t, req, http.StatusCreated) - t.Logf("Create artifact confirm") + t.Log("Create artifact confirm") sha := sha256.Sum256([]byte(bodya + bodyb)) diff --git a/tests/integration/repo_settings_test.go b/tests/integration/repo_settings_test.go index 8814a44699..63cc5332bc 100644 --- a/tests/integration/repo_settings_test.go +++ b/tests/integration/repo_settings_test.go @@ -367,7 +367,7 @@ func TestRepoFollowing(t *testing.T) { isLikeType := activityType == "Like" isCorrectObject := strings.HasSuffix(object, "/api/v1/activitypub/repository-id/1") if !isLikeType || !isCorrectObject { - t.Errorf("Activity is not a like for this repo") + t.Error("Activity is not a like for this repo") } }) } From d6ab2a464f1872532b6836b819387c83d8fb34a9 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 29 May 2025 17:45:18 +0200 Subject: [PATCH 49/94] fix: aggregate deleted team as ghost team (#7987) - If a review was requested from a deleted team, use the ghost team for the comment aggregator. - Resolves Codeberg/Community#1952 - Unit test added. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7987 Reviewed-by: Beowulf Co-authored-by: Gusted Co-committed-by: Gusted --- models/issues/comment.go | 7 ++++-- models/organization/team.go | 8 ++++++ routers/web/repo/action_aggregator_test.go | 29 ++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/models/issues/comment.go b/models/issues/comment.go index dc38f83f79..c44f65e29c 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -652,8 +652,11 @@ func (c *Comment) LoadAssigneeUserAndTeam(ctx context.Context) error { if c.Issue.Repo.Owner.IsOrganization() { c.AssigneeTeam, err = organization.GetTeamByID(ctx, c.AssigneeTeamID) - if err != nil && !organization.IsErrTeamNotExist(err) { - return err + if err != nil { + if !organization.IsErrTeamNotExist(err) { + return err + } + c.AssigneeTeam = organization.NewGhostTeam() } } } diff --git a/models/organization/team.go b/models/organization/team.go index 450082077f..c78eff39fb 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -292,3 +292,11 @@ func FixInconsistentOwnerTeams(ctx context.Context) (int64, error) { return int64(len(teamIDs)), nil } + +func NewGhostTeam() *Team { + return &Team{ + ID: -1, + Name: "Ghost team", + LowerName: "ghost team", + } +} diff --git a/routers/web/repo/action_aggregator_test.go b/routers/web/repo/action_aggregator_test.go index 385cf5cbe5..94e6d506c5 100644 --- a/routers/web/repo/action_aggregator_test.go +++ b/routers/web/repo/action_aggregator_test.go @@ -94,6 +94,14 @@ func reqReview(t int64, name string, delReq bool) *issue_model.Comment { return c } +func ghostReqReview(t, id int64) *issue_model.Comment { + c := testComment(t) + c.Type = issue_model.CommentTypeReviewRequest + c.AssigneeTeam = organization.NewGhostTeam() + c.AssigneeTeamID = id + return c +} + func reqReviewList(t int64, del bool, names ...string) *issue_model.Comment { req := []issue_model.RequestReviewTarget{} for _, name := range names { @@ -588,6 +596,27 @@ func TestCombineReviewRequests(t *testing.T) { reqReviewList(121, true, "titi", "toto-team"), }, }, + + // Ghost. + { + name: "ghost reviews", + beforeCombined: []*issue_model.Comment{ + reqReview(1, "titi", false), + ghostReqReview(2, 50), + ghostReqReview(3, 51), + ghostReqReview(4, 50), + }, + afterCombined: []*issue_model.Comment{ + { + PosterID: 1, + Type: issue_model.CommentTypeReviewRequest, + CreatedUnix: timeutil.TimeStamp(1), + AddedRequestReview: []issue_model.RequestReviewTarget{ + createReqReviewTarget("titi"), {Team: organization.NewGhostTeam()}, + }, + }, + }, + }, } for _, kase := range kases { From 8e2747859bbbbe87cca56fd8e1f794e5cf0aa86e Mon Sep 17 00:00:00 2001 From: Maks1mS Date: Thu, 29 May 2025 22:39:53 +0200 Subject: [PATCH 50/94] fix: ensure consistent empty repository topics field (#7920) Resolves #7878 An empty repository topic was not stored consistently across databases, this caused the `ONLY_SHOW_RELEVANT_REPOS` feature to not work correctly. Always store empty repository topics as an empty array to fix this. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7920 Reviewed-by: Gusted Co-authored-by: Maks1mS Co-committed-by: Maks1mS --- models/fixtures/repository.yml | 65 ++++++++++++++++++- models/forgejo_migrations/migrate.go | 2 + models/forgejo_migrations/v31.go | 29 +++++++++ models/forgejo_migrations/v31_test.go | 38 +++++++++++ .../Test_SetTopicsAsEmptySlice/repository.yml | 9 +++ .../repository.yml | 1 + models/repo/repo.go | 9 ++- services/repository/create_test.go | 10 +++ tests/e2e/fixtures/repository.yml | 1 + .../TestAdminDeleteUser/repository.yml | 1 + 10 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 models/forgejo_migrations/v31.go create mode 100644 models/forgejo_migrations/v31_test.go create mode 100644 models/migrations/fixtures/Test_SetTopicsAsEmptySlice/repository.yml diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index 0ba4d06e14..5941f82299 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -31,6 +31,8 @@ close_issues_via_commit_in_any_branch: false created_unix: 1731254961 updated_unix: 1731254961 + topics: '[]' + - id: 2 owner_id: 2 @@ -61,7 +63,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: true - + topics: '[]' - id: 3 owner_id: 3 @@ -94,6 +96,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1700000001 updated_unix: 1700000001 + topics: '[]' - id: 4 @@ -125,6 +128,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 5 @@ -158,6 +162,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1700000002 updated_unix: 1700000002 + topics: '[]' - id: 6 @@ -190,6 +195,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1710000001 updated_unix: 1710000001 + topics: '[]' - id: 7 @@ -222,6 +228,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1710000003 updated_unix: 1710000003 + topics: '[]' - id: 8 @@ -254,6 +261,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1710000002 updated_unix: 1710000002 + topics: '[]' - id: 9 @@ -284,6 +292,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 10 @@ -315,6 +324,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 11 @@ -346,6 +356,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 12 @@ -376,6 +387,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 13 @@ -406,6 +418,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 14 @@ -437,6 +450,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 15 @@ -468,6 +482,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 16 @@ -499,6 +514,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 17 @@ -529,6 +545,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 18 @@ -559,6 +576,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 19 @@ -589,6 +607,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 20 @@ -619,6 +638,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 21 @@ -649,6 +669,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 22 @@ -679,6 +700,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 23 @@ -709,6 +731,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 24 @@ -739,6 +762,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 25 @@ -769,6 +793,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 26 @@ -799,6 +824,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 27 @@ -829,6 +855,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 28 @@ -859,6 +886,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 29 @@ -889,6 +917,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 30 @@ -919,6 +948,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 31 @@ -950,6 +980,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 32 # org public repo @@ -982,6 +1013,7 @@ close_issues_via_commit_in_any_branch: false created_unix: 1700000003 updated_unix: 1700000003 + topics: '[]' - id: 33 @@ -1013,6 +1045,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 34 @@ -1043,6 +1076,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 35 @@ -1073,6 +1107,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 36 @@ -1104,6 +1139,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 37 @@ -1135,6 +1171,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 38 @@ -1166,6 +1203,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 39 @@ -1197,6 +1235,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 40 @@ -1228,6 +1267,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 41 @@ -1259,6 +1299,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 42 @@ -1290,6 +1331,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 43 @@ -1320,6 +1362,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 44 @@ -1351,6 +1394,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 45 @@ -1381,6 +1425,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 46 @@ -1412,6 +1457,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 47 @@ -1443,6 +1489,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 48 @@ -1474,6 +1521,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 49 @@ -1506,6 +1554,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 50 @@ -1537,6 +1586,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 51 @@ -1568,6 +1618,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 52 @@ -1599,6 +1650,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 53 @@ -1627,6 +1679,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 54 @@ -1639,6 +1692,7 @@ is_archived: false is_private: true status: 0 + topics: '[]' - id: 55 @@ -1651,6 +1705,7 @@ is_private: true num_issues: 1 status: 0 + topics: '[]' - id: 56 @@ -1664,6 +1719,7 @@ is_private: true status: 0 num_issues: 0 + topics: '[]' - id: 57 @@ -1677,6 +1733,7 @@ is_private: false status: 0 num_issues: 0 + topics: '[]' - id: 58 # org public repo @@ -1708,6 +1765,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 1059 @@ -1721,6 +1779,7 @@ is_private: false status: 0 num_issues: 0 + topics: '[]' - id: 59 @@ -1734,6 +1793,7 @@ is_private: true status: 0 num_issues: 0 + topics: '[]' - id: 60 @@ -1765,6 +1825,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 61 @@ -1796,6 +1857,7 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' - id: 62 owner_id: 2 @@ -1826,3 +1888,4 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index d79bb1e455..41197c434d 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -99,6 +99,8 @@ var migrations = []*Migration{ NewMigration("Add public key information to `FederatedUser` and `FederationHost`", AddPublicKeyInformationForFederation), // v29 -> v30 NewMigration("Migrate `User.NormalizedFederatedURI` column to extract port & schema into FederatedHost", MigrateNormalizedFederatedURI), + // v30 -> v31 + NewMigration("Normalize repository.topics to empty slice instead of null", SetTopicsAsEmptySlice), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v31.go b/models/forgejo_migrations/v31.go new file mode 100644 index 0000000000..3f31cd2e92 --- /dev/null +++ b/models/forgejo_migrations/v31.go @@ -0,0 +1,29 @@ +// Copyright 2025 The Forgejo Authors. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "xorm.io/xorm" + "xorm.io/xorm/schemas" +) + +func SetTopicsAsEmptySlice(x *xorm.Engine) error { + var err error + if x.Dialect().URI().DBType == schemas.POSTGRES { + _, err = x.Exec("UPDATE `repository` SET topics = '[]' WHERE topics IS NULL OR topics::text = 'null'") + } else { + _, err = x.Exec("UPDATE `repository` SET topics = '[]' WHERE topics IS NULL") + } + + if err != nil { + return err + } + + type Repository struct { + ID int64 `xorm:"pk autoincr"` + Topics []string `xorm:"TEXT JSON NOT NULL"` + } + + return x.Sync(new(Repository)) +} diff --git a/models/forgejo_migrations/v31_test.go b/models/forgejo_migrations/v31_test.go new file mode 100644 index 0000000000..5b4aac2a60 --- /dev/null +++ b/models/forgejo_migrations/v31_test.go @@ -0,0 +1,38 @@ +// Copyright 2025 The Forgejo Authors. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "testing" + + migration_tests "forgejo.org/models/migrations/test" + + "github.com/stretchr/testify/require" +) + +func Test_SetTopicsAsEmptySlice(t *testing.T) { + type Repository struct { + ID int64 `xorm:"pk autoincr"` + Topics []string `xorm:"TEXT JSON"` + } + + x, deferable := migration_tests.PrepareTestEnv(t, 0, new(Repository)) + defer deferable() + if x == nil || t.Failed() { + return + } + + require.NoError(t, SetTopicsAsEmptySlice(x)) + + var repos []Repository + require.NoError(t, x.Find(&repos)) + + for _, repo := range repos { + if repo.ID == 2 { + require.Equal(t, []string{"go", "dev"}, repo.Topics, "Valid topics should remain unchanged") + } else { + require.Equal(t, []string{}, repo.Topics, "NULL topics should be set to empty array") + } + } +} diff --git a/models/migrations/fixtures/Test_SetTopicsAsEmptySlice/repository.yml b/models/migrations/fixtures/Test_SetTopicsAsEmptySlice/repository.yml new file mode 100644 index 0000000000..e37b0b25b5 --- /dev/null +++ b/models/migrations/fixtures/Test_SetTopicsAsEmptySlice/repository.yml @@ -0,0 +1,9 @@ +# type Repository struct { +# ID int64 `xorm:"pk autoincr"` +# Topics []string `xorm:"TEXT JSON"` +# } +- + id: 1 +- + id: 2 + topics: '["go", "dev"]' diff --git a/models/repo/TestSearchRepositoryIDsByCondition/repository.yml b/models/repo/TestSearchRepositoryIDsByCondition/repository.yml index 9ce830783d..b10fbc9226 100644 --- a/models/repo/TestSearchRepositoryIDsByCondition/repository.yml +++ b/models/repo/TestSearchRepositoryIDsByCondition/repository.yml @@ -28,3 +28,4 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' diff --git a/models/repo/repo.go b/models/repo/repo.go index 6bf03d724c..688c03b3d5 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -183,7 +183,7 @@ type Repository struct { StatsIndexerStatus *RepoIndexerStatus `xorm:"-"` IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"` CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"` - Topics []string `xorm:"TEXT JSON"` + Topics []string `xorm:"TEXT JSON NOT NULL"` ObjectFormatName string `xorm:"VARCHAR(6) NOT NULL DEFAULT 'sha1'"` TrustModel TrustModelType @@ -196,6 +196,13 @@ type Repository struct { ArchivedUnix timeutil.TimeStamp `xorm:"DEFAULT 0"` } +// BeforeInsert will be invoked by XORM before updating a record +func (repo *Repository) BeforeInsert() { + if repo.Topics == nil { + repo.Topics = []string{} + } +} + func init() { db.RegisterModel(new(Repository)) } diff --git a/services/repository/create_test.go b/services/repository/create_test.go index 7eb3c0f805..0a6c34b6fe 100644 --- a/services/repository/create_test.go +++ b/services/repository/create_test.go @@ -147,3 +147,13 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { } require.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization") } + +func TestCreateRepository(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + + r, err := CreateRepositoryDirectly(db.DefaultContext, user, user, CreateRepoOptions{Name: "repo-last"}) + require.NoError(t, err) + require.NotNil(t, r.Topics) + require.Empty(t, r.Topics) +} diff --git a/tests/e2e/fixtures/repository.yml b/tests/e2e/fixtures/repository.yml index ac87721d6a..6afbd138e8 100644 --- a/tests/e2e/fixtures/repository.yml +++ b/tests/e2e/fixtures/repository.yml @@ -10,3 +10,4 @@ is_private: true status: 0 lfs_size: 8192 + topics: '[]' diff --git a/tests/integration/fixtures/TestAdminDeleteUser/repository.yml b/tests/integration/fixtures/TestAdminDeleteUser/repository.yml index 2c12c7e1de..d71232d834 100644 --- a/tests/integration/fixtures/TestAdminDeleteUser/repository.yml +++ b/tests/integration/fixtures/TestAdminDeleteUser/repository.yml @@ -28,3 +28,4 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + topics: '[]' From 161924abf830de48cf58dfb60addbb56287399b2 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 30 May 2025 13:01:08 +0200 Subject: [PATCH 51/94] chore(cleanup): suppress non actionable XORM warnings (#8021) The following will trigger a XORM warning: ``` type Repository struct { ID int64 `xorm:"pk autoincr"` Topics []string `xorm:"TEXT JSON NOT NULL"` } ``` that looks like: ``` [W] Table repository Column topics db default is '', struct default is ``` it cannot be resolved because: - SQLite requires a default when there is a NOT NULL - MySQL forbids a default for TEXT Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8021 Reviewed-by: Gusted Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- models/db/log.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/models/db/log.go b/models/db/log.go index b94af8e39c..387709cc50 100644 --- a/models/db/log.go +++ b/models/db/log.go @@ -69,6 +69,9 @@ func (l *XORMLogBridge) Warn(v ...any) { // Warnf show warning log func (l *XORMLogBridge) Warnf(format string, v ...any) { + if format == "Table %s Column %s db default is %s, struct default is %s" || format == "Table %s Column %s db nullable is %v, struct nullable is %v" { + return + } l.Log(stackLevel, log.WARN, format, v...) } From baa93ec66ecb7f0fe3cd6e19e9451523033167fb Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Fri, 30 May 2025 13:07:35 +0200 Subject: [PATCH 52/94] enhance validateable interface (#7714) This PR is part of https://codeberg.org/forgejo/forgejo/pulls/4767 filed by @algernon Validateable is enhanced for less duplication. ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. ### Release notes - [x] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. Co-authored-by: zam Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7714 Reviewed-by: Earl Warren Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- .deadcode-out | 1 + modules/forgefed/activity_like_test.go | 10 +---- modules/forgefed/actor_test.go | 2 +- modules/validation/validatable.go | 11 ++++- modules/validation/validatable_test.go | 56 +++++++++++++++++--------- 5 files changed, 51 insertions(+), 29 deletions(-) diff --git a/.deadcode-out b/.deadcode-out index 9641f272b3..dd81cc6c3f 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -204,6 +204,7 @@ forgejo.org/modules/util/filebuffer forgejo.org/modules/validation IsErrNotValid + ValidateIDExists forgejo.org/modules/web RouteMock diff --git a/modules/forgefed/activity_like_test.go b/modules/forgefed/activity_like_test.go index f4cb7e4e00..6b252d5960 100644 --- a/modules/forgefed/activity_like_test.go +++ b/modules/forgefed/activity_like_test.go @@ -143,7 +143,7 @@ func Test_ForgeLikeValidation(t *testing.T) { "actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", "object":"https://codeberg.org/api/activitypub/repository-id/1", "startTime": "2014-12-31T23:00:00-08:00"}`)) - if err := validateAndCheckError(sut, "Value bad-type is not contained in allowed values [Like]"); err != nil { + if err := validateAndCheckError(sut, "Field type contains the value bad-type, which is not in allowed subset [Like]"); err != nil { t.Error(err) } @@ -154,14 +154,6 @@ func Test_ForgeLikeValidation(t *testing.T) { if err := validateAndCheckError(sut, "StartTime was invalid."); err != nil { t.Error(err) } - - sut.UnmarshalJSON([]byte(`{"type":"Wrong", - "actor":"https://repo.prod.meissa.de/api/activitypub/user-id/1", - "object":"https://codeberg.org/api/activitypub/repository-id/1", - "startTime": "2014-12-31T23:00:00-08:00"}`)) - if err := validateAndCheckError(sut, "Value Wrong is not contained in allowed values [Like]"); err != nil { - t.Error(err) - } } func TestActivityValidation_Attack(t *testing.T) { diff --git a/modules/forgefed/actor_test.go b/modules/forgefed/actor_test.go index 10b6578953..4d58cfd806 100644 --- a/modules/forgefed/actor_test.go +++ b/modules/forgefed/actor_test.go @@ -145,7 +145,7 @@ func TestPersonIdValidation(t *testing.T) { sut.HostPort = 443 sut.IsPortSupplemented = true sut.UnvalidatedInput = "https://an.other.host/api/v1/activitypub/user-id/1" - if sut.Validate()[0] != "Value forgejox is not contained in allowed values [forgejo gitea]" { + if sut.Validate()[0] != "Field Source contains the value forgejox, which is not in allowed subset [forgejo gitea]" { t.Errorf("validation error expected but was: %v\n", sut.Validate()[0]) } } diff --git a/modules/validation/validatable.go b/modules/validation/validatable.go index d2c5553259..4500f6e53d 100644 --- a/modules/validation/validatable.go +++ b/modules/validation/validatable.go @@ -10,6 +10,8 @@ import ( "unicode/utf8" "forgejo.org/modules/timeutil" + + ap "github.com/go-ap/activitypub" ) // ErrNotValid represents an validation error @@ -41,6 +43,13 @@ func IsValid(v Validateable) (bool, error) { return true, nil } +func ValidateIDExists(value ap.Item, name string) []string { + if value == nil { + return []string{fmt.Sprintf("%v should not be nil", name)} + } + return ValidateNotEmpty(value.GetID().String(), name) +} + func ValidateNotEmpty(value any, name string) []string { isValid := true switch v := value.(type) { @@ -83,5 +92,5 @@ func ValidateOneOf(value any, allowed []any, name string) []string { return []string{} } } - return []string{fmt.Sprintf("Value %v is not contained in allowed values %v", value, allowed)} + return []string{fmt.Sprintf("Field %s contains the value %v, which is not in allowed subset %v", name, value, allowed)} } diff --git a/modules/validation/validatable_test.go b/modules/validation/validatable_test.go index c843afe702..1fe407b343 100644 --- a/modules/validation/validatable_test.go +++ b/modules/validation/validatable_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. +// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package validation @@ -7,6 +7,9 @@ import ( "testing" "forgejo.org/modules/timeutil" + + ap "github.com/go-ap/activitypub" + "github.com/stretchr/testify/assert" ) type Sut struct { @@ -37,33 +40,50 @@ func Test_IsValid(t *testing.T) { func Test_ValidateNotEmpty_ForString(t *testing.T) { sut := "" - if len(ValidateNotEmpty(sut, "dummyField")) == 0 { - t.Error("sut should be invalid") - } + res := ValidateNotEmpty(sut, "dummyField") + assert.Len(t, res, 1) + sut = "not empty" - if res := ValidateNotEmpty(sut, "dummyField"); len(res) > 0 { - t.Errorf("sut should be valid but was %q", res) - } + res = ValidateNotEmpty(sut, "dummyField") + assert.Empty(t, res, 0) } func Test_ValidateNotEmpty_ForTimestamp(t *testing.T) { sut := timeutil.TimeStamp(0) - if res := ValidateNotEmpty(sut, "dummyField"); len(res) == 0 { - t.Error("sut should be invalid") - } + res := ValidateNotEmpty(sut, "dummyField") + assert.Len(t, res, 1) + sut = timeutil.TimeStampNow() - if res := ValidateNotEmpty(sut, "dummyField"); len(res) > 0 { - t.Errorf("sut should be valid but was %q", res) + res = ValidateNotEmpty(sut, "dummyField") + assert.Empty(t, res, 0) +} + +func Test_ValidateIDExists_ForItem(t *testing.T) { + sut := ap.Activity{ + Object: nil, } + res := ValidateIDExists(sut.Object, "dummyField") + assert.Len(t, res, 1) + + sut = ap.Activity{ + Object: ap.IRI(""), + } + res = ValidateIDExists(sut.Object, "dummyField") + assert.Len(t, res, 1) + + sut = ap.Activity{ + Object: ap.IRI("https://dummy.link/id"), + } + res = ValidateIDExists(sut.Object, "dummyField") + assert.Empty(t, res, 0) } func Test_ValidateMaxLen(t *testing.T) { sut := "0123456789" - if len(ValidateMaxLen(sut, 9, "dummyField")) == 0 { - t.Error("sut should be invalid") - } + res := ValidateMaxLen(sut, 9, "dummyField") + assert.Len(t, res, 1) + sut = "0123456789" - if res := ValidateMaxLen(sut, 11, "dummyField"); len(res) > 0 { - t.Errorf("sut should be valid but was %q", res) - } + res = ValidateMaxLen(sut, 11, "dummyField") + assert.Empty(t, res, 0) } From 21151ea5ce767f09dfd9acfb2bdbf8b1a0023bd7 Mon Sep 17 00:00:00 2001 From: Julian Schlarb Date: Sun, 1 Jun 2025 09:02:29 +0200 Subject: [PATCH 53/94] fix: maven use groupId:artifactId for package name concatenation (#6352) Second part of #6327 to fix the Maven package naming. This pull request includes: * Changing the group and artifact IDs from being separated by `-` to `:` as suggested by [Maven](https://maven.apache.org/pom.html#Maven_Coordinates). * Making Maven package names case-sensitive * Migrating the database to: * Handle collisions of package names (e.g., groupId: foo- with artifactId: bar and groupId: foo with artifactId: -bar) by moving them into their own packages. * Fix the missing group ID issue (#6329). * Update lower_name to match the name value for maven pkgs to make it case-sensetive. ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6352 Reviewed-by: Earl Warren Co-authored-by: Julian Schlarb Co-committed-by: Julian Schlarb --- models/forgejo_migrations/migrate.go | 2 + models/forgejo_migrations/v32.go | 414 +++++++++++ models/forgejo_migrations/v32_test.go | 369 +++++++++ .../package.yml | 66 ++ .../package_blob.yml | 641 ++++++++++++++++ .../package_file.yml | 698 ++++++++++++++++++ .../package_version.yml | 281 +++++++ models/packages/package.go | 11 +- models/packages/package_file.go | 11 +- models/packages/package_version.go | 7 +- routers/api/packages/generic/generic.go | 4 +- routers/api/packages/maven/maven.go | 27 +- services/packages/packages.go | 12 +- tests/integration/api_packages_maven_test.go | 6 +- 14 files changed, 2522 insertions(+), 27 deletions(-) create mode 100644 models/forgejo_migrations/v32.go create mode 100644 models/forgejo_migrations/v32_test.go create mode 100644 models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package.yml create mode 100644 models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_blob.yml create mode 100644 models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_file.yml create mode 100644 models/migrations/fixtures/Test_ChangeMavenArtifactConcatenation/package_version.yml diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index 41197c434d..21a2077d06 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -101,6 +101,8 @@ var migrations = []*Migration{ NewMigration("Migrate `User.NormalizedFederatedURI` column to extract port & schema into FederatedHost", MigrateNormalizedFederatedURI), // v30 -> v31 NewMigration("Normalize repository.topics to empty slice instead of null", SetTopicsAsEmptySlice), + // v31 -> v32 + NewMigration("Migrate maven package name concatenation", ChangeMavenArtifactConcatenation), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v32.go b/models/forgejo_migrations/v32.go new file mode 100644 index 0000000000..bed335ab6b --- /dev/null +++ b/models/forgejo_migrations/v32.go @@ -0,0 +1,414 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "encoding/xml" + "fmt" + "regexp" + "slices" + "sort" + "strconv" + "strings" + + "forgejo.org/models/packages" + "forgejo.org/modules/json" + "forgejo.org/modules/log" + "forgejo.org/modules/packages/maven" + packages_service "forgejo.org/services/packages" + + "golang.org/x/net/context" + "xorm.io/xorm" +) + +var getPackage = packages_service.GetPackageFileStream + +type Snapshot struct { + baseVersion string + date string + time string + build int +} + +type Metadata struct { + XMLName xml.Name `xml:"metadata"` + ModelVersion string `xml:"modelVersion,attr"` + GroupID string `xml:"groupId"` + ArtifactID string `xml:"artifactId"` + Version string `xml:"version"` +} + +type mavenPackageResult struct { + PackageFile *packages.PackageFile `xorm:"extends"` + PackageVersion *packages.PackageVersion `xorm:"extends"` + Package *packages.Package `xorm:"extends"` + PackageName string `xorm:"-"` + Snapshot *Snapshot `xorm:"-"` + GroupID string `xorm:"-"` + ArtifactID string `xorm:"-"` +} + +// ChangeMavenArtifactConcatenation resolves old dash-concatenated Maven coordinates and regenerates metadata. +// Note: runs per-owner in a single transaction; failures roll back all owners. +func ChangeMavenArtifactConcatenation(x *xorm.Engine) error { + sess := x.NewSession() + defer sess.Close() + + if err := sess.Begin(); err != nil { + return err + } + + // get unique owner IDs of Maven packages + var ownerIDs []*int64 + if err := sess. + Table("package"). + Select("package.owner_id"). + Where("package.type = 'maven'"). + GroupBy("package.owner_id"). + OrderBy("package.owner_id DESC"). + Find(&ownerIDs); err != nil { + return err + } + + for _, id := range ownerIDs { + if err := fixMavenArtifactPerOwner(sess, id); err != nil { + log.Error("owner %d migration failed: %v", id, err) + return err // rollback all + } + } + + return sess.Commit() +} + +func fixMavenArtifactPerOwner(sess *xorm.Session, ownerID *int64) error { + results, err := getMavenPackageResultsToUpdate(sess, ownerID) + if err != nil { + return err + } + + if err = resolvePackageCollisions(results, sess); err != nil { + return err + } + + if err = processPackageVersions(results, sess); err != nil { + return err + } + + return processPackageFiles(results, sess) +} + +// processPackageFiles updates Maven package files and versions in the database +// Returns an error if any database or processing operation fails. +func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) error { + processedVersion := make(map[string][]*mavenPackageResult) + + for _, r := range results { + if r.Snapshot != nil { + key := fmt.Sprintf("%s:%s", r.PackageName, r.PackageVersion.LowerVersion) + processedVersion[key] = append(processedVersion[key], r) + } + + // Only update version_id when it differs + if r.PackageVersion.ID != r.PackageFile.VersionID { + pattern := strings.TrimSuffix(r.PackageFile.Name, ".pom") + "%" + // Per routers/api/packages/maven/maven.go:338, POM files already have the `IsLead`, so no update needed for this prop + if _, err := sess.Exec("UPDATE package_file SET version_id = ? WHERE version_id = ? and name like ?", r.PackageVersion.ID, r.PackageFile.VersionID, pattern); err != nil { + return err + } + } + } + + // If maven-metadata.xml is missing (snapshot path collision), skip regeneration + // Without this metadata, Maven cannot resolve snapshot details + for _, packageResults := range processedVersion { + sort.Slice(packageResults, func(i, j int) bool { + return packageResults[i].Snapshot.build > packageResults[j].Snapshot.build + }) + + rs := packageResults[0] + + pf, md, err := parseMetadata(sess, rs) + if err != nil { + return err + } + + if pf != nil && md != nil && md.GroupID == rs.GroupID && md.ArtifactID == rs.ArtifactID { + if pf.VersionID != rs.PackageFile.VersionID { + if _, err := sess.ID(pf.ID).Cols("version_id").Update(pf); err != nil { + return err + } + } + continue + } + + log.Warn("no maven-metadata.xml found for (id: %d) [%s:%s]", rs.PackageVersion.ID, rs.PackageName, rs.PackageVersion.Version) + } + + return nil +} + +// parseMetadata retrieves metadata for a Maven package file from the database and decodes it into a Metadata object. +// Returns the associated PackageFile, Metadata, and any error encountered during processing. +func parseMetadata(sess *xorm.Session, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { + ctx := context.Background() + + var pf packages.PackageFile + found, err := sess.Table(pf). + Where("version_id = ?", snapshot.PackageFile.VersionID). // still the old id + And("lower_name = ?", "maven-metadata.xml"). + Get(&pf) + if err != nil { + return nil, nil, err + } + + if !found { + return nil, nil, nil + } + + s, _, _, err := getPackage(ctx, &pf) + if err != nil { + return nil, nil, err + } + + defer s.Close() + dec := xml.NewDecoder(s) + var m Metadata + if err := dec.Decode(&m); err != nil { + return nil, nil, err + } + + return &pf, &m, nil +} + +// processPackageVersions processes Maven package versions by updating metadata or inserting new records as necessary. +// It avoids redundant updates by tracking already processed versions using a map. Returns an error on failure. +func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) error { + processedVersion := make(map[string]int64) + + for _, r := range results { + key := fmt.Sprintf("%s:%s", r.PackageName, r.PackageVersion.Version) + + if id, ok := processedVersion[key]; ok { + r.PackageVersion.ID = id + continue + } + + // for non collisions, just update the metadata + if r.PackageVersion.PackageID == r.Package.ID { + if _, err := sess.ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil { + return err + } + } else { + log.Info("Create new maven package version for %s:%s", r.PackageName, r.PackageVersion.Version) + r.PackageVersion.ID = 0 + r.PackageVersion.PackageID = r.Package.ID + if _, err := sess.Insert(r.PackageVersion); err != nil { + return err + } + } + + processedVersion[key] = r.PackageVersion.ID + } + + return nil +} + +// getMavenPackageResultsToUpdate retrieves Maven package results that need updates based on the owner ID. +// It processes POM metadata, fixes package inconsistencies, and filters corrupted package versions. +func getMavenPackageResultsToUpdate(sess *xorm.Session, ownerID *int64) ([]*mavenPackageResult, error) { + ctx := context.Background() + var candidates []*mavenPackageResult + if err := sess. + Table("package_file"). + Select("package_file.*, package_version.*, package.*"). + Join("INNER", "package_version", "package_version.id = package_file.version_id"). + Join("INNER", "package", "package.id = package_version.package_id"). + Where("package_file.lower_name LIKE ?", "%.pom"). + And("package.type = ?", "maven"). + And("package.owner_id = ?", ownerID). + OrderBy("package_version.id DESC, package_file.id DESC"). + Find(&candidates); err != nil { + return nil, err + } + + var results []*mavenPackageResult + var corruptedVersionIDs []int64 + + // fetch actual metadata from blob as all packages needs to be fixed following the new string concatenation + for _, r := range candidates { + if err := processPomMetadata(ctx, r); err != nil { + // Skip corrupted versions; admin intervention may be needed to repair these files. + log.Warn("Failed to process package file [id: %d] ignoring package version[%d]: %v", r.PackageFile.ID, r.PackageVersion.ID, err) + + corruptedVersionIDs = append(corruptedVersionIDs, r.PackageVersion.ID) + + continue + } + + results = append(results, r) + log.Debug("Resolved id [%d] from [%s:%s] to [%s:%s] [Snapshot: %v]", r.Package.ID, r.Package.Name, r.PackageVersion.Version, r.PackageName, r.PackageVersion.Version, r.Snapshot) + } + + for _, corruptedVersionID := range corruptedVersionIDs { + for i := 0; i < len(results); { + if corruptedVersionID == results[i].PackageVersion.ID { + results = append(results[:i], results[i+1:]...) + } else { + i++ + } + } + } + + return results, nil +} + +// resolvePackageCollisions handles name collisions by keeping the first existing record and inserting new Package records for subsequent collisions. +// Returns a map from PackageName to its resolved Package.ID. +func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) error { + // Group new names by lowerName + collisions := make(map[string][]string) + for _, r := range results { + names := collisions[r.Package.LowerName] + if !slices.Contains(names, r.PackageName) { + collisions[r.Package.LowerName] = append(names, r.PackageName) + } + } + + pkgIDByName := make(map[string]int64) + var err error + + for _, r := range results { + list := collisions[r.Package.LowerName] + + // update to the upcoming package name which is colon separated + r.Package.Name = r.PackageName + r.Package.LowerName = r.PackageName + + // exiting entry + if id, ok := pkgIDByName[r.PackageName]; ok { + r.Package.ID = id + // first package kept the current id + } else if list[0] == r.PackageName { + pkgIDByName[r.PackageName] = r.Package.ID + + if _, err = sess.ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { + return err + } + // create a new entry + } else { + log.Info("Create new maven package for %s", r.Package.Name) + + r.Package.ID = 0 + if _, err = sess.Insert(r.Package); err != nil { + return err + } + + pkgIDByName[r.PackageName] = r.Package.ID + } + } + + return nil +} + +// processPomMetadata processes a Maven package file, parses its POM metadata, and updates PackageVersion information. +func processPomMetadata(ctx context.Context, mpr *mavenPackageResult) error { + s, _, _, err := getPackage(ctx, mpr.PackageFile) + if err != nil { + return fmt.Errorf("unable to get package stream: %v", err) + } + defer s.Close() + + actualPom, err := maven.ParsePackageMetaData(s) + if err != nil { + return fmt.Errorf("failed to parse POM metadata: %v", err) + } + + raw, err := json.Marshal(actualPom) + if err != nil { + return fmt.Errorf("failed to marshal metadata: %v", err) + } + + var currentPom *maven.Metadata + if err = json.Unmarshal([]byte(mpr.PackageVersion.MetadataJSON), ¤tPom); err != nil { + return fmt.Errorf("failed to unmarshal metadata: %v", err) + } + + // since the rest api can also be (ab)used to upload artifacts wrong, just ignore them + if isInvalidMatch(currentPom, actualPom) { + return fmt.Errorf("artifact mismatch: actual [%s] expected [%s]", actualPom.ArtifactID, currentPom.ArtifactID) + } + + // this will also fix packages that missed its groupID + // Ref: https://codeberg.org/forgejo/forgejo/pulls/6329 + mpr.PackageVersion.MetadataJSON = string(raw) + + // Since Maven packages are case-sensitive, avoid potential clashes and clean-ups + // by enforcing consistent case handling similar to RPM packages. + mpr.PackageName = fmt.Sprintf("%s:%s", actualPom.GroupID, actualPom.ArtifactID) + + mpr.GroupID = actualPom.GroupID + mpr.ArtifactID = actualPom.ArtifactID + + if strings.HasSuffix(mpr.PackageVersion.Version, "-SNAPSHOT") { + snap, err := extraSnapshotDetails(currentPom, actualPom, mpr) + if err != nil { + return err + } + mpr.Snapshot = snap + } else { + // only snapshots are affected but kept in case of not complete fixtures + expectedFileName := fmt.Sprintf("%s-%s.pom", actualPom.ArtifactID, mpr.PackageVersion.Version) + if mpr.PackageFile.Name != expectedFileName { + log.Warn("invalid package file name - this is a collision which needs to be resolved expected [%s], actual [%s]", expectedFileName, mpr.PackageFile.Name) + } + } + + return nil +} + +// extraSnapshotDetails extracts detailed snapshot information +// Returns a Snapshot object encapsulating the extracted details or an error if the filename is invalid or parsing fails. +func extraSnapshotDetails(currentPom, actualPom *maven.Metadata, mpr *mavenPackageResult) (*Snapshot, error) { + pattern := `^%s-` + + `(?P[\d\.]+)-` + + `(?P\d{8})\.` + + `(?P