Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Bachstelze/b6e83881f0608816ba444b6394eafa58 to your computer and use it in GitHub Desktop.
Save Bachstelze/b6e83881f0608816ba444b6394eafa58 to your computer and use it in GitHub Desktop.
Bootstrap migration from 3 to 4 to 5
import os
import shutil
import re
# insert you local path into the template_path
# the script is migrating the subdirectories and files of the templates into a folder copy
template_path = "/weblate/templates/"
diff_3_to_4 = {
'@media (min-width: $screen-xs-min) and (max-width: $screen-sm-max)': '@media (min-width: map-get($grid-breakpoints, xs)) and (max-width: map-get($grid-breakpoints, xs))',
'@media (min-width: $screen-xs) and (max-width: ($screen-md-min - 1))': '@media (min-width: map-get($grid-breakpoints, xs)) and (max-width: map-get($grid-breakpoints, md)-1)',
'@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max)': '@include media-breakpoint-only(md)',
'@media (min-width: $screen-xs-min)': '@include media-breakpoint-up(xs)',
'@media (min-width: $screen-sm)': '@include media-breakpoint-up(md)',
'@media (min-width: $screen-sm-min)': '@include media-breakpoint-up(md)',
'@media (min-width: $screen-md-min)': '@include media-breakpoint-up(md)',
'@media (min-width: $screen-md)': '@include media-breakpoint-up(md)',
'@media (min-width: $screen-md-max)': '@include media-breakpoint-up(md)',
'@media (min-width: $screen-lg-min)': '@include media-breakpoint-up(lg)',
'@media (max-width: ($screen-xs-min - 1))': '@include media-breakpoint-down(xs)',
'@media (max-width: $screen-xs-max)': '@include media-breakpoint-down(xs)',
'@media (max-width: ($screen-sm-min - 1))': '@include media-breakpoint-down(md)',
'@media (max-width: $screen-sm)': '@include media-breakpoint-down(md)',
'@media (max-width: $screen-sm-min)': '@include media-breakpoint-down(md)',
'@media (max-width: $screen-sm-max)': '@include media-breakpoint-down(lg)',
'@media (max-width: $screen-md-max)': '@include media-breakpoint-down(md)',
'@media (max-width: $screen-lg-max)': '@include media-breakpoint-down(lg)',
'@media (max-width: $screen-xs-min - 1)': '@include media-breakpoint-down(xs)',
'@media (max-width: $screen-md-min)': '@include media-breakpoint-down(md)',
# bootstrap 2
'@media (max-width: $screen-xxs)': '@include media-breakpoint-down(xs)',
'.col-*-offset-*': '.offset-*',
'.col-*-push-*': '.order-*-2',
'.col-*-pull-*': '.order-*-1',
'.panel': '.card',
'.panel-heading': '.card-header',
'.panel-title': '.card-title',
'.panel-body': '.card-body',
'.panel-footer': '.card-footer',
'.panel-primary': '.card.bg-primary.text-white',
'.panel-success': '.card.bg-success.text-white',
'.panel-info': '.card.text-white.bg-info',
'.panel-warning': '.card.bg-warning',
'.panel-danger': '.card.bg-danger.text-white',
'.well': '.card.card-body',
'.thumbnail': '.card.card-body',
'.list-inline > li': '.list-inline-item',
'.dropdown-menu > li': '.dropdown-item',
'.nav navbar > li': '.nav-item',
'.nav navbar > li > a': '.nav-link',
'.navbar-right': '.ml-auto',
'.navbar-btn': '.nav-item',
'.navbar-fixed-top': '.fixed-top',
'.nav-stacked': '.flex-column',
'.btn-default': '.btn-secondary',
'.img-responsive': '.img-fluid',
'.img-circle': '.rounded-circle',
'.img-rounded': '.rounded',
#'.form-horizontal': '', // @note: removed
'.radio': '.form-check',
'.checkbox': '.form-check',
'.input-lg': '.form-control-lg',
'.input-sm': '.form-control-sm',
'.control-label': '.form-control-label',
'.table-condensed': '.table-sm',
'.pagination > li': '.page-item',
'.pagination > li > a': '.page-link',
#'.item': '.carousel-item', // @note: this is too much basic word
'.text-help': '.form-control-feedback',
'.pull-right': '.float-right',
'.pull-left': '.float-left',
'.center-block': '.mx-auto',
'.hidden-xs': '.d-none',
'.hidden-sm': '.d-sm-none',
'.hidden-md': '.d-md-none',
'.visible-xs': '.d-block.d-sm-none',
'.visible-sm': '.d-block.d-md-none',
'.visible-md': '.d-block.d-lg-none',
'.visible-lg': '.d-block.d-xl-none',
'.label': '.badge',
'.badge': '.badge.badge-pill',
# twig
'col-xs-': 'col-',
'col-md-': 'col-lg-',
'col-sm-': 'col-md-'
}
diff_4_to_5 = {
"no-gutters" : "g-0",
"custom-check" : "form-check",
"custom-switch" : "form-switch",
"custom-select" : "form-select",
"custom-file" : "form-control",
"form-file" : "form-control",
"custom-range" : "form-range",
"badge-pill" : "rounded-pill",
"close" : "btn-close",
"whiteList" : "allowList",
"left-" : "start-",
"right-" : "end-",
"float-left" : "float-start",
"float-right" : "float-end",
"border-left" : "border-start",
"border-right" : "border-end",
"rounded-left" : "rounded-start",
"rounded-right" : "rounded-end",
"ml-" : "ms-",
"mr-" : "me-",
"pl-" : "ps-",
"pr-" : "pe-",
"text-left" : "text-start",
"text-right" : "text-end",
"text-monospace" : "font-monospace",
"font-weight-" : "fw-",
"font-style-" : "fst-",
"rounded-sm" : "rounded-0",
"rounded-lg" : "rounded-3",
"sr-only" : "visually-hidden",
# sass functions
"table-row-variant()" : "table-variant()",
"lighten()" : "tint-color()",
"darken()" : "shade-color()",
"scale-color()" : "shift-color()"
}
dropped = [
"pre-scrollable",
"text-justify",
"thead-light",
"thead-dark",
"media ",
"color-level()",
"theme-color-interval",
"bg-gradient-",
"form-control-file",
"form-control-range",
"input-group-append",
"input-group-prepend",
"form-group",
"form-row",
"form-inline",
"badge-",
"btn-block",
"card-deck",
"card-columns",
"card",
"flip",
"jumbotron",
"arrow", # to "popover-arrow" or tooltip-arrow
"text-hide",
"embed-responsive-item"
]
def get_html_css_file(template_path):
for filename in os.listdir(template_path):
f = os.path.join(template_path, filename)
# checking if it is a file
if os.path.isfile(f):
if f.endswith(".html") or f.endswith(".css"):
yield(f)
else:
# we have an other directory like at the beginning
for recursive_filename in get_html_css_file(f):
yield recursive_filename
drops = 0
changes = 0
def migrate_file(file_path):
global drops
global changes
short_path = file_path.split("/new_templates/")[1]
old_file = open(file_path)
old_content = old_file.read()
#new_file = open(file_path, "wt")
#new_file_path = file_path.replace("/old_templates/", "/templates/")
for diff_key in diff_3_to_4.keys():
# we first migrate from version 3 to 4
old_content = old_content.replace(diff_key, diff_3_to_4[diff_key])
if diff_key in old_content:
changes+=1
for diff_key in diff_4_to_5.keys():
old_content = old_content.replace(diff_key, diff_4_to_5[diff_key])
if diff_key in old_content:
changes+=1
for drop in dropped:
for match in re.finditer(drop, old_content):
print("\ndropped '" + drop + "' in " + short_path + " with the following context:")
print(old_content[int(match.start()-25) : int(match.end()+25)])
drops+=1
# handle special case "by" to "x" with a number ratio
for match in re.finditer("by", old_content):
pre_number = old_content[match.start()-1:match.start()]
sur_number = old_content[match.end():match.end()+1]
if pre_number.isnumeric() and sur_number.isnumeric():
old_content = "".join(old_content[:match.start()],"x",old_content[match.end():])
changes += 1
old_file.close()
write_file = open(file_path, "w")
write_file.write(old_content)
write_file.close()
def process_directory(dir_path, dir_name):
old_template_path = dir_path[:]
new_template_path = dir_path.replace("/"+dir_name+"/", "/new_"+dir_name+"/")
if os.path.exists(new_template_path):
shutil.rmtree(new_template_path)
shutil.copytree(old_template_path, new_template_path)
for filename in get_html_css_file(new_template_path):
migrate_file(filename)
#print(filename)
print("created migrated files in")
print(new_template_path)
process_directory(template_path, "templates")
print("changes: " + str(changes))
print("drops: " + str(drops))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment