2021-10-12 04:40:03 +08:00
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
"""
A Python script to generate or update alias pages .
2024-05-13 16:21:30 +02:00
Disclaimer : This script generates a lot of false positives so it isn ' t suggested to use the sync option. If used, only stage changes and commit verified changes for your language by using -l LANGUAGE.
2021-10-12 04:40:03 +08:00
2024-05-13 16:21:30 +02:00
Note : If the current directory or one of its parents is called " tldr " , the script will assume it is the tldr root , i . e . , the directory that contains a clone of https : / / github . com / tldr - pages / tldr
If you aren ' t, the script will use TLDR_ROOT as the tldr root. Also, ensure ' git ' is available.
2023-12-21 19:47:18 +05:30
2025-02-16 10:39:55 +09:00
Note : This script uses an interactive prompt instead of positional arguments to :
- Prevent argument parsing errors with command names containing dashes ( e . g . ' pacman -S ' )
- Provide clearer guidance for required inputs
- Allow for input validation before page creation
2023-12-21 19:47:18 +05:30
Usage :
2025-02-16 10:39:55 +09:00
python3 scripts / set - alias - page . py [ - p PAGE ] [ - S ] [ - l LANGUAGE ] [ - s ] [ - n ]
2023-12-21 19:47:18 +05:30
Options :
- p , - - page PAGE
Specify the alias page in the format " platform/alias_command.md " .
2025-02-16 10:39:55 +09:00
This will start an interactive prompt to create / update the page .
2024-05-13 16:21:30 +02:00
- S , - - sync
Synchronize each translation ' s alias page (if exists) with that of the English page.
2024-05-09 13:01:01 +02:00
- l , - - language LANGUAGE
Specify the language , a POSIX Locale Name in the form of " ll " or " ll_CC " ( e . g . " fr " or " pt_BR " ) .
2023-12-21 19:47:18 +05:30
- s , - - stage
Stage modified pages ( requires ' git ' on $ PATH and TLDR_ROOT to be a Git repository ) .
2024-01-02 06:53:39 +01:00
- n , - - dry - run
Show what changes would be made without actually modifying the page .
2023-12-21 19:47:18 +05:30
Examples :
2025-02-16 10:39:55 +09:00
1. Create a new alias page interactively :
python3 scripts / set - alias - page . py - p osx / gsum
python3 scripts / set - alias - page . py - - page osx / gsum
This will start a wizard that guides you through creating the page .
2023-12-21 19:47:18 +05:30
2. Read English alias pages and synchronize them into all translations :
2024-01-02 06:53:39 +01:00
python3 scripts / set - alias - page . py - S
2024-05-13 16:21:30 +02:00
python3 scripts / set - alias - page . py - - sync
2024-01-02 06:53:39 +01:00
2024-05-13 16:21:30 +02:00
3. Read English alias pages and synchronize them for Brazilian Portuguese pages only :
2024-05-09 13:01:01 +02:00
python3 scripts / set - alias - page . py - S - l pt_BR
python3 scripts / set - alias - page . py - - sync - - language pt_BR
2024-05-13 16:21:30 +02:00
4. Read English alias pages , synchronize them into all translations and stage modified pages for commit :
2024-05-18 02:33:40 +02:00
python3 scripts / set - alias - page . py - Ss
python3 scripts / set - alias - page . py - - sync - - stage
2024-05-13 16:21:30 +02:00
5. Read English alias pages and show what changes would be made :
python3 scripts / set - alias - page . py - Sn
python3 scripts / set - alias - page . py - - sync - - dry - run
2021-10-12 04:40:03 +08:00
"""
import re
2024-05-13 16:21:30 +02:00
from pathlib import Path
2025-02-16 10:39:55 +09:00
from dataclasses import dataclass
2024-05-18 02:33:40 +02:00
from _common import (
IGNORE_FILES ,
Colors ,
get_tldr_root ,
get_pages_dir ,
get_target_paths ,
get_locale ,
get_status ,
stage ,
create_colored_line ,
create_argument_parser ,
)
2025-02-16 10:39:55 +09:00
@dataclass
class Config :
""" Global configuration for the script """
root : Path
pages_dirs : list [ Path ]
templates : dict [ str , str ]
dry_run : bool = False
language : str = " "
@dataclass
class AliasPageContent :
""" Content of an alias page """
title : str
original_command : str
documentation_command : str
@dataclass
class AliasPage :
""" Represents an alias page with its path and content """
page_path : str
content : AliasPageContent
2024-05-18 02:33:40 +02:00
IGNORE_FILES + = ( " tldr.md " , " aria2.md " )
def test_ignore_files ( ) :
assert IGNORE_FILES == (
" .DS_Store " ,
" tldr.md " ,
" aria2.md " ,
2024-05-13 16:21:30 +02:00
)
2024-05-18 02:33:40 +02:00
assert " .DS_Store " in IGNORE_FILES
assert " tldr.md " in IGNORE_FILES
2021-10-12 04:40:03 +08:00
2024-05-18 02:33:40 +02:00
def get_templates ( root : Path ) :
2021-10-12 04:40:03 +08:00
"""
Get all alias page translation templates from
TLDR_ROOT / contributing - guides / translation - templates / alias - pages . md .
Parameters :
2025-02-16 10:39:55 +09:00
root ( Path ) : The path of local tldr repository , i . e . , TLDR_ROOT .
2021-10-12 04:40:03 +08:00
Returns :
2025-02-16 10:39:55 +09:00
dict of ( str , str ) : Language labels map to alias page templates .
2021-10-12 04:40:03 +08:00
"""
2024-05-18 02:33:40 +02:00
template_file = root / " contributing-guides/translation-templates/alias-pages.md "
with template_file . open ( encoding = " utf-8 " ) as f :
2021-10-12 04:40:03 +08:00
lines = f . readlines ( )
# Parse alias-pages.md
templates = { }
i = 0
while i < len ( lines ) :
if lines [ i ] . startswith ( " ### " ) :
lang = lines [ i ] [ 4 : ] . strip ( " \n " ) . strip ( " " )
while True :
i = i + 1
if lines [ i ] . startswith ( " Not translated yet. " ) :
is_translated = False
break
elif lines [ i ] . startswith ( " ```markdown " ) :
i = i + 1
is_translated = True
break
if is_translated :
text = " "
while not lines [ i ] . startswith ( " ``` " ) :
text + = lines [ i ]
i = i + 1
templates [ lang ] = text
i = i + 1
return templates
2025-02-16 10:39:55 +09:00
def generate_alias_page_content (
template_content : str ,
page_content : AliasPageContent ,
) - > str :
"""
Generate alias page content by replacing placeholders in the template .
Parameters :
template_content ( str ) : The markdown template for the specific language .
page_content ( AliasPageContent ) : The content of the alias page
Returns :
str : The complete markdown content for the alias page .
"""
template_command = " example "
# Replace placeholders in template with actual values
result = template_content . replace ( template_command , page_content . title , 1 )
result = result . replace ( template_command , page_content . original_command , 1 )
result = result . replace ( template_command , page_content . documentation_command )
return result
2024-05-18 02:33:40 +02:00
def set_alias_page (
2025-02-16 10:39:55 +09:00
path : Path ,
page_content : AliasPageContent ,
2024-05-18 02:33:40 +02:00
) - > str :
2021-10-12 04:40:03 +08:00
"""
Write an alias page to disk .
Parameters :
2025-02-16 10:39:55 +09:00
path ( Path ) : Path to an alias page
page_content ( AliasPageContent ) : The content to write to the page
2021-10-12 04:40:03 +08:00
Returns :
2025-02-16 10:39:55 +09:00
str : Execution status
" " if the alias page standing for the same command already exists or if the locale does not match language_to_update .
" \x1b [36mpage added "
" \x1b [34mpage updated "
" \x1b [36mpage would be added "
" \x1b [34mpage would updated "
2021-10-12 04:40:03 +08:00
"""
2024-05-18 02:33:40 +02:00
locale = get_locale ( path )
2025-02-16 10:39:55 +09:00
if locale not in config . templates or (
config . language != " " and locale != config . language
2024-05-09 13:01:01 +02:00
) :
2021-10-12 04:40:03 +08:00
return " "
2025-02-16 10:39:55 +09:00
# Get existing alias command from the locale page
existing_locale_page_content = get_alias_command_in_page (
path , get_locale_alias_pattern ( locale )
2024-11-04 04:52:16 +01:00
)
2025-02-16 10:39:55 +09:00
if (
existing_locale_page_content . documentation_command
== page_content . documentation_command
) :
2021-10-12 04:40:03 +08:00
return " "
2024-01-02 06:53:39 +01:00
2025-02-16 10:39:55 +09:00
new_locale_page_content = generate_alias_page_content (
config . templates [ locale ] ,
page_content ,
)
# Determine status and write file
2024-05-18 02:33:40 +02:00
status = get_status (
2025-02-16 10:39:55 +09:00
" added " if not path . exists ( ) else " updated " ,
config . dry_run ,
" page " ,
2024-05-18 02:33:40 +02:00
)
2024-01-02 06:53:39 +01:00
2025-02-16 10:39:55 +09:00
if not config . dry_run :
2024-05-18 02:33:40 +02:00
path . parent . mkdir ( parents = True , exist_ok = True )
with path . open ( " w " , encoding = " utf-8 " ) as f :
2025-02-16 10:39:55 +09:00
f . write ( new_locale_page_content )
2021-10-12 04:40:03 +08:00
return status
2025-02-16 10:39:55 +09:00
def get_locale_alias_pattern ( locale : str ) - > str :
""" Get alias pattern from template """
template_line = re . search ( r " >.*`example` " , config . templates [ locale ] ) . group ( 0 )
locale_alias_pattern = template_line [ 2 : template_line . find ( " `example` " ) ] . strip ( )
return locale_alias_pattern
def get_alias_command_in_page ( path : Path , alias_pattern : str ) - > AliasPageContent :
2024-05-18 02:33:40 +02:00
"""
Determine whether the given path is an alias page .
Returns :
2025-02-16 10:39:55 +09:00
AliasPageContent : The page content , or empty strings if not an alias page
2024-05-18 02:33:40 +02:00
"""
if not path . exists ( ) :
2025-02-16 10:39:55 +09:00
return AliasPageContent ( title = " " , original_command = " " , documentation_command = " " )
2024-10-30 10:04:17 -07:00
2024-05-18 02:33:40 +02:00
with path . open ( encoding = " utf-8 " ) as f :
2025-02-16 10:39:55 +09:00
content = f . read ( )
lines = content . splitlines ( )
title = next ( ( line . strip ( " # \n " ) for line in lines if line . startswith ( " # " ) ) , " " )
command_lines = [ line for line in lines if " ` " in line ]
if len ( command_lines ) != 2 or not title :
return AliasPageContent ( title = " " , original_command = " " , documentation_command = " " )
original_command = " "
documentation_command = " "
alias_line = next ( ( line for line in command_lines if alias_pattern in line ) , None )
if alias_line :
description_match = re . search ( r " `([^`]+)` " , alias_line )
if description_match :
original_command = description_match [ 1 ]
tldr_line = next (
( line for line in command_lines if line . strip ( ) . startswith ( " `tldr " ) ) , None
)
if tldr_line :
tldr_match = re . search ( r " `tldr (.+)` " , tldr_line . strip ( ) )
if tldr_match :
documentation_command = tldr_match [ 1 ]
return AliasPageContent (
title = title ,
original_command = original_command ,
documentation_command = documentation_command ,
)
def sync_alias_page_to_locale ( pages_dir : Path , alias_page : AliasPage ) - > list [ Path ] :
2021-10-12 04:40:03 +08:00
"""
2025-02-16 10:39:55 +09:00
Synchronize an alias page into a specific locale directory .
2021-10-12 04:40:03 +08:00
Parameters :
2025-02-16 10:39:55 +09:00
pages_dir ( Path ) : Directory containing pages for a specific locale
alias_page ( AliasPage ) : The alias page to sync
2021-10-12 04:40:03 +08:00
Returns :
2025-02-16 10:39:55 +09:00
list [ Path ] : List of paths that were modified
2021-10-12 04:40:03 +08:00
"""
2025-02-16 10:39:55 +09:00
2024-05-18 02:33:40 +02:00
paths = [ ]
2025-02-16 10:39:55 +09:00
path = config . root / pages_dir / alias_page . page_path
status = set_alias_page ( path , alias_page . content )
if status != " " :
rel_path = " / " . join ( path . parts [ - 3 : ] )
paths . append ( rel_path )
print ( create_colored_line ( Colors . GREEN , f " { rel_path } { status } " ) )
2024-05-18 02:33:40 +02:00
return paths
2021-10-12 04:40:03 +08:00
2025-02-16 10:39:55 +09:00
def get_english_alias_pages ( en_path : Path ) - > list [ AliasPage ] :
"""
Get all English alias pages with their commands .
Parameters :
en_path ( Path ) : Path to English pages directory
Returns :
list [ AliasPage ] : List of alias pages with their content
"""
alias_pages = [ ]
alias_pattern = get_locale_alias_pattern ( " en " )
# Get all platform directories (common, linux, etc.)
platforms = [
page . name for page in en_path . iterdir ( ) if page . name not in IGNORE_FILES
]
# Iterate through each platform
for platform in platforms :
platform_path = en_path / platform
page_paths = [
f " { platform } / { page . name } "
for page in platform_path . iterdir ( )
if page . name not in IGNORE_FILES
]
# Check each command if it's an alias
for page_path in page_paths :
page_content = get_alias_command_in_page ( en_path / page_path , alias_pattern )
if page_content . original_command :
alias_pages . append ( AliasPage ( page_path = page_path , content = page_content ) )
return alias_pages
def prompt_alias_page_info ( page_path : str ) - > AliasPageContent :
"""
Prompt user for alias page content .
Returns :
AliasPageContent : The collected page content
"""
en_path = config . root / " pages "
if not page_path . lower ( ) . endswith ( " .md " ) :
page_path = f " { page_path } .md "
exists = ( en_path / page_path ) . exists ( )
print ( f " \n { ' Updating ' if exists else ' Creating new ' } alias page... " )
print ( create_colored_line ( Colors . CYAN , f " Page path: { page_path } " ) )
print (
create_colored_line (
Colors . BLUE ,
" \n The title will be used in the first line of the page after ' # ' " ,
)
)
print ( create_colored_line ( Colors . GREEN , " Example: npm run-script " ) )
title = input ( create_colored_line ( Colors . CYAN , " Enter page title: " ) ) . strip ( )
if not title :
raise SystemExit ( create_colored_line ( Colors . RED , " Title cannot be empty " ) )
print (
create_colored_line (
Colors . BLUE ,
" \n The original command will appear in ' This command is an alias of `command` ' " ,
)
)
print ( create_colored_line ( Colors . GREEN , " Example: npm run " ) )
original_command = input (
create_colored_line ( Colors . CYAN , " Enter original command: " )
) . strip ( )
if not original_command :
raise SystemExit (
create_colored_line ( Colors . RED , " Original command cannot be empty " )
)
print (
create_colored_line (
Colors . BLUE ,
" \n The documentation command will be used in ' tldr command ' line " ,
)
)
print ( create_colored_line ( Colors . GREEN , " Example: npm run " ) )
documentation_command = input (
create_colored_line (
Colors . CYAN ,
f " Enter documentation command (press Enter to use { original_command } ): " ,
)
) . strip ( )
if not documentation_command :
documentation_command = original_command
print ( " \n Summary: " )
print ( f " * Title: { create_colored_line ( Colors . CYAN , title ) } " )
print ( f " * Original command: { create_colored_line ( Colors . CYAN , original_command ) } " )
print (
f " * Documentation command: { create_colored_line ( Colors . CYAN , documentation_command ) } "
)
print ( create_colored_line ( Colors . BLUE , " \n This will create a page like: " ) )
print ( create_colored_line ( Colors . GREEN , f " # { title } " ) )
print (
create_colored_line (
Colors . GREEN , f " \n > This command is an alias of ` { original_command } `. "
)
)
print (
create_colored_line (
Colors . GREEN , " \n - View documentation for the original command: "
)
)
print ( create_colored_line ( Colors . GREEN , f " \n `tldr { documentation_command } ` " ) )
response = (
input ( create_colored_line ( Colors . CYAN , " \n Proceed? [Y/n] " ) ) . lower ( ) . strip ( )
)
if response and response not in [ " y " , " yes " ] :
raise SystemExit ( create_colored_line ( Colors . RED , " Cancelled by user " ) )
return AliasPageContent (
title = title ,
original_command = original_command ,
documentation_command = documentation_command ,
)
2021-10-12 04:40:03 +08:00
def main ( ) :
2024-05-18 02:33:40 +02:00
parser = create_argument_parser (
" Sets the alias page for all translations of a page "
2024-01-02 06:53:39 +01:00
)
2021-10-12 04:40:03 +08:00
args = parser . parse_args ( )
root = get_tldr_root ( )
2024-05-18 02:33:40 +02:00
pages_dirs = get_pages_dir ( root )
2025-02-16 10:39:55 +09:00
templates = get_templates ( root )
global config
config = Config (
root = root ,
pages_dirs = pages_dirs ,
templates = templates ,
dry_run = args . dry_run ,
language = args . language ,
)
2024-05-18 02:33:40 +02:00
target_paths = [ ]
2021-10-12 04:40:03 +08:00
# Use '--page' option
if args . page != " " :
2025-02-16 10:39:55 +09:00
page_info = prompt_alias_page_info ( args . page )
target_paths + = get_target_paths (
args . page , config . pages_dirs , check_exists = False
)
2021-10-12 04:40:03 +08:00
for path in target_paths :
2024-05-18 02:33:40 +02:00
rel_path = " / " . join ( path . parts [ - 3 : ] )
2025-02-16 10:39:55 +09:00
status = set_alias_page ( path , page_info )
2021-10-12 04:40:03 +08:00
if status != " " :
2024-05-18 02:33:40 +02:00
print ( create_colored_line ( Colors . GREEN , f " { rel_path } { status } " ) )
2021-10-12 04:40:03 +08:00
# Use '--sync' option
elif args . sync :
2025-02-16 10:39:55 +09:00
en_path = config . root / " pages "
pages_dirs = config . pages_dirs . copy ( )
pages_dirs . remove ( en_path )
alias_pages = get_english_alias_pages ( en_path )
for alias_page in alias_pages :
for pages_dir in pages_dirs :
target_paths . extend ( sync_alias_page_to_locale ( pages_dir , alias_page ) )
2021-10-12 04:40:03 +08:00
2024-01-02 06:53:39 +01:00
# Use '--stage' option
2025-02-16 10:39:55 +09:00
if args . stage and not config . dry_run and len ( target_paths ) > 0 :
2024-05-18 02:33:40 +02:00
stage ( target_paths )
2021-10-12 04:40:03 +08:00
if __name__ == " __main__ " :
main ( )