Wednesday, June 5, 2024

Essential Git Commands for Beginners

1. Setting Up Git

Before you start using GitHub, you need to install and set up Git on your local machine.

Installation

Download and install Git from the official website.

Configuration

After installation, configure Git with your name and email:

git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

2. Creating a Repository

Initialize a Local Repository

To start tracking a project with Git, navigate to your project directory and initialize a repository:

cd your-project-directory
git init

Clone a Remote Repository

To clone an existing repository from GitHub:

git clone https://github.com/username/repository.git

3. Basic Git Commands

Check Repository Status

To view the status of your working directory and staging area:

git status

Add Files to Staging Area

To add files to the staging area before committing:

git add filename

To add all changes:

git add .

Commit Changes

To commit the staged changes with a message:

git commit -m "Commit message"

4. Working with Branches

Create a New Branch

To create a new branch:

git branch branch-name

Switch to a Branch

To switch to an existing branch:

git checkout branch-name

Or create and switch to a new branch in one command:

git checkout -b new-branch-name

Merge Branches

To merge changes from another branch into the current branch:

git merge branch-name

5. Collaborating with Others

Push Changes to Remote Repository

To push your changes to a remote repository:

git push origin branch-name

Pull Changes from Remote Repository

To fetch and merge changes from a remote repository:

git pull origin branch-name

Fetch Changes

To fetch changes from a remote repository without merging:

git fetch origin

View Remote Repositories

To list all remote repositories:

git remote -v

Add a Remote Repository

To add a new remote repository:

git remote add origin https://github.com/username/repository.git

6. Viewing History

View Commit History

To view the commit history:

git log

View a Specific Commit

To view details of a specific commit:

git show commit-id

7. Undoing Changes

Unstage a File

To unstage a file from the staging area:

git reset HEAD filename

Revert a Commit

To revert a specific commit:

git revert commit-id

Discard Local Changes

To discard changes in your working directory:

git checkout -- filename

8. Working with Tags

Create a Tag

To create a new tag:

git tag tag-name

Push Tags to Remote

To push tags to a remote repository:

git push origin tag-name

Conclusion

Mastering these essential GitHub commands will help you effectively manage your projects and collaborate with others. As you become more comfortable with these commands, you'll be able to explore more advanced features of Git and GitHub, enhancing your productivity and efficiency as a developer. Happy coding!

Friday, March 1, 2024

Why Use /etc/nginx/sites-available and /etc/nginx/sites-enabled?

 

  1. Organization: Keeping each site's configuration in separate files makes the server easier to manage, especially when hosting multiple sites. You can easily enable, disable, or modify configurations for individual sites without affecting the others.

  2. Scalability: As you host more websites, you won't clutter the main nginx.conf file with site-specific configurations. This keeps your main configuration file clean and focused on global settings.

  3. Ease of Enable/Disable: You can enable a site by creating a symbolic link in the sites-enabled directory to its configuration file in sites-available. Similarly, you can disable a site by removing the symbolic link. This setup allows for quick enabling and disabling of sites without actually removing their configuration files.

How Does It Work?

  • /etc/nginx/sites-available: This directory stores the available server block configurations. Each site hosted on the server has its configuration file here, such as domain.com.conf. However, merely having a configuration file in sites-available does not activate the site.

  • /etc/nginx/sites-enabled: This directory contains symbolic links to configuration files in the sites-available directory. Nginx reads all the configurations linked here when starting up. Only sites linked in this directory are served by Nginx.

Creating and Enabling a New Site

To add a new site, you would:

  1. Create a new configuration file in /etc/nginx/sites-available/, such as your_domain.com.conf, and define the server block for your site.

  2. Enable the site by creating a symbolic link in /etc/nginx/sites-enabled to the configuration file you just created in sites-available. You can do this with the ln -s command:

    bash
    sudo ln -s /etc/nginx/sites-available/your_domain.com.conf /etc/nginx/sites-enabled/
  3. Test the Nginx configuration for errors:

    bash
    sudo nginx -t
  4. Reload Nginx to apply the changes:

    bash
    sudo systemctl reload nginx





Monday, February 12, 2024

Building a Blogging Site with React and PHP: A Step-by-Step Guide

Welcome to our comprehensive tutorial on building a React PHP Blogging Site. This step-by-step guide takes you through creating a fully functional blog using the powerful combination of React for the front end and PHP for the back end.

CRUD Operations

Like/Dislike Feature

By the end of this tutorial, I hope you will have a clear understanding of how to integrate a React frontend with a PHP backend, along with a functional blogging site you can continue to expand and customize.

Let’s start this exciting project and bring our blogging site to life!

Essential Tools for Our React PHP Blogging Site Tutorial

React,PHP,MySQL,Axios,Bootstrap

Environment Variables

To handle our API endpoint configurations, we use an .env file in our React PHP Blogging Platform.

REACT_APP_API_BASE_URL=http://localhost/Projects/blogging-stie/server/api

Database Schema

Our blogging site has primarily two tables to store data: blog_posts for the blog entries and post_votes for counting likes and dislikes.

CREATE TABLE `blog_posts`

(

    `id`           INT(11) NOT NULL AUTO_INCREMENT,

    `title`        VARCHAR(255) NOT NULL,

    `author`       VARCHAR(255) NOT NULL,

    `content`      TEXT NOT NULL,

    `publish_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


CREATE TABLE `post_votes`

(

    `id`         INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,

    `post_id`    INT(11) NOT NULL,

    `user_ip`    VARCHAR(50) NOT NULL,

    `vote_type`  ENUM('like', 'dislike') NOT NULL,

    `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    FOREIGN KEY (`post_id`) REFERENCES `blog_posts` (`id`)

        ON DELETE CASCADE

        ON UPDATE CASCADE

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Configuring CORS

In today’s web application, security is crucial. To enable safe cross-origin requests, we implement CORS policies in our config.php file.


Key Components of Our CORS Configuration

Allowed Origins


Allowed Headers


Handling Preflight Requests

// Define configuration options

$allowedOrigins = ['http://localhost:3000'];

$allowedHeaders = ['Content-Type'];


// Set headers for CORS

$origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';

if (in_array($origin, $allowedOrigins)) {

    header('Access-Control-Allow-Origin: ' . $origin);

}


if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {

    header('Access-Control-Allow-Methods: ' . implode(', ', $allowedMethods));

}


if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {

    $requestHeaders = explode(',', $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);

    $requestHeaders = array_map('trim', $requestHeaders); // Trim whitespace from headers

    if (count(array_intersect($requestHeaders, $allowedHeaders)) == count($requestHeaders)) {

        header('Access-Control-Allow-Headers: ' . implode(', ', $allowedHeaders));

    }

}

Database Configuration and Connection

To store and manage the data for our blogging platform, we use a MySQL database.

<?php

// Database configuration

$dbHost     = "";

$dbUsername = "";

$dbPassword = "";

$dbName     = "";


// Create database connection

$conn = new mysqli($dbHost, $dbUsername, $dbPassword, $dbName);


// Check connection

if ($conn->connect_error) {

    die("Connection failed: " . $conn->connect_error);

}

Create Single Post

Core Features of the CreatePost Component

State Management with Hooks


Form Validation


Asynchronous Data Handling


Navigation and Feedback

import React, { useState } from 'react';

import { useNavigate } from 'react-router-dom';

import axios from 'axios';


function CreatePost() {

    const [title, setTitle] = useState('');

    const [content, setContent] = useState('');

    const [author, setAuthor] = useState('');

    const [isLoading, setIsLoading] = useState(false);

    const [error, setError] = useState(''); // State for storing the error message


    const navigate = useNavigate();


    // Example validation function (extend as needed)

    const validateForm = () => {

        if (!title.trim() || !content.trim() || !author.trim()) {

            setError("Please fill in all fields.");

            return false;

        }

        // Additional validation logic here

        return true;

    };


    const handleSubmit = async (event) => {

        event.preventDefault();

        setError(''); // Reset error message on new submission

        if (!validateForm()) return; // Perform validation


        setIsLoading(true);


        try {

            const response = await axios.post(`${process.env.REACT_APP_API_BASE_URL}/create-post.php`, {

                title,

                content,

                author

            });

            console.log(response.data);

            navigate('/');

        } catch (error) {

            console.error(error);

            setError('Failed to create post. Please try again later.');

            setIsLoading(false);

        }

    };


    return (

        <div className="container mt-4">

            <h2>Create a New Post</h2>

            {error && <div className="alert alert-danger" role="alert">{error}</div>} {/* Display error message */}

            <form onSubmit={handleSubmit}>

                <div className="mb-3">

                    <label htmlFor="title" className="form-label">Title</label>

                    <input

                        type="text"

                        className="form-control"

                        id="title"

                        value={title}

                        onChange={(e) => setTitle(e.target.value)}

                        required

                    />

                </div>

                <div className="mb-3">

                    <label htmlFor="content" className="form-label">Content</label>

                    <textarea

                        className="form-control"

                        id="content"

                        rows="5"

                        value={content}

                        onChange={(e) => setContent(e.target.value)}

                        required

                    ></textarea>

                </div>

                <div className="mb-3">

                    <label htmlFor="author" className="form-label">Author</label>

                    <input

                        type="text"

                        className="form-control"

                        id="author"

                        value={author}

                        onChange={(e) => setAuthor(e.target.value)}

                        required

                    />

                </div>

                <button type="submit" className="btn btn-primary" disabled={isLoading}>

                    {isLoading ? <span><span className="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Creating post...</span> : 'Create Post'}

                </button>

            </form>

        </div>

    );

}


export default CreatePost;

header('Access-Control-Allow-Headers: Content-Type'); // Allow Content-Type header


require_once('../config/config.php');

require_once('../config/database.php');


// Retrieve the request body as a string

$request_body = file_get_contents('php://input');


// Decode the JSON data into a PHP array

$data = json_decode($request_body, true);


// Validate input fields with basic validation

if (empty($data['title']) || empty($data['content']) || empty($data['author'])) {

    http_response_code(400);

    echo json_encode(['message' => 'Error: Missing or empty required parameter']);

    exit();

}


// Validate input fields

if (!isset($data['title']) || !isset($data['content']) || !isset($data['author'])) {

    http_response_code(400);

    die(json_encode(['message' => 'Error: Missing required parameter']));

}


// Sanitize input

$title = filter_var($data['title'], FILTER_SANITIZE_STRING);

$author = filter_var($data['author'], FILTER_SANITIZE_STRING);

$content = filter_var($data['content'], FILTER_SANITIZE_STRING);


// Prepare statement

$stmt = $conn->prepare('INSERT INTO blog_posts (title, content, author) VALUES (?, ?, ?)');

$stmt->bind_param('sss', $title, $content, $author);


// Execute statement

if ($stmt->execute()) {

    // Get the ID of the newly created post

    $id = $stmt->insert_id;


    // Return success response

    http_response_code(201);

    echo json_encode(['message' => 'Post created successfully', 'id' => $id]);

} else {

    // Return error response with more detail if possible

    http_response_code(500);

    echo json_encode(['message' => 'Error creating post: ' . $stmt->error]);

}


// Close statement and connection

$stmt->close();

$conn->close();

Display All Posts

import React, { useState, useEffect } from 'react';

import { Link } from 'react-router-dom';

import axios from 'axios';


function PostList() {

    const [posts, setPosts] = useState([]);

    const [isLoading, setIsLoading] = useState(true);

    const [error, setError] = useState('');

    const [currentPage, setCurrentPage] = useState(1);

    const [totalPosts, setTotalPosts] = useState(0);

    const postsPerPage = 10;


    useEffect(() => {

        const fetchPosts = async () => {

            setIsLoading(true);

            try {

                const response = await axios.get(`${process.env.REACT_APP_API_BASE_URL}/posts.php?page=${currentPage}`);

                setPosts(response.data.posts);

                setTotalPosts(response.data.totalPosts);

                setIsLoading(false);

            } catch (error) {

                console.error(error);

                setError('Failed to load posts.');

                setIsLoading(false);

            }

        };


        fetchPosts();

    }, [currentPage]);


    const totalPages = Math.ceil(totalPosts / postsPerPage);

    const goToPreviousPage = () => setCurrentPage(currentPage - 1);

    const goToNextPage = () => setCurrentPage(currentPage + 1);


    return (

        <div className="container mt-5">

            <h2 className="mb-4">All Posts</h2>

            {error && <div className="alert alert-danger">{error}</div>}

            <div className="row">

                {isLoading ? (

                    <p>Loading posts...</p>

                ) : posts.length ? (

                    posts.map(post => (

                        <div className="col-md-6" key={post.id}>

                            <div className="card mb-4">

                                <div className="card-body">

                                    <h5 className="card-title">{post.title}</h5>

                                    <p className="card-text">By {post.author} on {new Date(post.publish_date).toLocaleDateString()}</p>

                                    <Link to={`/post/${post.id}`} className="btn btn-primary">Read More</Link>

                                </div>

                            </div>

                        </div>

                    ))

                ) : (

                    <p>No posts available.</p>

                )}

            </div>

            <nav aria-label="Page navigation">

                <ul className="pagination">

                    <li className={`page-item ${currentPage === 1 ? 'disabled' : ''}`}>

                        <button className="page-link" onClick={goToPreviousPage}>Previous</button>

                    </li>

                    {Array.from({ length: totalPages }, (_, index) => (

                        <li key={index} className={`page-item ${index + 1 === currentPage ? 'active' : ''}`}>

                            <button className="page-link" onClick={() => setCurrentPage(index + 1)}>{index + 1}</button>

                        </li>

                    ))}

                    <li className={`page-item ${currentPage === totalPages ? 'disabled' : ''}`}>

                        <button className="page-link" onClick={goToNextPage}>Next</button>

                    </li>

                </ul>

            </nav>

        </div>

    );

}


export default PostList;

// Load configuration files

require_once('../config/config.php');

require_once('../config/database.php');


header('Content-Type: application/json');


// Define configuration options

$allowedMethods = ['GET'];

$maxPostsPerPage = 10;


// Implement basic pagination

$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;

$offset = ($page - 1) * $maxPostsPerPage;


// Query to count total posts

$countQuery = "SELECT COUNT(*) AS totalPosts FROM blog_posts";

$countResult = mysqli_query($conn, $countQuery);

$countRow = mysqli_fetch_assoc($countResult);

$totalPosts = $countRow['totalPosts'];


// Check if total posts query is successful

if (!$countResult) {

    http_response_code(500); // Internal Server Error

    echo json_encode(['message' => 'Error querying database for total posts count: ' . mysqli_error($conn)]);

    mysqli_close($conn);

    exit();

}


// Query to get all blog posts with pagination and ordering

$query = "SELECT * FROM blog_posts ORDER BY publish_date DESC LIMIT $offset, $maxPostsPerPage";

$result = mysqli_query($conn, $query);


// Check if paginated posts query is successful

if (!$result) {

    http_response_code(500); // Internal Server Error

    echo json_encode(['message' => 'Error querying database for paginated posts: ' . mysqli_error($conn)]);

    mysqli_close($conn);

    exit();

}


// Convert query result into an associative array

$posts = mysqli_fetch_all($result, MYSQLI_ASSOC);


// Check if there are posts

if (empty($posts)) {

    // No posts found, you might want to handle this case differently

    http_response_code(404); // Not Found

    echo json_encode(['message' => 'No posts found', 'totalPosts' => $totalPosts]);

} else {

    // Return JSON response including totalPosts

    echo json_encode(['posts' => $posts, 'totalPosts' => $totalPosts]);

}


// Close database connection

mysqli_close($conn);

Single Post Display & Like/Dislike Feature

import React, { useState } from "react";

import { useParams } from "react-router-dom";

import axios from "axios";


const Post = () => {

    const { id } = useParams();

    const [post, setPost] = useState(null);

    const [likeCount, setLikeCount] = useState(0);

    const [dislikeCount, setDislikeCount] = useState(0);

    const [ipAddress, setIpAddress] = useState("");


    const fetchPost = async () => {

        try {

            const response = await axios.get(`${process.env.REACT_APP_API_BASE_URL}/post.php/${id}`);

            const post = response.data.data;

            setPost(post);

            setLikeCount(post.likes);

            setDislikeCount(post.dislikes);

        } catch (error) {

            console.log(error);

        }

    };


    const fetchIpAddress = async () => {

        try {

            const response = await axios.get("https://api.ipify.org/?format=json");

            setIpAddress(response.data.ip);

        } catch (error) {

            console.log(error);

        }

    };


    const handleLike = async () => {

        try {

            const response = await axios.post(`${process.env.REACT_APP_API_BASE_URL}/post.php/${id}/like/${ipAddress}`);

            const likes = response.data.data;

            setLikeCount(likes);

        } catch (error) {

            console.log(error);

        }

    };


    const handleDislike = async () => {

        try {

            const response = await axios.post(`${process.env.REACT_APP_API_BASE_URL}/post.php/${id}/dislike/${ipAddress}`);

            const dislikes = response.data.data;

            setDislikeCount(dislikes);

        } catch (error) {

            console.log(error);

        }

    };


    React.useEffect(() => {

        fetchPost();

        fetchIpAddress();

    }, []);


    if (!post) {

        return <div>Loading...</div>;

    }


    return (

        <div className="container my-4">

            <h1 className="mb-4">{post.title}</h1>

            <p>{post.content}</p>

            <hr />

            <div className="d-flex justify-content-between">

                <div>

                    <button className="btn btn-outline-primary me-2" onClick={handleLike}>

                        Like <span className="badge bg-primary">{likeCount}</span>

                    </button>

                    <button className="btn btn-outline-danger" onClick={handleDislike}>

                        Dislike <span className="badge bg-danger">{dislikeCount}</span>

                    </button>

                </div>

                <div>

                    <small className="text-muted">

                        Posted by {post.author} on {post.date}

                    </small>

                </div>

            </div>

        </div>

    );

};


export default Post;

// Load configuration files

require_once('../config/config.php');

require_once('../config/database.php');


if ($_SERVER['REQUEST_METHOD'] === 'GET') {

    $requestUri = $_SERVER['REQUEST_URI'];

    $parts = explode('/', $requestUri);

    $id = end($parts);


    $query = "SELECT bp.*, 

                     (SELECT COUNT(*) FROM post_votes WHERE post_id = bp.id AND vote_type = 'like') AS numLikes,

                     (SELECT COUNT(*) FROM post_votes WHERE post_id = bp.id AND vote_type = 'dislike') AS numDislikes

              FROM blog_posts AS bp WHERE bp.id = ?";


    $stmt = $conn->prepare($query);

    $stmt->bind_param('i', $id);

    $stmt->execute();

    $result = $stmt->get_result();


    if ($result->num_rows === 1) {

        $post = $result->fetch_assoc();


        $response = [

            'status' => 'success',

            'data' => [

                'id' => $post['id'],

                'title' => $post['title'],

                'content' => $post['content'],

                'author' => $post['author'],

                'date' => date("l jS \of F Y", strtotime($post['publish_date'])),

                'likes' => $post['numLikes'],

                'dislikes' => $post['numDislikes']

            ]

        ];


        header('Content-Type: application/json');

        echo json_encode($response);

    } else {

        $response = [

            'status' => 'error',

            'message' => 'Post not found'

        ];


        header('Content-Type: application/json');

        echo json_encode($response);

    }


    $stmt->close();

    $conn->close();

}


function checkVote($conn, $postId, $ipAddress, $voteType) {

    $query = "SELECT * FROM post_votes WHERE post_id=? AND user_ip=? AND vote_type=?";

    $stmt = mysqli_prepare($conn, $query);

    mysqli_stmt_bind_param($stmt, "iss", $postId, $ipAddress, $voteType);

    mysqli_stmt_execute($stmt);

    $result = mysqli_stmt_get_result($stmt);

    return mysqli_num_rows($result) > 0;

}


function insertVote($conn, $postId, $ipAddress, $voteType) {

    if (!checkVote($conn, $postId, $ipAddress, $voteType)) {

        $query = "INSERT INTO post_votes (post_id, user_ip, vote_type) VALUES (?, ?, ?)";

        $stmt = mysqli_prepare($conn, $query);

        mysqli_stmt_bind_param($stmt, "iss", $postId, $ipAddress, $voteType);

        mysqli_stmt_execute($stmt);

        return mysqli_stmt_affected_rows($stmt) > 0;

    }

    return false;

}


function removeVote($conn, $postId, $ipAddress, $voteType) {

    if (checkVote($conn, $postId, $ipAddress, $voteType)) {

        $query = "DELETE FROM post_votes WHERE post_id=? AND user_ip=? AND vote_type=?";

        $stmt = mysqli_prepare($conn, $query);

        mysqli_stmt_bind_param($stmt, "iss", $postId, $ipAddress, $voteType);

        mysqli_stmt_execute($stmt);

        return mysqli_stmt_affected_rows($stmt) > 0;

    }

    return false;

}


if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    $segments = explode('/', $_SERVER['REQUEST_URI']);

    $postId = $segments[6];

    $action = $segments[7];

    $ipAddress = $segments[8];

    $voteType = $action === 'like' ? 'like' : 'dislike';


    if (checkVote($conn, $postId, $ipAddress, $voteType)) {

        if (removeVote($conn, $postId, $ipAddress, $voteType)) {

            http_response_code(200);

            echo json_encode(['message' => ucfirst($voteType) . ' removed successfully.']);

        } else {

            http_response_code(500);

            echo json_encode(['message' => 'Failed to remove ' . $voteType . '.']);

        }

    } else {

        if (insertVote($conn, $postId, $ipAddress, $voteType)) {

            http_response_code(201);

            echo json_encode(['message' => ucfirst($voteType) . ' added successfully.']);

        } else {

            http_response_code(500);

            echo json_encode(['message' => 'Failed to add ' . $voteType . '.']);

        }

    }

}

Navbar

import React from 'react';

import { Link } from 'react-router-dom';


const Navbar = () => {

    return (

        <nav className="navbar navbar-expand-lg navbar-light bg-light">

            <div className="container-fluid">

                <Link className="navbar-brand" to="/">Blog Application</Link>

                <button

                    className="navbar-toggler"

                    type="button"

                    data-bs-toggle="collapse"

                    data-bs-target="#navbarNav"

                    aria-controls="navbarNav"

                    aria-expanded="false"

                    aria-label="Toggle navigation"

                >

                    <span className="navbar-toggler-icon"></span>

                </button>

                <div className="collapse navbar-collapse" id="navbarNav">

                    <ul className="navbar-nav">

                        <li className="nav-item">

                            <Link className="nav-link" to="/">Home</Link>

                        </li>

                        <li className="nav-item">

                            <Link className="nav-link" to="/create-post">Create Post</Link>

                        </li>

                    </ul>

                </div>

            </div>

        </nav>

    );

};


export default Navbar;

App.js and Route

import React from 'react';

import { BrowserRouter, Routes, Route } from 'react-router-dom';

import './App.css';

import Navbar from './components/Navbar';

import CreatePost from './components/CreatePost';

import Post from './components/Post';

import PostList from './components/PostList';


function App() {

  return (

      <div className="App">

        <BrowserRouter>

          <Navbar />

          <Routes>

            <Route path={"/"} element={<PostList />} />

            <Route path="/create-post" element={<CreatePost />} />

            <Route path="/post/:id" element={<Post />} />

          </Routes>

        </BrowserRouter>

      </div>

  );

}


export default App;

Congratulations on completing this comprehensive guide to building a blogging site with React and PHP! Now you’ve a good idea to integrate a React frontend with a PHP backend, implementing essential features like CRUD operations and a like/dislike system.


This project not only enhances your development skills, but also serves as a solid foundation for future web applications.


Thank you for choosing this tutorial to advance your web development journey on how to create a blogging site using React and PHP. 

Github Link: https://github.com/mmainulhasan/Projects/tree/master/blogging-stie

https://dev.to/mmainulhasan/building-a-blogging-site-with-react-and-php-a-step-by-step-guide-5bfi

Thursday, November 30, 2023

Tuesday, October 31, 2023

Vue js Lazy loading translations

 //i18n-setup.js

import Vue from 'vue'

import VueI18n from 'vue-i18n'

import messages from '@/lang/en'

import axios from 'axios'


Vue.use(VueI18n)


export const i18n = new VueI18n({

  locale: 'en', // set locale

  fallbackLocale: 'en',

  messages // set locale messages

})


const loadedLanguages = ['en'] // our default language that is preloaded


function setI18nLanguage (lang) {

  i18n.locale = lang

  axios.defaults.headers.common['Accept-Language'] = lang

  document.querySelector('html').setAttribute('lang', lang)

  return lang

}


export function loadLanguageAsync (lang) {

  if (i18n.locale !== lang) {

    if (!loadedLanguages.includes(lang)) {

      return import(/* webpackChunkName: "lang-[request]" */ `@/lang/${lang}`).then(msgs => {

        i18n.setLocaleMessage(lang, msgs.default)

        loadedLanguages.push(lang)

        return setI18nLanguage(lang)

      })

    }

    return Promise.resolve(setI18nLanguage(lang))

  }

  return Promise.resolve(lang)

}

Javascript Export and Import explained

 https://javascript.info/import-export