diff --git a/.gitignore b/.gitignore
index f89f5021b1..878ab3bc81 100644
--- a/.gitignore
+++ b/.gitignore
@@ -113,3 +113,6 @@ prime/
 
 # Manpage
 /man
+
+# Generated merged Forgejo+Gitea language files
+/options/locale/locale_*
diff --git a/Makefile b/Makefile
index 75087f37d0..724511a29f 100644
--- a/Makefile
+++ b/Makefile
@@ -834,10 +834,14 @@ generate: generate-backend
 generate-backend: $(TAGS_PREREQ) generate-go
 
 .PHONY: generate-go
-generate-go: $(TAGS_PREREQ)
+generate-go: $(TAGS_PREREQ) merge-locales
 	@echo "Running go generate..."
 	@CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES)
 
+.PHONY: merge-locales
+merge-locales:
+	@CC= GOOS= GOARCH= $(GO) run build/merge-forgejo-locales.go
+
 .PHONY: security-check
 security-check:
 	go run $(GOVULNCHECK_PACKAGE) ./...
@@ -1010,13 +1014,7 @@ lockfile-check:
 
 .PHONY: update-translations
 update-translations:
-	mkdir -p ./translations
-	cd ./translations && curl -L https://crowdin.com/download/project/gitea.zip > gitea.zip && unzip gitea.zip
-	rm ./translations/gitea.zip
-	$(SED_INPLACE) -e 's/="/=/g' -e 's/"$$//g' ./translations/*.ini
-	$(SED_INPLACE) -e 's/\\"/"/g' ./translations/*.ini
-	mv ./translations/*.ini ./options/locale/
-	rmdir ./translations
+	# noop to detect merge conflicts (potentially needs updating the scripts) and avoid breaking with Gitea
 
 .PHONY: generate-license
 generate-license:
diff --git a/build/merge-forgejo-locales.go b/build/merge-forgejo-locales.go
new file mode 100644
index 0000000000..730f51c62e
--- /dev/null
+++ b/build/merge-forgejo-locales.go
@@ -0,0 +1,99 @@
+// Copyright 2022 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+import (
+	"bufio"
+	"os"
+	"regexp"
+	"strings"
+
+	"gopkg.in/ini.v1"
+)
+
+const (
+	trimPrefix   = "gitea_"
+	sourceFolder = "options/locales/"
+)
+
+// returns list of locales, still containing the file extension!
+func generate_locale_list() []string {
+	localeFiles, _ := os.ReadDir(sourceFolder)
+	locales := []string{}
+	for _, localeFile := range localeFiles {
+		if !localeFile.IsDir() && strings.HasPrefix(localeFile.Name(), trimPrefix) {
+			locales = append(locales, strings.TrimPrefix(localeFile.Name(), trimPrefix))
+		}
+	}
+	return locales
+}
+
+// replace all occurrences of Gitea with Forgejo
+func renameGiteaForgejo(filename string) []byte {
+	file, err := os.Open(filename)
+	if err != nil {
+		panic(err)
+	}
+
+	replacer := strings.NewReplacer(
+		"Gitea", "Forgejo",
+		"https://docs.gitea.io/en-us/install-from-binary/", "https://forgejo.org/download/#installation-from-binary",
+		"https://github.com/go-gitea/gitea/tree/master/docker", "https://forgejo.org/download/#container-image",
+		"https://docs.gitea.io/en-us/install-from-package/", "https://forgejo.org/download",
+		"https://code.gitea.io/gitea", "https://forgejo.org/download",
+		"code.gitea.io/gitea", "Forgejo",
+		`<a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a>`, `<a href="https://codeberg.org/forgejo/forgejo/issues" target="_blank">Codeberg</a>`,
+		"https://github.com/go-gitea/gitea", "https://codeberg.org/forgejo/forgejo",
+		"https://blog.gitea.io", "https://forgejo.org/news",
+		"https://docs.gitea.io/en-us/protected-tags/", "https://forgejo.org/docs/latest/user/protection/#protected-tags",
+		"https://docs.gitea.io/en-us/webhooks/", "https://forgejo.org/docs/latest/user/webhooks/",
+	)
+
+	out := make([]byte, 0, 1024)
+	scanner := bufio.NewScanner(file)
+	scanner.Split(bufio.ScanLines)
+	for scanner.Scan() {
+		line := scanner.Text()
+
+		if strings.HasPrefix(line, "license_desc=") {
+			line = strings.Replace(line, "GitHub", "Forgejo", 1)
+		}
+
+		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
+			out = append(out, []byte("\n"+line+"\n")...)
+		} else if strings.HasPrefix(line, "settings.web_hook_name_gitea") {
+			out = append(out, []byte("\n"+line+"\n")...)
+			out = append(out, []byte("settings.web_hook_name_forgejo = Forgejo\n")...)
+		} else if strings.HasPrefix(line, "migrate.gitea.description") {
+			re := regexp.MustCompile(`(.*Gitea)`)
+			out = append(out, []byte(re.ReplaceAllString(line, "${1}/Forgejo")+"\n")...)
+		} else {
+			out = append(out, []byte(replacer.Replace(line)+"\n")...)
+		}
+	}
+	file.Close()
+	return out
+}
+
+func main() {
+	locales := generate_locale_list()
+	var err error
+	var localeFile *ini.File
+	for _, locale := range locales {
+		giteaLocale := sourceFolder + "gitea_" + locale
+		localeFile, err = ini.LoadSources(ini.LoadOptions{
+			IgnoreInlineComment: true,
+		}, giteaLocale, renameGiteaForgejo(giteaLocale))
+		if err != nil {
+			panic(err)
+		}
+		err = localeFile.SaveTo("options/locale/locale_" + locale)
+		if err != nil {
+			panic(err)
+		}
+	}
+}
diff --git a/options/locale/locale_cs-CZ.ini b/options/locales/gitea_cs-CZ.ini
similarity index 100%
rename from options/locale/locale_cs-CZ.ini
rename to options/locales/gitea_cs-CZ.ini
diff --git a/options/locale/locale_de-DE.ini b/options/locales/gitea_de-DE.ini
similarity index 100%
rename from options/locale/locale_de-DE.ini
rename to options/locales/gitea_de-DE.ini
diff --git a/options/locale/locale_el-GR.ini b/options/locales/gitea_el-GR.ini
similarity index 100%
rename from options/locale/locale_el-GR.ini
rename to options/locales/gitea_el-GR.ini
diff --git a/options/locale/locale_en-US.ini b/options/locales/gitea_en-US.ini
similarity index 100%
rename from options/locale/locale_en-US.ini
rename to options/locales/gitea_en-US.ini
diff --git a/options/locale/locale_es-ES.ini b/options/locales/gitea_es-ES.ini
similarity index 100%
rename from options/locale/locale_es-ES.ini
rename to options/locales/gitea_es-ES.ini
diff --git a/options/locale/locale_fa-IR.ini b/options/locales/gitea_fa-IR.ini
similarity index 100%
rename from options/locale/locale_fa-IR.ini
rename to options/locales/gitea_fa-IR.ini
diff --git a/options/locale/locale_fi-FI.ini b/options/locales/gitea_fi-FI.ini
similarity index 100%
rename from options/locale/locale_fi-FI.ini
rename to options/locales/gitea_fi-FI.ini
diff --git a/options/locale/locale_fr-FR.ini b/options/locales/gitea_fr-FR.ini
similarity index 100%
rename from options/locale/locale_fr-FR.ini
rename to options/locales/gitea_fr-FR.ini
diff --git a/options/locale/locale_hu-HU.ini b/options/locales/gitea_hu-HU.ini
similarity index 100%
rename from options/locale/locale_hu-HU.ini
rename to options/locales/gitea_hu-HU.ini
diff --git a/options/locale/locale_id-ID.ini b/options/locales/gitea_id-ID.ini
similarity index 100%
rename from options/locale/locale_id-ID.ini
rename to options/locales/gitea_id-ID.ini
diff --git a/options/locale/locale_is-IS.ini b/options/locales/gitea_is-IS.ini
similarity index 100%
rename from options/locale/locale_is-IS.ini
rename to options/locales/gitea_is-IS.ini
diff --git a/options/locale/locale_it-IT.ini b/options/locales/gitea_it-IT.ini
similarity index 100%
rename from options/locale/locale_it-IT.ini
rename to options/locales/gitea_it-IT.ini
diff --git a/options/locale/locale_ja-JP.ini b/options/locales/gitea_ja-JP.ini
similarity index 100%
rename from options/locale/locale_ja-JP.ini
rename to options/locales/gitea_ja-JP.ini
diff --git a/options/locale/locale_ko-KR.ini b/options/locales/gitea_ko-KR.ini
similarity index 100%
rename from options/locale/locale_ko-KR.ini
rename to options/locales/gitea_ko-KR.ini
diff --git a/options/locale/locale_lv-LV.ini b/options/locales/gitea_lv-LV.ini
similarity index 100%
rename from options/locale/locale_lv-LV.ini
rename to options/locales/gitea_lv-LV.ini
diff --git a/options/locale/locale_nl-NL.ini b/options/locales/gitea_nl-NL.ini
similarity index 100%
rename from options/locale/locale_nl-NL.ini
rename to options/locales/gitea_nl-NL.ini
diff --git a/options/locale/locale_pl-PL.ini b/options/locales/gitea_pl-PL.ini
similarity index 100%
rename from options/locale/locale_pl-PL.ini
rename to options/locales/gitea_pl-PL.ini
diff --git a/options/locale/locale_pt-BR.ini b/options/locales/gitea_pt-BR.ini
similarity index 100%
rename from options/locale/locale_pt-BR.ini
rename to options/locales/gitea_pt-BR.ini
diff --git a/options/locale/locale_pt-PT.ini b/options/locales/gitea_pt-PT.ini
similarity index 100%
rename from options/locale/locale_pt-PT.ini
rename to options/locales/gitea_pt-PT.ini
diff --git a/options/locale/locale_ru-RU.ini b/options/locales/gitea_ru-RU.ini
similarity index 100%
rename from options/locale/locale_ru-RU.ini
rename to options/locales/gitea_ru-RU.ini
diff --git a/options/locale/locale_si-LK.ini b/options/locales/gitea_si-LK.ini
similarity index 100%
rename from options/locale/locale_si-LK.ini
rename to options/locales/gitea_si-LK.ini
diff --git a/options/locale/locale_sk-SK.ini b/options/locales/gitea_sk-SK.ini
similarity index 100%
rename from options/locale/locale_sk-SK.ini
rename to options/locales/gitea_sk-SK.ini
diff --git a/options/locale/locale_sv-SE.ini b/options/locales/gitea_sv-SE.ini
similarity index 100%
rename from options/locale/locale_sv-SE.ini
rename to options/locales/gitea_sv-SE.ini
diff --git a/options/locale/locale_tr-TR.ini b/options/locales/gitea_tr-TR.ini
similarity index 100%
rename from options/locale/locale_tr-TR.ini
rename to options/locales/gitea_tr-TR.ini
diff --git a/options/locale/locale_uk-UA.ini b/options/locales/gitea_uk-UA.ini
similarity index 100%
rename from options/locale/locale_uk-UA.ini
rename to options/locales/gitea_uk-UA.ini
diff --git a/options/locale/locale_zh-CN.ini b/options/locales/gitea_zh-CN.ini
similarity index 100%
rename from options/locale/locale_zh-CN.ini
rename to options/locales/gitea_zh-CN.ini
diff --git a/options/locale/locale_zh-HK.ini b/options/locales/gitea_zh-HK.ini
similarity index 100%
rename from options/locale/locale_zh-HK.ini
rename to options/locales/gitea_zh-HK.ini
diff --git a/options/locale/locale_zh-TW.ini b/options/locales/gitea_zh-TW.ini
similarity index 100%
rename from options/locale/locale_zh-TW.ini
rename to options/locales/gitea_zh-TW.ini