diff --git a/scripts/tests/test_translation_fixer/test_complex_doc/data/en_doc.md b/scripts/tests/test_translation_fixer/test_complex_doc/data/en_doc.md
index cbb3cc2614..4e988e7d82 100644
--- a/scripts/tests/test_translation_fixer/test_complex_doc/data/en_doc.md
+++ b/scripts/tests/test_translation_fixer/test_complex_doc/data/en_doc.md
@@ -173,3 +173,13 @@ This is an H
This is an link to the main FastAPI site - tool should add language code to the URL.
This is an link to one of the pages on FastAPI site - tool should add language code to the URL.
+
+# Header (with HTML link to tiangolo.com) { #header-with-html-link-to-tiangolo-com }
+
+#Not a header
+
+```Python
+# Also not a header
+```
+
+Some text
\ No newline at end of file
diff --git a/scripts/tests/test_translation_fixer/test_complex_doc/data/translated_doc.md b/scripts/tests/test_translation_fixer/test_complex_doc/data/translated_doc.md
index 6d6e4d1d59..8ea837f12d 100644
--- a/scripts/tests/test_translation_fixer/test_complex_doc/data/translated_doc.md
+++ b/scripts/tests/test_translation_fixer/test_complex_doc/data/translated_doc.md
@@ -168,3 +168,13 @@ def hello_world():// Печать приветствия
Это ссылка на основной сайт FastAPI — инструмент должен добавить код языка в URL.
Это ссылка на одну из страниц на сайте FastAPI — инструмент должен добавить код языка в URL.
+
+# Заголовок (с HTML ссылкой на tiangolo.com) { #header-5 }
+
+#Не заголовок
+
+```Python
+# Также не заголовок
+```
+
+Немного текста
diff --git a/scripts/tests/test_translation_fixer/test_complex_doc/data/translated_doc_expected.md b/scripts/tests/test_translation_fixer/test_complex_doc/data/translated_doc_expected.md
index 90d14677bf..18e763c124 100644
--- a/scripts/tests/test_translation_fixer/test_complex_doc/data/translated_doc_expected.md
+++ b/scripts/tests/test_translation_fixer/test_complex_doc/data/translated_doc_expected.md
@@ -168,3 +168,13 @@ def hello_world():// Print greeting
Это ссылка на основной сайт FastAPI — инструмент должен добавить код языка в URL.
Это ссылка на одну из страниц на сайте FastAPI — инструмент должен добавить код языка в URL.
+
+# Заголовок (с HTML ссылкой на tiangolo.com) { #header-with-html-link-to-tiangolo-com }
+
+#Не заголовок
+
+```Python
+# Также не заголовок
+```
+
+Немного текста
diff --git a/scripts/tests/test_translation_fixer/test_header_permalinks/data/en_doc.md b/scripts/tests/test_translation_fixer/test_header_permalinks/data/en_doc.md
new file mode 100644
index 0000000000..3814b385f0
--- /dev/null
+++ b/scripts/tests/test_translation_fixer/test_header_permalinks/data/en_doc.md
@@ -0,0 +1,19 @@
+# Header 1 { #header-1 }
+
+Some text
+
+## Header 2 { #header-2 }
+
+Some more text
+
+### Header 3 { #header-3 }
+
+Even more text
+
+# Header 4 { #header-4 }
+
+A bit more text
+
+#Not a header
+
+Final portion of text
\ No newline at end of file
diff --git a/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_level_mismatch_1.md b/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_level_mismatch_1.md
new file mode 100644
index 0000000000..23a7458f24
--- /dev/null
+++ b/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_level_mismatch_1.md
@@ -0,0 +1,19 @@
+# Header 1 { #header-1 }
+
+Some text
+
+# Header 2 { #header-2 }
+
+Some more text
+
+### Header 3 { #header-3 }
+
+Even more text
+
+# Header 4 { #header-4 }
+
+A bit more text
+
+#Not a header
+
+Final portion of text
\ No newline at end of file
diff --git a/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_level_mismatch_2.md b/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_level_mismatch_2.md
new file mode 100644
index 0000000000..ec390ae127
--- /dev/null
+++ b/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_level_mismatch_2.md
@@ -0,0 +1,19 @@
+# Header 1 { #header-1 }
+
+Some text
+
+## Header 2 { #header-2 }
+
+Some more text
+
+### Header 3 { #header-3 }
+
+Even more text
+
+## Header 4 { #header-4 }
+
+A bit more text
+
+#Not a header
+
+Final portion of text
\ No newline at end of file
diff --git a/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_number_gt.md b/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_number_gt.md
new file mode 100644
index 0000000000..43416f8df1
--- /dev/null
+++ b/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_number_gt.md
@@ -0,0 +1,19 @@
+# Header 1 { #header-1 }
+
+Some text
+
+## Header 2 { #header-2 }
+
+Some more text
+
+### Header 3 { #header-3 }
+
+Even more text
+
+# Header 4 { #header-4 }
+
+A bit more text
+
+# Extra header
+
+Final portion of text
\ No newline at end of file
diff --git a/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_number_lt.md b/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_number_lt.md
new file mode 100644
index 0000000000..220c6d5497
--- /dev/null
+++ b/scripts/tests/test_translation_fixer/test_header_permalinks/data/translated_doc_number_lt.md
@@ -0,0 +1,19 @@
+# Header 1 { #header-1 }
+
+Some text
+
+## Header 2 { #header-2 }
+
+Some more text
+
+### Header 3 { #header-3 }
+
+Even more text
+
+Header 4 is missing
+
+A bit more text
+
+#Not a header
+
+Final portion of text
\ No newline at end of file
diff --git a/scripts/tests/test_translation_fixer/test_header_permalinks/test_header_level_mismatch.py b/scripts/tests/test_translation_fixer/test_header_permalinks/test_header_level_mismatch.py
new file mode 100644
index 0000000000..9fe2f7ba70
--- /dev/null
+++ b/scripts/tests/test_translation_fixer/test_header_permalinks/test_header_level_mismatch.py
@@ -0,0 +1,60 @@
+from pathlib import Path
+
+import pytest
+from typer.testing import CliRunner
+
+from scripts.translation_fixer import cli
+
+data_path = Path(
+ "scripts/tests/test_translation_fixer/test_header_permalinks/data"
+).absolute()
+
+
+@pytest.mark.parametrize(
+ "copy_test_files",
+ [(f"{data_path}/en_doc.md", f"{data_path}/translated_doc_level_mismatch_1.md")],
+ indirect=True,
+)
+def test_level_mismatch_1(runner: CliRunner, root_dir: Path, copy_test_files):
+ result = runner.invoke(
+ cli,
+ ["fix-pages", "docs/lang/docs/doc.md"],
+ )
+ assert result.exit_code == 1
+
+ fixed_content = (root_dir / "docs" / "lang" / "docs" / "doc.md").read_text()
+ expected_content = Path(
+ f"{data_path}/translated_doc_level_mismatch_1.md"
+ ).read_text()
+
+ assert fixed_content == expected_content # Translated doc remains unchanged
+ assert "Error processing docs/lang/docs/doc.md" in result.output
+ assert (
+ "Header levels do not match between document and original document"
+ " (found #, expected ##) for header №2 in line 5"
+ ) in result.output
+
+
+@pytest.mark.parametrize(
+ "copy_test_files",
+ [(f"{data_path}/en_doc.md", f"{data_path}/translated_doc_level_mismatch_2.md")],
+ indirect=True,
+)
+def test_level_mismatch_2(runner: CliRunner, root_dir: Path, copy_test_files):
+ result = runner.invoke(
+ cli,
+ ["fix-pages", "docs/lang/docs/doc.md"],
+ )
+ assert result.exit_code == 1
+
+ fixed_content = (root_dir / "docs" / "lang" / "docs" / "doc.md").read_text()
+ expected_content = Path(
+ f"{data_path}/translated_doc_level_mismatch_2.md"
+ ).read_text()
+
+ assert fixed_content == expected_content # Translated doc remains unchanged
+ assert "Error processing docs/lang/docs/doc.md" in result.output
+ assert (
+ "Header levels do not match between document and original document"
+ " (found ##, expected #) for header №4 in line 13"
+ ) in result.output
diff --git a/scripts/tests/test_translation_fixer/test_header_permalinks/test_header_number_mismatch.py b/scripts/tests/test_translation_fixer/test_header_permalinks/test_header_number_mismatch.py
new file mode 100644
index 0000000000..c0e78d0302
--- /dev/null
+++ b/scripts/tests/test_translation_fixer/test_header_permalinks/test_header_number_mismatch.py
@@ -0,0 +1,56 @@
+from pathlib import Path
+
+import pytest
+from typer.testing import CliRunner
+
+from scripts.translation_fixer import cli
+
+data_path = Path(
+ "scripts/tests/test_translation_fixer/test_header_permalinks/data"
+).absolute()
+
+
+@pytest.mark.parametrize(
+ "copy_test_files",
+ [(f"{data_path}/en_doc.md", f"{data_path}/translated_doc_number_gt.md")],
+ indirect=True,
+)
+def test_gt(runner: CliRunner, root_dir: Path, copy_test_files):
+ result = runner.invoke(
+ cli,
+ ["fix-pages", "docs/lang/docs/doc.md"],
+ )
+ assert result.exit_code == 1
+
+ fixed_content = (root_dir / "docs" / "lang" / "docs" / "doc.md").read_text()
+ expected_content = Path(f"{data_path}/translated_doc_number_gt.md").read_text()
+
+ assert fixed_content == expected_content # Translated doc remains unchanged
+ assert "Error processing docs/lang/docs/doc.md" in result.output
+ assert (
+ "Number of headers with permalinks does not match the number "
+ "in the original document (5 vs 4)"
+ ) in result.output
+
+
+@pytest.mark.parametrize(
+ "copy_test_files",
+ [(f"{data_path}/en_doc.md", f"{data_path}/translated_doc_number_lt.md")],
+ indirect=True,
+)
+def test_lt(runner: CliRunner, root_dir: Path, copy_test_files):
+ result = runner.invoke(
+ cli,
+ ["fix-pages", "docs/lang/docs/doc.md"],
+ )
+ assert result.exit_code == 1
+
+ fixed_content = (root_dir / "docs" / "lang" / "docs" / "doc.md").read_text()
+ expected_content = Path(f"{data_path}/translated_doc_number_lt.md").read_text()
+
+ assert fixed_content == expected_content # Translated doc remains unchanged
+ assert "Error processing docs/lang/docs/doc.md" in result.output
+ assert (
+ "Number of headers with permalinks does not match the number "
+ "in the original document (3 vs 4)"
+ ) in result.output