Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/components/BackButton.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
import IconChevronLeft from "@/assets/icons/IconChevronLeft.svg";
import LinkButton from "./LinkButton.astro";
import { SITE } from "@/config";
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);
---

{
Expand All @@ -13,7 +17,7 @@ import { SITE } from "@/config";
class="focus-outline -ms-2 mt-8 mb-2 hover:text-foreground/75"
>
<IconChevronLeft class="inline-block size-6 rtl:rotate-180" />
<span>Go back</span>
<span>{t("nav.back")}</span>
</LinkButton>
</div>
)
Expand Down
6 changes: 5 additions & 1 deletion src/components/BackToTopButton.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
---
import IconChevronLeft from "@/assets/icons/IconChevronLeft.svg";
import IconArrowNarrowUp from "@/assets/icons/IconArrowNarrowUp.svg";
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);
---

<div
Expand All @@ -27,7 +31,7 @@ import IconArrowNarrowUp from "@/assets/icons/IconArrowNarrowUp.svg";
<IconChevronLeft class="inline-block rotate-90 md:hidden" />
<span class="sr-only text-sm group-hover:text-accent md:not-sr-only">
<IconArrowNarrowUp class="inline-block size-4" />
Back To Top
{t("nav.top")}
</span>
</button>
</div>
Expand Down
7 changes: 6 additions & 1 deletion src/components/Breadcrumb.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
---
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);

// Remove current url path and remove trailing slash if exists
const currentUrlPath = Astro.url.pathname.replace(/\/+$/, "");

Expand Down Expand Up @@ -28,7 +33,7 @@ if (breadcrumbList[0] === "tags" && !isNaN(Number(breadcrumbList[2]))) {
class="font-light [&>li]:inline [&>li:not(:last-child)>a]:hover:opacity-100"
>
<li>
<a href="/" class="opacity-80">Home</a>
<a href="/" class="opacity-80">{t("nav.home")}</a>
<span aria-hidden="true" class="opacity-80">&raquo;</span>
</li>
{
Expand Down
6 changes: 5 additions & 1 deletion src/components/Datetime.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
import IconCalendar from "@/assets/icons/IconCalendar.svg";
import { SITE } from "@/config";
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);

dayjs.extend(utc);
dayjs.extend(timezone);
Expand Down Expand Up @@ -44,7 +48,7 @@ const date = datetime.format("D MMM, YYYY"); // e.g., '22 Mar, 2025'
{
isModified && (
<span class:list={["text-sm", { "sm:text-base": size === "lg" }]}>
Updated:
{t("post.updated")}
</span>
)
}
Expand Down
6 changes: 5 additions & 1 deletion src/components/Footer.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
---
import type { HTMLAttributes } from "astro/types";
import Socials from "./Socials.astro";
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);

const currentYear = new Date().getFullYear();

Expand All @@ -26,7 +30,7 @@ const { noMarginTop = false, class: className, ...attrs } = Astro.props;
<div class="my-2 flex flex-col items-center whitespace-nowrap sm:flex-row">
<span>Copyright &#169; {currentYear}</span>
<span class="hidden sm:inline">&nbsp;|&nbsp;</span>
<span>All rights reserved.</span>
<span>{t("footer.rights")}.</span>
</div>
</div>
</footer>
20 changes: 12 additions & 8 deletions src/components/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import IconSunHigh from "@/assets/icons/IconSunHigh.svg";
import IconMenuDeep from "@/assets/icons/IconMenuDeep.svg";
import LinkButton from "./LinkButton.astro";
import { SITE } from "@/config";
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);

const { pathname } = Astro.url;

Expand Down Expand Up @@ -73,17 +77,17 @@ const isActive = (path: string) => {
>
<li class="col-span-2">
<a href="/posts" class:list={{ "active-nav": isActive("/posts") }}>
Posts
{t("posts.title")}
</a>
</li>
<li class="col-span-2">
<a href="/tags" class:list={{ "active-nav": isActive("/tags") }}>
Tags
{t("tags.title")}
</a>
</li>
<li class="col-span-2">
<a href="/about" class:list={{ "active-nav": isActive("/about") }}>
About
{t("about.title")}
</a>
</li>
{
Expand All @@ -97,11 +101,11 @@ const isActive = (path: string) => {
"active-nav [&>svg]:stroke-accent": isActive("/archives"),
},
]}
title="Archives"
title={t("archives.title")}
aria-label="archives"
>
<IconArchive class="hidden sm:inline-block" />
<span class="sm:sr-only">Archives</span>
<span class="sm:sr-only">{t("archives.title")}</span>
</LinkButton>
</li>
)
Expand All @@ -113,11 +117,11 @@ const isActive = (path: string) => {
"focus-outline flex p-3 sm:p-1",
{ "[&>svg]:stroke-accent": isActive("/search") },
]}
title="Search"
title={t("search.title")}
aria-label="search"
>
<IconSearch />
<span class="sr-only">Search</span>
<span class="sr-only">{t("search.title")}</span>
</LinkButton>
</li>
{
Expand All @@ -126,7 +130,7 @@ const isActive = (path: string) => {
<button
id="theme-btn"
class="focus-outline relative size-12 p-4 sm:size-8 hover:[&>svg]:stroke-accent"
title="Toggles light & dark"
title={t("nav.toggle")}
aria-label="auto"
aria-live="polite"
>
Expand Down
7 changes: 5 additions & 2 deletions src/components/Pagination.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import type { CollectionEntry } from "astro:content";
import IconArrowLeft from "@/assets/icons/IconArrowLeft.svg";
import IconArrowRight from "@/assets/icons/IconArrowRight.svg";
import LinkButton from "./LinkButton.astro";
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);
type Props = {
page: Page<CollectionEntry<"blog">>;
};
Expand All @@ -26,7 +29,7 @@ const { page } = Astro.props;
aria-label="Goto Previous Page"
>
<IconArrowLeft class="inline-block rtl:rotate-180" />
Prev
{t("nav.prev")}
</LinkButton>
{page.currentPage} / {page.lastPage}
<LinkButton
Expand All @@ -35,7 +38,7 @@ const { page } = Astro.props;
class:list={["ms-4 select-none", { "opacity-50": !page.url.next }]}
aria-label="Goto Next Page"
>
Next
{t("nav.next")}
<IconArrowRight class="inline-block rtl:rotate-180" />
</LinkButton>
</nav>
Expand Down
6 changes: 5 additions & 1 deletion src/components/ShareLinks.astro
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
---
import { SHARE_LINKS } from "@/constants";
import LinkButton from "./LinkButton.astro";
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);

const URL = Astro.url;
---

{
SHARE_LINKS.length > 0 && (
<div class="flex flex-none flex-col items-center justify-center gap-1 md:items-start">
<span class="italic">Share this post on:</span>
<span class="italic">{t("post.share")}</span>
<div class="text-center">
{SHARE_LINKS.map(social => (
<LinkButton
Expand Down
113 changes: 113 additions & 0 deletions src/i18n/ui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { SITE } from "@/config";

export const languages = {
en: "English",
fr: "Français",
};

export const defaultLang = "en";
// Use 'lang' in config.ts file
export const lang = SITE.lang;

export const ui = {
en: {
"nav.home": "Home",
"nav.back": "Go back",
"nav.top": "Back To Top",
"nav.prev": "Prev",
"nav.next": "Next",
"nav.toggle": "Toggles light & dark",
"nav.prevPost": "Previous Post",
"nav.nextPost": "Next Post",

"months.january": "January",
"months.february": "February",
"months.march": "March",
"months.april": "April",
"months.may": "May",
"months.june": "June",
"months.july": "July",
"months.august": "August",
"months.september": "September",
"months.october": "October",
"months.november": "November",
"months.december": "Decembrer",

"footer.rights": "All rights reserved",

"posts.title": "Posts",
"post.updated": "Updated:",
"post.share": "Share this post on:",
"posts.subtitle": "All the articles I've posted.",

"archives.title": "Archives",
"archives.subtitle": "All the articles I've archived.",

"search.title": "Search",
"search.subtitle": "Search any article ...",

"tags.title": "tags",
"tags.desc": "All the tags used in posts.",

"about.title": "About",

"error.back": "Go back home",
"error.notFound": "Page not found",

"home.social": "Social Links:",
"home.featured": "Featured",
"home.recent": "Recent Posts",
"home.all": "All posts",
"home.rss": "RSS feed",
},
fr: {
"nav.home": "Accueil",
"nav.back": "retour",
"nav.top": "Remonter",
"nav.prev": "Précédent",
"nav.next": "Suivant",
"nav.toggle": "Thème clair / Thème sombre",
"nav.prevPost": "Article Précédent",
"nav.nextPost": "Article Suivant",

"months.january": "Janvier",
"months.february": "Février",
"months.march": "Mars",
"months.april": "Avril",
"months.may": "Mai",
"months.june": "Juin",
"months.july": "Juillet",
"months.august": "Août",
"months.september": "Septembre",
"months.october": "Octobre",
"months.november": "Novembre",
"months.december": "Décembre",

"footer.rights": "tous droits réservés",

"posts.title": "Articles",
"post.updated": "Modifié le:",
"post.share": "Partager sur:",
"posts.subtitle": "Tous les articles postés",

"archives.title": "Archives",
"archives.subtitle": "Tous les articles archivés.",

"search.title": "Rechercher",
"search.subtitle": "Rechercher un article...",

"tags.title": "tags",
"tags.desc": "Tous les tags utilisés.",

"about.title": "À propos",

"error.back": "Retourner à l'accueil",
"error.notFound": "Page non trouvée",

"home.social": "Réseaux sociaux:",
"home.featured": "A la une",
"home.recent": "Articles récents",
"home.all": "Tous les articles",
"home.rss": "Flux RSS",
},
} as const;
13 changes: 13 additions & 0 deletions src/i18n/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ui, defaultLang, lang } from "./ui";

//Return lang constant from ui.ts, if exist in translated ui. Return default lang if not
export function getLang() {
if (lang in ui) return lang as keyof typeof ui;
return defaultLang;
}

export function useTranslations(lang: keyof typeof ui) {
return function t(key: keyof (typeof ui)[typeof defaultLang]) {
return ui[lang][key] || ui[defaultLang][key];
};
}
8 changes: 6 additions & 2 deletions src/layouts/PostDetails.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import { slugifyStr } from "@/utils/slugify";
import IconChevronLeft from "@/assets/icons/IconChevronLeft.svg";
import IconChevronRight from "@/assets/icons/IconChevronRight.svg";
import { SITE } from "@/config";
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);

type Props = {
post: CollectionEntry<"blog">;
Expand Down Expand Up @@ -138,7 +142,7 @@ const nextPost =
>
<IconChevronLeft class="inline-block flex-none rtl:rotate-180" />
<div>
<span>Previous Post</span>
<span>{t("nav.prevPost")}</span>
<div class="text-sm text-accent/85">{prevPost.title}</div>
</div>
</a>
Expand All @@ -151,7 +155,7 @@ const nextPost =
class="flex w-full justify-end gap-1 text-end hover:opacity-75 sm:col-start-2"
>
<div>
<span>Next Post</span>
<span>{t("nav.nextPost")}</span>
<div class="text-sm text-accent/85">{nextPost.title}</div>
</div>
<IconChevronRight class="inline-block flex-none rtl:rotate-180" />
Expand Down
8 changes: 6 additions & 2 deletions src/pages/404.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import Header from "@/components/Header.astro";
import Footer from "@/components/Footer.astro";
import LinkButton from "@/components/LinkButton.astro";
import { SITE } from "@/config";
import { getLang, useTranslations } from "../i18n/utils";

const lang = getLang();
const t = useTranslations(lang);
---

<Layout title={`404 Not Found | ${SITE.title}`}>
Expand All @@ -16,12 +20,12 @@ import { SITE } from "@/config";
<div class="mb-14 flex flex-col items-center justify-center">
<h1 class="text-9xl font-bold text-accent">404</h1>
<span aria-hidden="true">¯\_(ツ)_/¯</span>
<p class="mt-4 text-2xl sm:text-3xl">Page Not Found</p>
<p class="mt-4 text-2xl sm:text-3xl">{t("error.notFound")}</p>
<LinkButton
href="/"
class="my-6 text-lg underline decoration-dashed underline-offset-8"
>
Go back home
{t("error.back")}
</LinkButton>
</div>
</main>
Expand Down
Loading