diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2817110d1a2..4b7bb77ccf3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,9 +21,12 @@ jobs:
- name: Linter checks
run: |
bash _tools/format.sh
- codespell -I _tools/codespell-ignore.txt -x _tools/codespell-ignore-lines.txt {about,community,development,getting_started,tutorials}/**/*.rst
+ codespell -I _tools/codespell-ignore.txt -x _tools/codespell-ignore-lines.txt -S tutorials/i18n/locales.rst {about,community,contributing,getting_started,tutorials}/**/*.rst
+
+ - name: Migrate to Redot
+ run: |
+ python migrate.py . _migrated True
# Use dummy builder to improve performance as we don't need the generated HTML in this workflow.
- name: Sphinx build
- run: |
- sphinx-build --color -b dummy -d _build/doctrees -W . _build/html
+ run: make SPHINXOPTS='--color -W' SPHINXSOURCEDIR='./_migrated' dummy
diff --git a/.gitignore b/.gitignore
index 94c3fee6bc2..a7b8c5ee5e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
.env
_build/
+_build-old/
+_migrated/
env/
__pycache__
*.pyc
diff --git a/img/docs_logo.svg b/img/docs_logo.svg
index fe52d3e695e..f957d787505 100644
--- a/img/docs_logo.svg
+++ b/img/docs_logo.svg
@@ -1 +1,35 @@
-
+
+
+
+
diff --git a/migrate.py b/migrate.py
new file mode 100644
index 00000000000..80121ed1ed1
--- /dev/null
+++ b/migrate.py
@@ -0,0 +1,317 @@
+"""
+## Migrate files from Godot to Redot
+
+Usage (order is important):
+py migrate.py [inputdir] [outputdir] [include unimplemented]
+
+example:
+py migrate.py . _migrated True
+
+Will replace specific godot strings with redot. It tries to ignore external projects and other things that can't
+change.
+
+A distinction is made between unimplemented instances of the godot keyword (for instance references to the main
+website), and implemented ones (like references to github repositories that are already compatible).
+
+The idea is that, as the project is being migrated to the new name, the unimplemented mappings will gradually move
+to the 'normal' mappings. This way the docs can grow along with the project, keeping broken links to a minimum. When
+all is done, migrate.py has served its purpose and can be deleted.
+
+It will recursively traverse all directories, targeting .rst and .md files. It will convert the text and filename
+if necessary, and then save them to the output directory (default _migrated)
+
+From there, the docs can be built in the normal way.
+"""
+
+import fnmatch
+import os
+from shutil import copyfile
+import shutil
+import sys
+import codecs
+from distutils.dir_util import copy_tree
+
+encoding = 'utf-8'
+defaultInputDirectory = '.'
+defaultOutputDirectory = '_migrated'
+defaultIncludeUnimplemented = False
+filename_masks = ['.rst', '.md']
+
+# Mappings that will currently lead to nowhere. Can be treated as a todo list.
+mappings_unimplemented = [
+ # Non existing urls
+ ('https://hosted.weblate.org/projects/godot-engine/godot-docs', 'https://hosted.weblate.org/projects/redot-engine/redot-docs'),
+ ('https://hosted.weblate.org/engage/godot-engine/', 'https://hosted.weblate.org/engage/redot-engine/'),
+ ('https://store.steampowered.com/app/404790/Godot_Engine/', 'https://store.steampowered.com/app/TODO'),
+ ('https://flathub.org/apps/details/org.godotengine.Godot', 'https://flathub.org/apps/details/org.redot-engine.Redot'),
+ ('https://godot.foundation', 'https://redot.foundation'),
+ ('https://hosted.weblate.org/projects/godot-engine/godot/', 'https://hosted.weblate.org/projects/redot-engine/redot/'),
+ ('https://hosted.weblate.org/projects/godot-engine/', 'https://hosted.weblate.org/projects/redot-engine/'),
+ ('https://hosted.weblate.org/browse/godot-engine', 'https://hosted.weblate.org/browse/redot-engine'),
+ ('https://repo1.maven.org/maven2/org/godotengine/godot/', 'https://repo1.maven.org/maven2/org/redot-engine/redot/'),
+ # Non existing internal urls
+ ('https://chat.godotengine.org/', 'https://chat.redotengine.org/'),
+ ('https://editor.godotengine.org', 'https://editor.redotengine.org'),
+ ('https://forum.godotengine.org/', 'https://forum.redotengine.org/'),
+ ('https://fund.godotengine.org', 'https://fund.redotengine.org'),
+ # The following mappings probably require changes to the core engine
+ ('GodotEngine.epub', 'RedotEngine.epub'),
+ ('godotengine.org/license', 'redotengine.org/license'),
+ ('AsGodotDictionary', 'AsRedotDictionary'),
+ ('GODOT_', 'REDOT_'),
+ ('-godot-', '-redot-'),
+ ('project.godot', 'project.redot'),
+ ('Godot.Collections', 'Redot.Collections'),
+ ('"Godot"', '"Redot"'),
+ ('.godot/', '.redot/'),
+ ('.godot.', '.redot.'),
+ ('APPDATA%\\Godot\\', 'APPDATA%\\Redot\\'),
+ ('AppData%\\Godot\\', 'AppData%\\Redot\\'),
+ ('Caches/Godot/', 'Caches/Redot/'),
+ ('cache/godot/', 'cache/redot/'),
+ ('Support/Godot/', 'Support/Redot/'),
+ ('config/godot/', 'config/redot/'),
+ ('share/godot/', 'share/redot/'),
+ (' godot_', ' redot_'),
+ ('org.godotengine.Godot', 'org.redotengine.Redot'),
+ ('godot-ios-plugins', 'redot-ios-plugins'),
+ ('godot-syntax-themes', 'redot-syntax-themes'),
+ ('godot_skin', 'redot_skin'),
+ ('godot_scene_node', 'redot_scene_node'),
+ ('``godotengine/godot', '``redot-engine/redot'),
+ ('>/Godot/', '>/Redot/'),
+ ('``.godot``', '``.redot``'),
+ ('``godot``', '``redot``'),
+ ('/godot.', '/redot.'),
+ ('GodotPhysics', 'RedotPhysics'),
+ ('AsGodotObject', 'AsRedotObject'),
+ ('non-Godot', 'non-Redot'),
+ ('Godot-', 'Redot-'),
+ ('libgodot', 'libredot'),
+ ('godot.linuxbsd', 'redot.linuxbsd'),
+ ('Godot.app', 'Redot.app'),
+ ('MacOS/Godot', 'MacOS/Redot'),
+ ('C:\\godot', 'C:\\redot'),
+ ('GodotSharp', 'RedotSharp'),
+ ('godot.gdkey', 'redot.gdkey'),
+ ('godot-nir', 'redot-nir'),
+ ('godot-angle', 'redot-angle'),
+ ('godot-binary', 'redot-binary'),
+ ('``Godot', '``Redot'),
+ ('godot/modules', 'redot/modules'),
+ ('godot_binary', 'redot_binary'),
+ ('godotengine.org', 'redotengine.org'),
+ ('godot-source', 'redot-source'),
+ ('godot/bin', 'redot/bin'),
+ ('gdb godot', 'gdb redot'),
+ ('GODOT', 'REDOT'),
+ ('USERNAME/godot', 'USERNAME/redot'),
+ ('godot-xr', 'redot-xr'),
+ ('godotisawesome', 'redotisawesome'),
+ ('godot-cpp', 'redot-cpp'),
+ ('GodotCPP', 'RedotCPP'),
+ ('namespace godot', 'namespace redot'),
+ ('godot_cpp', 'redot_cpp'),
+ ('GodotObject', 'RedotObject'),
+ ('GodotBot', 'RedotBot'),
+ ('GodotPlugin', 'RedotPlugin'),
+ ('org.godotengine', 'org.redotengine'),
+ ('/godot>', '/redot>'),
+ ('repos/godotengine/godot', 'repos/redot-engine/redot'),
+ ('godot demo', 'redot demo'),
+ ('godotsharp', 'redotsharp'),
+]
+
+# Mappings that should work on first migration
+mappings = [
+ # Table breakers
+ ('| ``"Please include this when reporting the bug on: https://github.com/godotengine/godot/issues"`` |', '| ``"Please include this when reporting the bug on: https://github.com/redot-engine/godot/issues"``|'),
+ ('https://github.com/godotengine/godot/pull/40364>`_ for more. |', 'https://github.com/redot-engine/redot/pull/40364>`_ for more. |'),
+ # Almost existing urls
+ ('https://docs.godotengine.org', 'https://docs.redotengine.org'),
+ ('https://godotengine.org', 'https://redotengine.org'),
+ # Existing urls
+ ('https://nightly.link/godotengine/godot-docs/workflows/build_offline_docs/master/godot', 'https://nightly.link/redot-engine/redot-docs/workflows/build_offline_docs/master/redot'),
+ ('https://github.com/godotengine/godot-docs/issues', 'https://github.com/redot-engine/redot-docs/issues'),
+ ('https://github.com/godotengine/godot/blob/master', 'https://github.com/redot-engine/redot/blob/master'),
+ ('https://raw.githubusercontent.com/godotengine/godot/master', 'https://raw.githubusercontent.com/redot-engine/redot/master'),
+ ('https://github.com/godotengine/godot-demo-projects', 'https://github.com/redot-engine/redot-demo-projects'),
+ ('https://discord.gg/bdcfAYM4W9', 'https://discord.gg/redot'),
+ ('https://github.com/godotengine/godot', 'https://github.com/redot-engine/redot'),
+ ('https://github.com/godotengine/godot-proposals', 'https://github.com/redot-engine/redot-proposals'),
+ ('https://raw.githubusercontent.com/godotengine/godot-docs', 'https://raw.githubusercontent.com/redot-engine/redot-docs'),
+ ('https://github.com/godotengine/', 'https://github.com/redot-engine/'),
+ # Generic replacements
+ ('GODOT_COPYRIGHT.txt', 'REDOT_COPYRIGHT.txt'),
+ ('godot-docs', 'redot-docs'),
+ ('GODOT ENGINE', 'REDOT ENGINE'),
+ ('/bin/godot', '/bin/redot'),
+ ('/Applications/Godot.app', '/Applications/Redot.app'),
+ ('highlight=Godot', 'highlight=Redot'),
+ ('/godot_', '/redot_'),
+ ('/godot-', '/redot-'),
+ ('_godot_', '_redot_'),
+ ('``godot``', '``redot``'),
+ ('Godot ', 'Redot '),
+ (' Godot', ' Redot'),
+ (' Godot.', ' Redot.'),
+ (' Godot?', ' Redot?'),
+ ('Godot\'', 'Redot\''),
+ ('Godot,', 'Redot,'),
+ ('Godot:', 'Redot:'),
+ (' godot ', ' redot '),
+ ('\nGodot.', '\nRedot.'),
+ ('_godot\n', '_redot\n'),
+ ('godot.gif', 'redot.gif'),
+ ('godot.jpg', 'redot.jpg'),
+ ('godot.png', 'redot.png'),
+ ('"godot_', '"redot_'),
+ ('"godotengine"', '"redotengine"'),
+ ('GodotEngine', 'RedotEngine'),
+ ('godot-giscus', 'redot-giscus'),
+ ('"godotengine/', '"redotengine/'),
+ ('godot_is_latest', 'redot_is_latest'),
+ ('godot-edit-guideline', 'redot-edit-guideline'),
+ ('_godot_', '_redot_'),
+ ('to_godot', 'to_redot'),
+ ('godot.html', 'redot.html'),
+ ('by-godot', 'by-redot'),
+ ('MadeWithGodot', 'MadeWithRedot'),
+]
+
+filename_mappings = [
+ ('godot', 'redot'),
+]
+
+static_dirs = [
+ '**/img',
+ '**/files',
+ '_extensions',
+ '_static',
+ '_styleguides',
+ '_templates',
+ '_tools',
+]
+
+alphanumeric = [
+ 'py',
+ 'md',
+ 'css',
+ 'txt',
+ 'css',
+ 'js',
+ 'html',
+ 'csv',
+ 'rst',
+]
+
+# force stdout encoding so it won't fail on print statements
+if (sys.stdout.encoding != encoding):
+ sys.stdout = codecs.getwriter(encoding)(sys.stdout.buffer, 'strict')
+ sys.stdout.encoding = encoding
+
+def is_target(filename):
+ return any(filename.lower().endswith(m) for m in filename_masks)
+
+def generateOutputName(root, fileName, outputDirectory):
+ on = os.path.join('.', outputDirectory, root, fileName)
+ on = convertContent(on, filename_mappings)
+ ensureDirExists(on)
+ return on
+
+def convertContent(content, mappings):
+ for mapping in mappings:
+ search, replace = mapping
+ if (search != ''):
+ content = content.replace(search, replace)
+ return content
+
+def ensureDirExists(outputName):
+ dirname = os.path.dirname(outputName)
+ try:
+ os.makedirs(dirname)
+ except FileExistsError:
+ pass
+
+def copyFile(root, filename, outputDirectory):
+ inputName = os.path.join(root, filename)
+ outputName = generateOutputName(root, inputName.replace('.\\', '').replace('./', ''), outputDirectory)
+
+ print(f'Copying "{inputName}" to "{outputName}"')
+ shutil.copyfile(inputName, outputName)
+
+def convertFile(root, filename, outputDirectory, includeUnimplemented):
+ inputName = os.path.join(root, filename)
+ outputName = generateOutputName(root, filename, outputDirectory)
+
+ print(f'Converting "{inputName}" to "{outputName}"')
+ with open(inputName, mode = 'r', encoding = encoding) as input:
+ data = input.read()
+
+ if (includeUnimplemented):
+ data = convertContent(data, mappings_unimplemented)
+ data = convertContent(data, mappings)
+ ensureDirExists(outputName)
+ with open(outputName, mode = 'w', encoding = encoding) as output:
+ output.write(data)
+
+def copyGlobalDir(inputDirectory, inputMask, outputDirectory):
+ for root, dirs, files in os.walk(inputDirectory):
+ if (inputMask in root and outputDirectory not in root):
+ for f in files:
+ inputName = os.path.join(root, f)
+ outputName = generateOutputName(root, f, outputDirectory)
+ ensureDirExists(outputName)
+ print(f"Copying {inputName} to {outputName}")
+ copyfile(inputName, outputName)
+
+def convertStaticDir(inputDirectory, outputDirectory):
+ for root, dirs, files in os.walk(inputDirectory):
+ if (outputDirectory not in root and '__' not in root):
+ for f in files:
+ if (f.split('.')[1] in alphanumeric):
+ convertFile(root, f, outputDirectory, True)
+ else:
+ copyFile(root, f, outputDirectory)
+
+def migrate(inputDirectory, outputDirectory, includeUnimplemented):
+ outputsig = os.path.join('.', outputDirectory)
+ for root, dirs, files in os.walk(inputDirectory):
+ # ignore output path
+ if (root.startswith(outputsig)):
+ continue
+
+ items = filter(is_target, files)
+ for item in items:
+ convertFile(root, item, outputDirectory, includeUnimplemented)
+
+inputDir = defaultInputDirectory
+outputDir = defaultOutputDirectory
+includeUnimplemented = defaultIncludeUnimplemented
+if (len(sys.argv) > 1):
+ inputDir = sys.argv[1]
+if (len(sys.argv) > 2):
+ outputDir = sys.argv[2]
+if (len(sys.argv) > 3):
+ includeUnimplemented = sys.argv[3]
+
+print(f"Simple rst migrator. Uses str.replace to map from Godot to Redot.")
+print(f"Usage: py migrate.py [inputDir] [outputDir] [includeUnimplemented], example: py migrate.py . _mymigration True")
+print(f"Author: @Craptain on X")
+print(f"Input directory: {inputDir}, output directory: {outputDir}, include unimplemented: {includeUnimplemented}")
+
+migrate(inputDir, outputDir, includeUnimplemented)
+
+print("Copying config files...")
+convertFile(inputDir, 'conf.py', outputDir, includeUnimplemented)
+convertFile(inputDir, 'robots.txt', outputDir, includeUnimplemented)
+print("Copying static directories...")
+
+for dir in static_dirs:
+ if ('**' in dir):
+ print(f"Copying dirs with mask {dir}")
+ copyGlobalDir(inputDir, dir.split('/')[1], outputDir)
+ else:
+ print(f"Converting dir {dir}")
+ convertStaticDir(dir, outputDir)
+print("Done")