From 18eb59ff2a5e64a916abbc3d12fed83252dbc433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyril=20=C5=A0ebek?= Date: Wed, 29 May 2024 08:05:39 +0200 Subject: [PATCH] Create i18n blog posts Content stored in /src/content/blog/[slug]/.md TODO: fix missing content in language TODO: main blog page --- src/components/FormattedDate.astro | 17 ++++++++ src/content/blog/blog-post-1/de.md | 5 +-- src/content/blog/blog-post-1/en.md | 3 +- src/content/config.js | 16 ++++++- src/i18n/utils.ts | 63 +++++++++++++++++++++++++++ src/layouts/BlogPost.astro | 31 +++++++++++++ src/layouts/MarkDownPostLayout.astro | 7 --- src/pages/[lang]/blog/[...slug].astro | 42 ++++++++++++------ 8 files changed, 155 insertions(+), 29 deletions(-) create mode 100644 src/components/FormattedDate.astro create mode 100644 src/i18n/utils.ts create mode 100644 src/layouts/BlogPost.astro delete mode 100644 src/layouts/MarkDownPostLayout.astro diff --git a/src/components/FormattedDate.astro b/src/components/FormattedDate.astro new file mode 100644 index 0000000..ed99c43 --- /dev/null +++ b/src/components/FormattedDate.astro @@ -0,0 +1,17 @@ +--- +interface Props { + date: Date; +} + +const { date } = Astro.props; +--- + + \ No newline at end of file diff --git a/src/content/blog/blog-post-1/de.md b/src/content/blog/blog-post-1/de.md index 0d4235f..4e7aaca 100644 --- a/src/content/blog/blog-post-1/de.md +++ b/src/content/blog/blog-post-1/de.md @@ -1,15 +1,12 @@ --- -layout: ../../layouts/PostLayout.astro title: "Astro, Vite and MDX test DE" description: "Lorem ipsum dolor sit amet" author: "Iniubong Obonguko" publishDate: 2020-01-01T00:00:00Z image: "https://images.unsplash.com/photo-1664380619395-a25d867b5fb9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&q=80&w=1080" -slug: "blog-post-1" -lang: "de" +language: "de" --- ## Story about Old days -DEDEDE In the olden days, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam adipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus et malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus vestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque sagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet. ## Conclusion diff --git a/src/content/blog/blog-post-1/en.md b/src/content/blog/blog-post-1/en.md index 5bbb18b..6fe3560 100644 --- a/src/content/blog/blog-post-1/en.md +++ b/src/content/blog/blog-post-1/en.md @@ -4,8 +4,7 @@ description: "Lorem ipsum dolor sit amet" author: "Iniubong Obonguko" publishDate: 2020-01-01T00:00:00Z image: "https://images.unsplash.com/photo-1664380619395-a25d867b5fb9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&q=80&w=1080" -lang: "en" -slug: "blog-post-1" +language: "en" --- ## Story about Old days In the olden days, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam adipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus et malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus vestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque sagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet. diff --git a/src/content/config.js b/src/content/config.js index 9891dc6..e5fc102 100644 --- a/src/content/config.js +++ b/src/content/config.js @@ -1,5 +1,5 @@ // 1. Import utilities from `astro:content` -import { z, defineCollection } from 'astro:content'; +import { z, defineCollection, getCollection } from 'astro:content'; // 2. Define your collection(s) const blogCollection = defineCollection({ type: "content", @@ -17,4 +17,16 @@ const blogCollection = defineCollection({ // This key should match your collection directory name in "src/content" export const collections = { 'blog': blogCollection, -}; \ No newline at end of file +}; + +export async function getBlogPosts() { + const posts = await getCollection('blog'); + + return posts.map((post) => { + const blog_slug = post.slug.split('/')[0]; + return { + ...post, + blog_slug + } + }) +} \ No newline at end of file diff --git a/src/i18n/utils.ts b/src/i18n/utils.ts new file mode 100644 index 0000000..ed2484d --- /dev/null +++ b/src/i18n/utils.ts @@ -0,0 +1,63 @@ +import { dictionary } from "./dictionary"; + +export const DEFAULT_LANG = 'en' +export const LANGUAGES = { + 'en': "English", + 'de': "German", + 'fr': "Français", + 'cs': "Česky" +} + +var ui = dictionary + +export type UiType = keyof typeof ui; + +export function getLangFromUrl(url: URL) { + const [, lang] = url.pathname.split("/"); + if (lang in ui) return lang as UiType; + return DEFAULT_LANG; +} + +export function useTranslations(lang?: UiType) { + return function t( + key: keyof (typeof ui)[typeof DEFAULT_LANG], + ...args: any[] + ) { + let translation = ui[lang ?? DEFAULT_LANG][key] || ui[DEFAULT_LANG][key]; + if (args.length > 0) { + for (let i = 0; i < args.length; i++) { + // @ts-ignore + translation = translation.replace(`{${i}}`, args[i]); + } + } + return translation; + }; +} + +export function pathNameIsInLanguage(pathname: string, lang: UiType) { + return pathname.startsWith(`/${lang}`) || (lang === DEFAULT_LANG && !pathNameStartsWithLanguage(pathname)); +} + +function pathNameStartsWithLanguage(pathname: string) { + let startsWithLanguage = false; + const languages = Object.keys(LANGUAGES); + + for (let i = 0; i < languages.length; i++) { + const lang = languages[i]; + if (pathname.startsWith(`/${lang}`)) { + startsWithLanguage = true; + break; + } + } + + return startsWithLanguage; +} + +export function getLocalizedPathname(pathname: string, lang: UiType) { + if (pathNameStartsWithLanguage(pathname)) { + const availableLanguages = Object.keys(LANGUAGES).join('|'); + const regex = new RegExp(`^\/(${availableLanguages})`); + return pathname.replace(regex, `/${lang}`); + } + return `/${lang}${pathname}`; +} \ No newline at end of file diff --git a/src/layouts/BlogPost.astro b/src/layouts/BlogPost.astro new file mode 100644 index 0000000..6900fa5 --- /dev/null +++ b/src/layouts/BlogPost.astro @@ -0,0 +1,31 @@ +--- +import type { CollectionEntry } from "astro:content"; +import FormattedDate from "../components/FormattedDate.astro"; +import SinglePage from "./SinglePage.astro"; +import MainLayout from "./MainLayout.astro"; + +type Props = CollectionEntry<"blog">["data"]; + +const { title, description, publishDate, language } = + Astro.props; +--- + + +
+
+
+
+ +
+

{title}

+
+
+ +
+
+
\ No newline at end of file diff --git a/src/layouts/MarkDownPostLayout.astro b/src/layouts/MarkDownPostLayout.astro deleted file mode 100644 index e950f9c..0000000 --- a/src/layouts/MarkDownPostLayout.astro +++ /dev/null @@ -1,7 +0,0 @@ ---- -const { frontmatter } = Astro.props; ---- - -

{frontmatter.title}

-

{frontmatter.autor}

- \ No newline at end of file diff --git a/src/pages/[lang]/blog/[...slug].astro b/src/pages/[lang]/blog/[...slug].astro index 44d6072..0e855e0 100644 --- a/src/pages/[lang]/blog/[...slug].astro +++ b/src/pages/[lang]/blog/[...slug].astro @@ -1,17 +1,31 @@ --- -import { getCollection } from 'astro:content'; -// 1. Generate a new path for every collection entry -export async function getStaticPaths() { - const blogEntries = await getCollection('blog'); - console.log(blogEntries) - return blogEntries.map(entry => ({ - params: { slug: entry.slug }, props: { entry }, - })); -} -// 2. For your template, you can get the entry directly from the prop -const { entry } = Astro.props; -const { Content } = await entry.render(); +import { getBlogPosts } from "../../../content/config"; +import BlogPost from "../../../layouts/BlogPost.astro"; +export async function getStaticPaths() { + const pages = await getBlogPosts(); + + const paths = pages.map((page) => { + return { + // @ts-ignore + params: { lang: page?.data.language || "en", slug: page.blog_slug }, + props: page, + }; + }); + + return paths; +} + +const { lang, slug } = Astro.params; +const page = Astro.props; +// @ts-ignore +const formattedDate = page.data?.date?.toLocaleString(lang); + +const { Content } = await page.render(); --- -

{entry.data.title}

- \ No newline at end of file + + +

{page.data.title}

+

by {page.data.author} • {formattedDate}

+ +
\ No newline at end of file