diff --git a/.github/workflows/translation-sync.yml b/.github/workflows/translation-sync.yml
deleted file mode 100644
index 1f2498f9..00000000
--- a/.github/workflows/translation-sync.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-name: Translation Sync
-
-on:
- schedule:
- - cron: '0 2 * * *'
- workflow_dispatch:
-
-permissions:
- contents: read
-
-jobs:
- sync:
- uses: owncloud/reusable-workflows/.github/workflows/translation-sync.yml@main
- with:
- mode: old
- sub_path: l10n
- secrets:
- TX_TOKEN: ${{ secrets.TX_TOKEN }}
- TRANSLATION_APP_ID: ${{ secrets.TRANSLATION_APP_ID }}
- TRANSLATION_APP_PRIVATE_KEY: ${{ secrets.TRANSLATION_APP_PRIVATE_KEY }}
diff --git a/.gitignore b/.gitignore
index 0546f499..a8917959 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,6 @@ build/artifacts
.rvm
report
build
-/l10n/.transifexrc
.php_cs.cache
.php-cs-fixer.cache
js/public
diff --git a/README.md b/README.md
index fea401b6..c5efd7bf 100644
--- a/README.md
+++ b/README.md
@@ -77,10 +77,11 @@ and our [Code of Conduct](CODE_OF_CONDUCT.md) before getting started.
## Translations
-Help translate this project on Transifex:
-****
-
-Please submit translations via Transifex -- do not open pull requests for translation changes.
+Translations are maintained directly in this repository under `l10n/`, as
+paired `l10n/.js` and `l10n/.json` catalogs. To add or update a
+translation, edit both files for the language and open a pull request. When a
+translatable source string changes, its translations must be updated in the same
+pull request.
## Security
diff --git a/l10n/.tx/config b/l10n/.tx/config
deleted file mode 100644
index d9a5c71f..00000000
--- a/l10n/.tx/config
+++ /dev/null
@@ -1,9 +0,0 @@
-[main]
-host = https://www.transifex.com
-lang_map = ja_JP: ja
-
-[o:owncloud-org:p:owncloud:r:notes]
-file_filter = /notes.po
-source_file = templates/notes.pot
-source_lang = en
-type = PO