diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..1c32db2
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,172 @@
+name: Release Pipeline
+
+on:
+ workflow_dispatch:
+ inputs:
+ mode:
+ description: "Choose whether to create a new tagged release or deploy an existing tag"
+ required: true
+ type: choice
+ default: release
+ options:
+ - release
+ - deploy-existing-tag
+ version:
+ description: "Version without the v prefix (examples: 1.2.0, 1.2.0-rc.1)"
+ required: true
+
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: release-pages
+ cancel-in-progress: false
+
+jobs:
+ validate:
+ runs-on: ubuntu-latest
+ outputs:
+ tag_name: ${{ steps.meta.outputs.tag_name }}
+ source_ref: ${{ steps.meta.outputs.source_ref }}
+ is_prerelease: ${{ steps.meta.outputs.is_prerelease }}
+ should_create_release: ${{ steps.meta.outputs.should_create_release }}
+ steps:
+ - name: Validate version format
+ env:
+ VERSION: ${{ inputs.version }}
+ run: |
+ python - <<'PY'
+ import os, re, sys
+ version = os.environ["VERSION"]
+ pattern = r"\d+\.\d+\.\d+(?:-(?:alpha|beta|rc)\.\d+)?"
+ if not re.fullmatch(pattern, version):
+ print(f"Invalid version: {version}")
+ print("Expected formats: 1.2.0, 1.2.0-alpha.1, 1.2.0-beta.1, 1.2.0-rc.1")
+ sys.exit(1)
+ PY
+
+ - name: Restrict new releases to main
+ if: inputs.mode == 'release'
+ run: |
+ if [[ "${{ github.ref_name }}" != "main" ]]; then
+ echo "New releases must be dispatched from the main branch."
+ exit 1
+ fi
+
+ - name: Checkout repository metadata
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Resolve release metadata
+ id: meta
+ env:
+ MODE: ${{ inputs.mode }}
+ VERSION: ${{ inputs.version }}
+ DISPATCH_REF: ${{ github.ref_name }}
+ run: |
+ TAG_NAME="v${VERSION}"
+
+ if [[ "$MODE" == "release" ]]; then
+ if git ls-remote --exit-code --tags origin "refs/tags/${TAG_NAME}" >/dev/null 2>&1; then
+ echo "Tag ${TAG_NAME} already exists on origin."
+ exit 1
+ fi
+ SOURCE_REF="${GITHUB_SHA}"
+ SHOULD_CREATE_RELEASE=true
+ else
+ if ! git ls-remote --exit-code --tags origin "refs/tags/${TAG_NAME}" >/dev/null 2>&1; then
+ echo "Tag ${TAG_NAME} does not exist on origin."
+ exit 1
+ fi
+ SOURCE_REF="${TAG_NAME}"
+ SHOULD_CREATE_RELEASE=false
+ fi
+
+ if [[ "$VERSION" == *-* ]]; then
+ IS_PRERELEASE=true
+ else
+ IS_PRERELEASE=false
+ fi
+
+ {
+ echo "tag_name=${TAG_NAME}"
+ echo "source_ref=${SOURCE_REF}"
+ echo "is_prerelease=${IS_PRERELEASE}"
+ echo "should_create_release=${SHOULD_CREATE_RELEASE}"
+ } >> "$GITHUB_OUTPUT"
+
+ build:
+ runs-on: ubuntu-latest
+ needs: validate
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ needs.validate.outputs.source_ref }}
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: npm
+
+ - name: Set Eleventy path prefix
+ run: |
+ REPO_NAME="${GITHUB_REPOSITORY#*/}"
+ if [[ "$REPO_NAME" == *.github.io ]]; then
+ echo "ELEVENTY_PATH_PREFIX=/" >> "$GITHUB_ENV"
+ else
+ echo "ELEVENTY_PATH_PREFIX=/${REPO_NAME}/" >> "$GITHUB_ENV"
+ fi
+
+ - run: npm ci
+ - run: npm run build:css
+ - run: npx eleventy --pathprefix="$ELEVENTY_PATH_PREFIX"
+ - run: npm run check
+
+ - uses: actions/upload-pages-artifact@v3
+ with:
+ path: _site
+
+ release:
+ runs-on: ubuntu-latest
+ needs: [validate, build]
+ if: needs.validate.outputs.should_create_release == 'true'
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Create and push tag
+ env:
+ TAG_NAME: ${{ needs.validate.outputs.tag_name }}
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ git fetch --tags origin
+ git tag "$TAG_NAME" "$GITHUB_SHA"
+ git push origin "$TAG_NAME"
+
+ - name: Create GitHub release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ needs.validate.outputs.tag_name }}
+ generate_release_notes: true
+ prerelease: ${{ needs.validate.outputs.is_prerelease == 'true' }}
+
+ deploy:
+ runs-on: ubuntu-latest
+ needs: [validate, build, release]
+ if: |
+ always() &&
+ needs.build.result == 'success' &&
+ (needs.release.result == 'success' || needs.release.result == 'skipped')
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ steps:
+ - id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/_includes/footer.njk b/_includes/footer.njk
index 3965a16..9a5091d 100644
--- a/_includes/footer.njk
+++ b/_includes/footer.njk
@@ -16,7 +16,7 @@
{% set projectLabel = "Project" if not isUzPage else "Loyiha" %}
{% set homeLabel = "Home" if not isUzPage else "Bosh sahifa" %}
{% set tutorialLabel = "Tutorials" if not isUzPage else "Darslar" %}
-{% set toolsLabel = "Online IDE" if not isUzPage else "Onlayn IDE" %}
+{% set toolsLabel = "Python Lab" %}
{% set joinGithubLabel = "Join on GitHub" if not isUzPage else "GitHub'da qo'shiling" %}
{% set contributeLabel = "Contributing Guide" if not isUzPage else "Hissa qo'shish qo'llanmasi" %}
{% set issueLabel = "Report an Issue" if not isUzPage else "Muammo haqida yozing" %}
diff --git a/_includes/head.njk b/_includes/head.njk
index 0c4d7b1..c851dae 100644
--- a/_includes/head.njk
+++ b/_includes/head.njk
@@ -5,7 +5,7 @@
{% set pageDescription = description or defaultSiteDescription %}
{% set metaKeywords = site.keywordsUz if (langCode == 'uz' and site.keywordsUz) else site.keywords %}
{% if isHomepage %}
- {% set pageTitle = ("Python dasturlash darslari | " + site.name) if langCode == 'uz' else ("Python Programming Tutorial | " + site.name) %}
+ {% set pageTitle = ("Pythonni bepul o'rganing: Boshlang'ichdan advancedgacha darslar | " + site.name) if langCode == 'uz' else ("Learn Python Free: Beginner to Advanced Lessons | " + site.name) %}
{% elif isTutorial %}
{% set pageTitle = (tutorialDisplayTitle + " — Boshlang'ichdan advanced darajagacha to'liq dars") if langCode == 'uz' else (tutorialDisplayTitle + " - Complete Tutorial Basic to Advanced") %}
{% elif title %}
@@ -33,7 +33,7 @@
-
+
@@ -43,7 +43,7 @@
-
+
@@ -74,14 +74,14 @@
{"@context":"https://schema.org","@type":"WebSite","@id":"{{ site.url }}/#website","url":"{{ site.url }}","name":"{{ site.name }}","description":"{{ pageDescription }}","inLanguage":"{{ 'uz-UZ' if langCode == 'uz' else 'en-US' }}"}
{% else %}
{% endif %}
diff --git a/_includes/landing.njk b/_includes/landing.njk
index 6707b21..60af34f 100644
--- a/_includes/landing.njk
+++ b/_includes/landing.njk
@@ -1,250 +1,364 @@
-
- {% set currentUrl = page.url or '/' %}
- {% set isUzPage = (lang or '') == 'uz' or currentUrl == '/uz/' or currentUrl.startsWith('/uz/') %}
- {% set contentListTitle = "Python darslari ro'yxati" if isUzPage else "Python Tutorial Content List" %}
- {% set contentListSubtitle = "Pythonni bosqichma-bosqich, boshlang'ichdan advanced darajagacha o'rganing. Har bir mavzu bo'yicha aniq qo'llanmalar bilan." if isUzPage else "Learn Python structurally from basics to advanced topics with step-by-step guides." %}
- {# Keep level names as-is (Basic/Intermediate/Advanced) #}
- {% set basicLabel = "Basic Level" %}
- {% set intermediateLabel = "Intermediate Level" %}
- {% set advancedLabel = "Advanced Level" %}
- {% set basicAlt = "Basic Level" %}
- {% set intermediateAlt = "Intermediate Level" %}
- {% set advancedAlt = "Advanced Level" %}
- {% set basicDesc = "Pythonni noldan boshlang! O'zgaruvchilar, ma'lumot turlari, operatorlar va boshqaruv konstruksiyalarini o'rganasiz. Dasturlashga endi kirayotganlar uchun mos." if isUzPage else "Start your Python adventure from the basics! Learn fundamental concepts like variables, data types, operators, and control structures. Suitable for beginners new to programming." %}
- {% set intermediateDesc = "Python ko'nikmalaringizni rivojlantiring. Modullar, fayl bilan ishlash (file I/O), exception'lar va Obyektga yo'naltirilgan dasturlash (OOP) kabi mavzularni o'zlashtirasiz. Asoslarni bilganlar uchun." if isUzPage else "Develop your Python skills further. Master concepts like modules, file I/O, exceptions, and Object-Oriented Programming (OOP). Aimed at those who understand Python basics and want to expand their capabilities." %}
- {% set advancedDesc = "Pythonning advanced usullariga chuqurroq kiring. Type hint'lar, decorator'lar, iterator/generator'lar va multithreading hamda async/await kabi professional texnikalarni o'rganasiz." if isUzPage else "Dive deep into advanced Python techniques. Learn type hints, decorators, iterators, generators, and professional programming techniques like multithreading and async/await." %}
- {% set defaultTutorialList = collections.tutorialsUz if (isUzPage and collections.tutorialsUz) else collections.tutorials %}
- {% set tutorialList = tutorialItems or defaultTutorialList %}
- {% set dataStructureItems = collections.dataStructuresUz if isUzPage else collections.dataStructures %}
- {% set algorithmItems = collections.algorithmsUz if isUzPage else collections.algorithms %}
- {% set dsaSectionTitle = "Ma'lumotlar tuzilmalari va algoritmlar" if isUzPage else "Data Structures & Algorithms" %}
- {% set dsaSectionSubtitle = "Samarali dasturiy ta'minotning asoslarini o'rganing. Python kodlari, murakkablik tahlili va amaliy patternlar bilan bosqichma-bosqich yo'l." if isUzPage else "Master the building blocks of efficient software. Implementation-first approach with Python code, complexity analysis, and common patterns." %}
- {% set dataStructuresTitle = "Ma'lumotlar tuzilmalari" if isUzPage else "Data Structures" %}
- {% set algorithmsTitle = "Algoritmlar" if isUzPage else "Algorithms" %}
- {% set dataStructuresRoadmapLabel = "Yo'l xaritasini ochish" if isUzPage else "View roadmap" %}
- {% set algorithmsRoadmapLabel = "Yo'l xaritasini ochish" if isUzPage else "View roadmap" %}
- {% set dataStructuresRoadmapUrl = '/uz/data-structures/' if isUzPage else '/en/data-structures/' %}
- {% set algorithmsRoadmapUrl = '/uz/algorithms/' if isUzPage else '/en/algorithms/' %}
- {% set dataStructuresCardCopy = "Necaise kitobi asosidagi yo'nalish: ADT, massivlar, bog'langan tuzilmalar, stack/queue, hash table, daraxtlar, heap va graph mavzulari." if isUzPage else "Book-aligned track (Necaise): ADTs, arrays, linked structures, queues/stacks, hash tables, and trees, plus heap and graph extensions." %}
- {% set algorithmsCardCopy = "Necaise kitobi asosidagi yo'nalish: algoritm tahlili, qidirish/saralash, rekursiya va murakkab saralash usullari hamda qo'shimcha graph va dynamic programming mavzulari." if isUzPage else "Book-aligned track (Necaise): algorithm analysis, searching/sorting, recursion, and advanced sorting, plus graph and dynamic programming extensions." %}
-
-
-
{{ contentListTitle }}
-
- {{ contentListSubtitle }}
-
+{% set currentUrl = page.url or '/' %}
+{% set isUzPage = (lang or '') == 'uz' or currentUrl == '/uz/' or currentUrl.startsWith('/uz/') %}
+{% set defaultTutorialList = collections.tutorialsUz if (isUzPage and collections.tutorialsUz) else collections.tutorials %}
+{% set tutorialList = tutorialItems or defaultTutorialList %}
+{% set dataStructureItems = collections.dataStructuresUz if isUzPage else collections.dataStructures %}
+{% set algorithmItems = collections.algorithmsUz if isUzPage else collections.algorithms %}
+{% set tutorialUrl = '/uz/tutorial/what-is-python/' if isUzPage else '/en/tutorial/what-is-python/' %}
+{% set intermediateStartUrl = '/uz/tutorial/python-modules/' if isUzPage else '/en/tutorial/python-modules/' %}
+{% set advancedStartUrl = '/uz/tutorial/python-type-hints/' if isUzPage else '/en/tutorial/python-type-hints/' %}
+{% set ideUrl = '/online-python-ide/' %}
+{% set dataStructuresRoadmapUrl = '/uz/data-structures/' if isUzPage else '/en/data-structures/' %}
+{% set algorithmsRoadmapUrl = '/uz/algorithms/' if isUzPage else '/en/algorithms/' %}
+{% set totalTutorials = tutorialList.length %}
+{% set heroEyebrow = "Bepul Python o'quv yo'li" if isUzPage else "Free Python learning path" %}
+{% set heroTitle = "Birinchi dasturdan amaliy ko'nikmagacha Pythonni bosqichma-bosqich o'rganing" if isUzPage else "Learn Python step by step from your first script to practical, advanced topics" %}
+{% set heroBody = "3 daraja bo'yicha tuzilgan darslar, brauzer ichidagi IDE va DSA yo'l xaritalari bilan o'rganishni tez boshlang. O'zbek va ingliz tillarida bir xil asosiy yo'nalishlar mavjud." if isUzPage else "Start with a structured curriculum across three levels, practice in a browser IDE, and move into DSA roadmaps when you are ready. The core learning path is available in both English and Uzbek." %}
+{% set heroPrimaryLabel = "Darsni boshlash" if isUzPage else "Start lesson 1" %}
+{% set heroSecondaryLabel = "Yo'nalishlarni ko'rish" if isUzPage else "Explore learning paths" %}
+{% set heroTertiaryLabel = "Python Labni ochish" if isUzPage else "Open Python Lab" %}
+{% set heroStatLessons = "ta dars" if isUzPage else "lessons" %}
+{% set heroStatLanguages = "2 til" if isUzPage else "2 languages" %}
+{% set heroStatTracks = "3 daraja" if isUzPage else "3 levels" %}
+{% set heroStatTools = "DSA + IDE" %}
+{% set heroGuideTitle = "Qayerdan boshlash kerak?" if isUzPage else "Where should you start?" %}
+{% set heroGuideBody = "Agar Python yangi bo'lsa, 1-darsdan boshlang. Agar siz allaqachon o'zgaruvchilar, shartlar va funksiyalarni bilsangiz, o'rta darajaga o'ting. Ishlayotgan kodni tezda sinash kerak bo'lsa, Python Lab'dan foydalaning." if isUzPage else "If Python is new to you, start from lesson 1. If you already know variables, conditions, and functions, jump into intermediate topics. If you want to test code quickly, use the Python Lab." %}
+{% set pathsTitle = "O'zingizga mos yo'nalishni tanlang" if isUzPage else "Choose the path that matches your level" %}
+{% set pathsBody = "Har bir yo'nalish boshqa holat uchun mo'ljallangan: boshlash, bilimni chuqurlashtirish yoki professional uslublarga o'tish." if isUzPage else "Each track is built for a different moment: getting started, building working fluency, or moving into more professional Python patterns." %}
+{% set basicLabel = "Boshlang'ich" if isUzPage else "Beginner" %}
+{% set intermediateLabel = "O'rta" if isUzPage else "Intermediate" %}
+{% set advancedLabel = "Advanced" %}
+{% set basicSummary = "Agar Python siz uchun yangi bo'lsa, shu yerdan boshlang. Sintaksis, o'zgaruvchilar, shartlar, sikllar va funksiyalar bilan mustahkam poydevor yarating." if isUzPage else "If Python is new to you, start here. Build a solid foundation with syntax, variables, conditions, loops, and functions." %}
+{% set intermediateSummary = "Agar asoslarni bilsangiz, shu yo'nalishga o'ting. Modullar, fayllar, exception'lar, OOP va amaliy Python ish jarayonlarini o'rganing." if isUzPage else "If you already know the basics, move into this track. Learn modules, files, exceptions, OOP, and practical Python workflows." %}
+{% set advancedSummary = "Agar siz allaqachon ishlaydigan Python yozsangiz, shu bo'limdan chuqurlashing. Type hints, decorators, async, testing va design pattern'larni o'rganing." if isUzPage else "If you already write working Python, go deeper here. Study type hints, decorators, async, testing, and design patterns." %}
+{% set basicTopics = "O'zgaruvchilar, ma'lumot turlari, operatorlar, shartlar, sikllar, funksiyalar" if isUzPage else "Variables, data types, operators, conditions, loops, functions" %}
+{% set intermediateTopics = "Modullar, file I/O, exception handling, class/object, virtual environment" if isUzPage else "Modules, file I/O, exception handling, classes/objects, virtual environments" %}
+{% set advancedTopics = "Type hints, iterators, context managers, async/await, pytest, FastAPI" if isUzPage else "Type hints, iterators, context managers, async/await, pytest, FastAPI" %}
+{% set basicCta = "Boshlang'ichni ochish" if isUzPage else "Open beginner track" %}
+{% set intermediateCta = "O'rta darajani ochish" if isUzPage else "Open intermediate track" %}
+{% set advancedCta = "Advanced darslarni ochish" if isUzPage else "Open advanced track" %}
+{% set curriculumTitle = "To'liq o'quv dasturi" if isUzPage else "Full curriculum" %}
+{% set curriculumBody = "Agar sizga to'liq indeks kerak bo'lsa, barcha darslar quyida darajalar bo'yicha joylashtirilgan. Havolalar va URL'lar o'zgarmagan." if isUzPage else "If you prefer a complete index, all lessons are grouped below by level. Links and URLs remain unchanged." %}
+{% set curriculumHintTitle = "Tez tavsiya" if isUzPage else "Quick recommendation" %}
+{% set curriculumHintBody = "Ko'pchilik uchun eng to'g'ri boshlanish nuqtasi birinchi darsdir. Agar siz sintaksisni allaqachon bilsangiz, to'g'ridan-to'g'ri modullar va file I/O bo'limiga o'ting." if isUzPage else "For most people, the right starting point is lesson 1. If you already know the syntax basics, jump straight to modules and file I/O." %}
+{% set basicSectionTitle = "Boshlang'ich darslar" if isUzPage else "Beginner lessons" %}
+{% set intermediateSectionTitle = "O'rta darajadagi darslar" if isUzPage else "Intermediate lessons" %}
+{% set advancedSectionTitle = "Advanced darslar" if isUzPage else "Advanced lessons" %}
+{% set basicSectionBody = "Asosiy tushunchalar va kundalik Python yozish uchun kerak bo'ladigan minimum bilim." if isUzPage else "The essential concepts you need to read and write everyday Python." %}
+{% set intermediateSectionBody = "Kodni amaliyroq qilish uchun modullar, fayllar va obyektga yo'naltirilgan yondashuvlar." if isUzPage else "Modules, files, and object-oriented concepts that make your code more practical." %}
+{% set advancedSectionBody = "Yirikroq yoki murakkabroq loyihalar uchun foydali bo'lgan professional texnikalar." if isUzPage else "Professional techniques that help when your codebase or problem space gets more complex." %}
+{% set dsaSectionTitle = "Keyingi qadam: Data Structures & Algorithms" if isUzPage else "Next step: Data Structures & Algorithms" %}
+{% set dsaSectionBody = "Bu bo'lim boshlang'ich Python yo'lidan alohida. Undan algoritmik fikrlashni kuchaytirish, intervyu tayyorlov yoki computer science asoslarini mustahkamlash uchun foydalaning." if isUzPage else "This track sits alongside the main Python curriculum. Use it to strengthen algorithmic thinking, prepare for interviews, or build computer science fundamentals." %}
+{% set dsaLocalizedNote = "DSA bo'limlari bosqichma-bosqich kengaymoqda; yo'l xaritalari boshlash uchun eng yaxshi nuqta." if isUzPage else "Roadmaps are the best entry point if you want a guided sequence." %}
+{% set dataStructuresTitle = "Data Structures" %}
+{% set algorithmsTitle = "Algorithms" %}
+{% set dataStructuresBody = "ADT, array, linked structure, stack/queue, hash table, tree va graph mavzularini bosqichma-bosqich o'rganing." if isUzPage else "Study ADTs, arrays, linked structures, queues/stacks, hash tables, trees, and graph representations in a gradual sequence." %}
+{% set algorithmsBody = "Murakkablik tahlili, qidirish, saralash, graph algoritmlari va dynamic programming mavzulariga o'ting." if isUzPage else "Move from complexity analysis into searching, sorting, graph algorithms, and dynamic programming." %}
+{% set roadmapLabel = "Yo'l xaritasini ochish" if isUzPage else "Open roadmap" %}
+{% set languageTitle = "Ikki tilda o'rganing" if isUzPage else "Learn in two languages" %}
+{% set languageBody = "Asosiy Python kursi ingliz va o'zbek tillarida mavjud. Bir xil yo'l bilan ishlang va kerak bo'lsa tilni almashtiring." if isUzPage else "The core Python curriculum is available in both English and Uzbek. Follow the same structure and switch languages whenever it helps your learning." %}
+{% set languagePrimary = "O'zbek sahifasiga o'tish" if not isUzPage else "English homepagega o'tish" %}
+{% set languageUrl = '/uz/' if not isUzPage else '/en/' %}
+
+
+
{{ "Primary path" if not isUzPage else "Asosiy yo'nalish" }}
+
{{ "Begin with the core Python lessons, then branch into DSA or tools when you need practice and depth." if not isUzPage else "Avval asosiy Python darslarini tugating, so'ng amaliyot va chuqurlik uchun DSA yoki vositalarga o'ting." }}
+
+
+
{{ "Open source" if not isUzPage else "Open source" }}
+
{{ "The curriculum stays easy to review and improve because the project remains linkable, static, and GitHub-friendly." if not isUzPage else "Loyiha statik va GitHub bilan qulay ishlashi sababli o'quv dasturini ko'rib chiqish va yaxshilash oson qoladi." }}
+
+
+
+
+
+
+
+
+
+
{{ dsaSectionTitle }}
+
{{ dsaSectionBody }}
+
{{ dsaLocalizedNote }}
+
+
+
+
+
+
+
+
{{ dataStructuresTitle }}
+
+
{{ dataStructuresBody }}
+
+ {% for item in dataStructureItems %}
+ {% if item.data.order > 0 and loop.index <= 5 %}
+
+
+ {{ item.data.title }}
+
+ {% endif %}
+ {% endfor %}
+