This commit is contained in:
Sergey Yarkov 2023-02-18 13:01:19 +03:00
parent 998a229d7a
commit cb0ae84515
No known key found for this signature in database
GPG Key ID: E91CE1FABC619B6F
12 changed files with 155 additions and 104 deletions

View File

@ -1,6 +1,6 @@
---
import { IS_SITE_UNDER_CONSTRUCTION } from "@root/constants";
import Header from "@components/Header/Header.astro";
import Header from "@components/Header.astro";
import Footer from "@components/Footer.astro";
import SiteContent from "@components/SiteContent.astro";
import UnderConstruction from "./UnderConstruction.astro";
@ -9,7 +9,9 @@ import UnderConstruction from "./UnderConstruction.astro";
<body>
{
IS_SITE_UNDER_CONSTRUCTION && (
<UnderConstruction>&#128679;&nbsp; This web site is under development &nbsp;&#128679;</UnderConstruction>
<UnderConstruction>
&#128679;&nbsp; This web site is under development &nbsp;&#128679;
</UnderConstruction>
)
}
<Header />

View File

@ -1,8 +1,9 @@
---
import UIString from "@components/UIString.astro";
---
<div>
<p>Не найдено ни одной записи.</p>
<p><UIString key={"recentArticles.empty"} /></p>
</div>
<style lang="scss">

View File

@ -4,55 +4,51 @@ import { IS_SITE_UNDER_CONSTRUCTION, SITE_METADATA } from "@root/constants";
import ThemeSwitcher from "@components/ThemeSwticher";
import LanguageSelect from "@components/LanguageSelect.astro";
import NavToggle from "@components/NavToggle";
import HeaderLink from "./HeaderLink.astro";
import UIString from "@components/UIString.astro";
import TranslatedLink from "@components/TranslatedLink.astro";
---
<header {...(IS_SITE_UNDER_CONSTRUCTION ? { style: 'top: 45px;' } : {})}>
<div class="container">
<div class="logo">
<a href={"/"}>{SITE_METADATA.name}</a>
<TranslatedLink to="/">
{SITE_METADATA.name}
</TranslatedLink>
</div>
<nav itemscope itemtype="http://schema.org/SiteNavigationElement">
<ul>
{
(
SITE_METADATA.nav.map((link) => (
<li>
<HeaderLink itemprop={"url"} href={"/articles"}>
статьи
</HeaderLink>
<TranslatedLink
itemprop={"url"}
to={link.url}
class:list={[{ active: link.url === Astro.url.pathname }]}
>
<UIString key={link.key} />
</TranslatedLink>
</li>
<li>
<HeaderLink itemprop={"url"} href={"/projects"}>
проекты
</HeaderLink>
</li>
<li>
<HeaderLink itemprop={"url"} href={"/about"}>
обо мне
</HeaderLink>
</li>
<li>
<HeaderLink itemprop={"url"} href={"/contacts"}>
контакты
</HeaderLink>
</li>
)
))
}
</ul>
{SITE_METADATA.contacts && <div class="contacts">
<a target="_blank" href={"mailto:" + SITE_METADATA.contacts.email}>
Email
<Icon name="email" width="18" height="18" />
</a>
<a target="_blank" href={SITE_METADATA.contacts.github}>
Github
<Icon name="github_filled" width="18" height="18" />
</a>
<a target="_blank" href={SITE_METADATA.contacts.telegram}>
Telegram
<Icon name="telegram" width="18" height="18" />
</a>
</div>}
{
SITE_METADATA.contacts && (
<div class="contacts">
<a target="_blank" href={"mailto:" + SITE_METADATA.contacts.email}>
Email
<Icon name="email" width="18" height="18" />
</a>
<a target="_blank" href={SITE_METADATA.contacts.github}>
Github
<Icon name="github_filled" width="18" height="18" />
</a>
<a target="_blank" href={SITE_METADATA.contacts.telegram}>
Telegram
<Icon name="telegram" width="18" height="18" />
</a>
</div>
)
}
<div class="flex settings">
<LanguageSelect />
<ThemeSwitcher client:visible />
@ -74,7 +70,7 @@ import HeaderLink from "./HeaderLink.astro";
z-index: 9999;
& .container {
display: flex;
align-items: center;
align-items: flex-start;
justify-content: space-between;
}
& .logo a {
@ -157,7 +153,7 @@ import HeaderLink from "./HeaderLink.astro";
display: block;
& ul {
display: block;
text-align: center;
text-align: left;
margin: 0;
& li {
margin-bottom: 30px;
@ -168,8 +164,14 @@ import HeaderLink from "./HeaderLink.astro";
}
& .contacts {
margin-right: 0;
margin-top: 2rem;
justify-content: center;
margin-top: 2.5rem;
& a {
margin-right: 0.2rem;
}
& svg {
width: 21px;
height: 21px;
}
}
}
}

View File

@ -1,12 +0,0 @@
---
export interface Props extends astroHTML.JSX.AnchorHTMLAttributes {}
const { href, class: className, ...props } = Astro.props;
const { pathname } = Astro.url;
const isActive = href === pathname || href === pathname.replace(/\/$/, "");
---
<a href={href} class:list={[className, { active: isActive }]} {...props}>
<slot />
</a>

View File

@ -1,6 +1,7 @@
---
import { Icon } from "astro-icon";
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "@root/constants";
import { URL_BLOG_PREFIX } from "@root/pages/[...blog].astro";
import { getLanguageFromURL } from "@i18n/utils";
const lang = getLanguageFromURL(Astro.url.pathname);
@ -19,6 +20,35 @@ const lang = getLanguageFromURL(Astro.url.pathname);
</select>
</div>
<script define:vars={{ lang, DEFAULT_LANGUAGE, URL_BLOG_PREFIX }}>
const $select = document.getElementById("languageSelect");
const changeLanguageHandler = (e) => {
const newLang = e.target.value;
const pathname = window.location.pathname;
let url;
if (lang !== DEFAULT_LANGUAGE) {
url = pathname.split("/").slice(2).join("/");
} else {
url = pathname.slice(1);
}
if (newLang !== DEFAULT_LANGUAGE) url = `${newLang}/${url}`;
/* Move user to articles page with new language */
if (window.location.pathname.includes(`/${URL_BLOG_PREFIX}/`)) {
window.location.pathname =
`${newLang === DEFAULT_LANGUAGE ? "" : newLang + "/"}` + "articles/";
return;
}
window.location.pathname = url;
};
$select.addEventListener("change", changeLanguageHandler);
</script>
<style lang="scss">
div {
position: relative;
@ -63,31 +93,3 @@ const lang = getLanguageFromURL(Astro.url.pathname);
}
}
</style>
<script define:vars={{ lang, DEFAULT_LANGUAGE }}>
const $select = document.getElementById("languageSelect");
const changeLanguageHandler = (e) => {
const newLang = e.target.value;
const pathname = window.location.pathname;
let url;
if (lang !== DEFAULT_LANGUAGE) {
url = pathname.split("/").slice(2).join("/");
} else {
url = pathname.slice(1);
}
if (newLang !== DEFAULT_LANGUAGE) url = `${newLang}/${url}`;
/* Move user to articles page with new language */
if (window.location.pathname.includes("/blog/")) {
window.location.pathname = `${newLang === DEFAULT_LANGUAGE ? "" : newLang + "/"}` + "articles/";
return;
}
window.location.pathname = url;
};
$select.addEventListener("change", changeLanguageHandler);
</script>

View File

@ -1,20 +1,25 @@
---
import type { CollectionEntry } from "astro:content";
import siteconfig from "siteconfig.json";
import ArticleItem from "@components/ArticleItem.astro";
import EmptyList from "@components/EmptyList.astro";
import siteconfig from "siteconfig.json";
import UIString from "@components/UIString.astro";
import TranslatedLink from "@components/TranslatedLink.astro";
export interface Props {
articles: CollectionEntry<"blog">[];
length?: number | undefined;
}
const { articles, length = siteconfig.articles.recentLimit } = Astro.props as Props;
const isEmpty = articles.length === 0;
---
<section>
<div>
<h2>недавние публикации</h2>
<a href="/articles/">смотреть все</a>
<h2><UIString key={"recentArticles.heading"} /></h2>
<TranslatedLink to="/articles">
<UIString key={"recentArticles.all"} />
</TranslatedLink>
</div>
<div itemscope itemtype="http://schema.org/Blog">

View File

@ -0,0 +1,16 @@
---
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "@root/constants";
import { getLanguageFromURL } from "@i18n/utils";
export interface Props extends astroHTML.JSX.AnchorHTMLAttributes {
to: string;
}
let pageLang = getLanguageFromURL(Astro.url.pathname);
const { to, ...attrs } = Astro.props as Props;
if (!SUPPORTED_LANGUAGES.includes(pageLang)) pageLang = DEFAULT_LANGUAGE;
const href = pageLang === DEFAULT_LANGUAGE ? to : `/${pageLang}${to}`;
---
<a href={href} {...attrs}><slot /></a>

View File

@ -0,0 +1,9 @@
---
import { useTranslation } from "@i18n/utils";
export interface Props {
key: UIDictionaryKeys;
}
---
{useTranslation(Astro)(Astro.props.key)}

View File

@ -9,4 +9,10 @@ export const SITE_METADATA: SiteMetadataType = {
author: "Sergey Yarkov",
themeColor: "#1e2327",
contacts: siteconfig.contacts,
nav: [
{ key: "navigation.articles", url: "/articles/" },
{ key: "navigation.projects", url: "/projects/" },
{ key: "navigation.about", url: "/about/" },
{ key: "navigation.contacts", url: "/contacts/" },
],
};

View File

@ -1,7 +1,8 @@
import languages from "./languages";
import type { AstroGlobal } from "astro";
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "@root/constants";
import languages from "./languages";
function transformExports<T>(modules: Record<string, { default: T }>) {
function transformExports<T>(modules: Record<string, { default: T }>): Record<string, string> {
const translations: Record<string, T> = {};
for (const [path, module] of Object.entries(modules)) {
const [, lang] = path.split("/");
@ -16,12 +17,29 @@ export function getLanguageKeys(): Array<string> {
);
}
export const getLanguageFromURL = (pathname: string): LanguageKeys => {
export function getLanguageFromURL(pathname: string): LanguageKeys {
const lang = pathname.split("/")[1] as LanguageKeys;
if (Object.hasOwn(languages, lang)) return lang;
return DEFAULT_LANGUAGE;
};
}
export const removeLangFromURL = (pathname: string): string => {
export function removeLangFromURL(pathname: string): string {
return pathname.split("/").slice(2).join("/");
};
}
const translations = transformExports(import.meta.glob("./*/ui.ts", { eager: true }));
const s = translations["ru"];
function translate(key: UIDictionaryKeys, lang: LanguageKeys): string | undefined {
const value = translations[lang]?.[key] || translations[DEFAULT_LANGUAGE][key];
if (value === undefined) console.log(`Cannot find any string for translation key "${key}".`);
return value;
}
export function useTranslation(
Astro: Readonly<AstroGlobal>
): (key: UIDictionaryKeys) => string | undefined {
const pageLang = getLanguageFromURL(Astro.url.pathname);
return (key: UIDictionaryKeys) => translate(key, pageLang);
}

View File

@ -2,10 +2,11 @@
import { getCollection } from "astro:content";
import PageLayout from "@layouts/default.astro";
import Splash from "@components/Splash/Splash.astro";
import SplashHeading from "@root/components/Splash/SplashHeading.astro";
import SplashLinks from "@root/components/Splash/SplashLinks.astro";
import SplashLink from "@root/components/Splash/SplashLink.astro";
import RecentArticles from "@root/components/RecentArticles.astro";
import SplashHeading from "@components/Splash/SplashHeading.astro";
import SplashLinks from "@components/Splash/SplashLinks.astro";
import SplashLink from "@components/Splash/SplashLink.astro";
import RecentArticles from "@components/RecentArticles.astro";
import UIString from "@components/UIString.astro";
const articles = (await getCollection("blog", (e) => !e.data.draft)).sort(
(a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf()
@ -16,17 +17,16 @@ const articles = (await getCollection("blog", (e) => !e.data.draft)).sort(
<Splash>
<SplashHeading>
<hgroup>
<h1>Hello, I'am Sergey</h1>
<h1><UIString key={"splash.headline"} /></h1>
<h2>Fullstack <b>web-developer</b> & <b>freelancer</b></h2>
</hgroup>
<p>
Я занимаюсь разработкой веб-сайтов и веб-приложений разной сложности, а также публикую
статьи и туториалы на разные IT темы.
</p>
<p><UIString key={"splash.description"} /></p>
</SplashHeading>
<SplashLinks>
<SplashLink icon="information" href="/about/">обо мне</SplashLink>
<SplashLink icon="email_filled" href="/contacts/">написать мне</SplashLink>
<SplashLink icon="information" href="/about/"><UIString key={"splash.about"} /></SplashLink>
<SplashLink icon="email_filled" href="/contacts/">
<UIString key={"splash.contact"} />
</SplashLink>
</SplashLinks>
</Splash>
<RecentArticles articles={articles} />

2
src/types/main.d.ts vendored
View File

@ -1,4 +1,5 @@
import languages from "@i18n/languages";
import uiKeys from "@i18n/ru/ui";
export {};
@ -31,5 +32,6 @@ declare global {
telegram?: string;
github?: string;
};
nav: Array<{ key: UIDictionaryKeys; url: string }>;
};
}