feat: added language select

This commit is contained in:
Sergey Yarkov 2022-08-16 13:50:06 +03:00
parent 6a68833cf7
commit c9a45efccd
25 changed files with 139 additions and 60 deletions

View File

@ -0,0 +1,7 @@
---
import UIString from "../UIString";
---
<footer>
<h2><UIString key={"articlePost.comments"} /></h2>
</footer>

View File

@ -0,0 +1 @@
export { default } from "./ArticleComments.astro";

View File

@ -74,6 +74,7 @@
border-radius: var(--border-radius-lg);
font-size: 1rem;
font-weight: 500;
font-family: "IBM Plex Mono", monospace;
padding: 0.2rem 0.3rem;
}

View File

@ -1,17 +1,16 @@
---
import { createDateFormatter, getLanguageFromURL } from "@root/utils";
import { createDateFormatter } from "@root/utils";
import { Image } from "@astrojs/image/components";
import ArticleComments from "@components/ArticleComments";
import "./ArticlePost.scss";
export type Props = {
data: ArticleType;
lang: string;
};
const { data } = Astro.props as Props;
const { data, lang } = Astro.props as Props;
const { title, tags, author, published_at } = data;
// const lang = getLanguageFromURL(Astro.url.pathname);
// console.log(lang);
const DateFormatter = createDateFormatter("ru");
---
@ -46,7 +45,5 @@ const DateFormatter = createDateFormatter("ru");
}
</header>
<slot />
<footer>
<h2>Comments</h2>
</footer>
<ArticleComments />
</article>

View File

@ -6,6 +6,7 @@ import UIString from "@components/UIString";
import NavToggle from "./NavToggle";
import "./Header.scss";
import TranslatedLink from "@components/TranslatedLink";
import LanguageSelect from "@components/LanguageSelect";
const { name, nav, contacts } = SITE_METADATA;
const { pathname } = Astro.url;
@ -45,7 +46,7 @@ const { pathname } = Astro.url;
</a>
</div>
<div class="flex header__nav-settings">
<Icon name="translate" width="21" height="21" />
<LanguageSelect />
<ThemeSwitcher client:visible />
</div>
</nav>

View File

@ -77,6 +77,8 @@
}
body[data-is-mobile-open="true"] {
overflow-y: hidden;
touch-action: none;
& .header {
padding: 15px 0;
background-color: var(--background-header-color);
@ -121,12 +123,22 @@
display: block;
position: absolute;
bottom: 45px;
right: 2.5rem;
right: 2.2rem;
& svg {
width: 36px;
height: 36px;
}
}
& .language-select {
position: absolute;
bottom: 115px;
right: 2.2rem;
& svg,
& select {
height: 36px;
width: 36px;
}
}
}
}
}

View File

@ -0,0 +1,59 @@
---
import { Icon } from "astro-icon";
import { DEFAULT_LANGUAGE } from "@root/config";
import languages from "@i18n/languages";
import { getLanguageFromURL } from "@root/utils";
const lang = getLanguageFromURL(Astro.url.pathname);
---
<div class="flex language-select">
<Icon name="translate" width="21" height="21" />
<select id="languageSelect" value={lang} aria-label="Select language">
{
Object.entries(languages).map(([code]) => (
<option value={code} selected={lang === code}>
<span>{code.toLocaleUpperCase()}</span>
</option>
))
}
</select>
</div>
<style lang="scss">
div {
position: relative;
&:hover {
opacity: var(--opacity-hover);
}
}
svg {
position: absolute;
right: 0;
}
select {
appearance: none;
width: 21px;
opacity: 0;
margin: 0;
border: none;
font-size: var(--font-size-md);
font-family: "IBM Plex Mono";
border-radius: var(--border-radius-md);
cursor: pointer;
}
</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;
const url = newLang === DEFAULT_LANGUAGE ? pathname.slice(3) : `${newLang}${pathname}`;
window.location.pathname = url;
};
$select.addEventListener("change", changeLanguageHandler);
</script>

View File

@ -0,0 +1 @@
export { default } from "./LanguageSelect.astro";

View File

@ -1,5 +1,5 @@
---
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
import { getLanguageFromURL } from "@root/utils";
import { Icon } from "astro-icon";
import UIString from "../UIString";
@ -23,7 +23,7 @@ const { icon, name, year, description, articleUrl, sourceUrl, demoUrl } = data;
<span>{year}</span>
</div>
<div class="project-item__description">
<p>{description[lang] || description[FALLBACK_LANGUAGE]}</p>
<p>{description[lang] || description[DEFAULT_LANGUAGE]}</p>
</div>
<div class="project-item__links">
{

View File

@ -1,5 +1,5 @@
---
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
import { getLanguageFromURL } from "@root/utils";
import { Icon } from "astro-icon";
import "./SkillsBlock.scss";
@ -16,7 +16,7 @@ const {
<div class="skills-block">
<div>
<h3>{title[lang] || title[FALLBACK_LANGUAGE]}</h3>
<h3>{title[lang] || title[DEFAULT_LANGUAGE]}</h3>
</div>
<div>
<ul>

View File

@ -1,5 +1,5 @@
---
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
import { getLanguageFromURL } from "@root/utils";
export interface Props extends astroHTML.JSX.AnchorHTMLAttributes {
@ -8,7 +8,7 @@ export interface Props extends astroHTML.JSX.AnchorHTMLAttributes {
const lang = getLanguageFromURL(Astro.url.pathname);
const { to, ...attrs } = Astro.props as Props;
const href = lang === FALLBACK_LANGUAGE ? to : `/${lang}${to}`;
const href = lang === DEFAULT_LANGUAGE ? to : `/${lang}${to}`;
---
<a href={href} {...attrs}><slot /></a>

View File

@ -1,4 +1,4 @@
export const FALLBACK_LANGUAGE = "ru";
export const DEFAULT_LANGUAGE: LanguageKeys = "ru";
export const SITE_METADATA: SiteMetadataType = {
title: "Sergey Yarkov",

View File

@ -3,6 +3,7 @@ export default {
"site.description":
"Here I publish articles and tutorials on various IT topics, as well as talk about my projects that I have done over the past few years.",
"articles.empty": "No articles found...",
"articlePost.comments": "Comments",
"recentArticles.empty": "There is nothing here yet...",
/* Page titles */
"pageTitle.about": "About me",

View File

@ -3,6 +3,7 @@ export default {
"site.description":
"Здесь я публикую статьи и туториалы на различные IT темы, а также рассказываю о своих проектах которые я сделал за последние несколько лет.",
"articles.empty": "Не найдено ни одной статьи...",
"articlePost.comments": "Комментарии",
"recentArticles.empty": "Пока что здесь ничего нет...",
/* Page titles */
"pageTitle.about": "Обо мне",

View File

@ -1,6 +1,6 @@
import type { AstroGlobal } from "astro";
import * as utils from "@utils/url";
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
import languages from "./languages";
export function getLanguageKeys(): Array<string> {
@ -21,8 +21,8 @@ const translations = transformExports<UIDictionaryKeys>(import.meta.glob("./*/ui
export function useTranslation(Astro: Readonly<AstroGlobal>): (key: UIDictionaryKeys) => string | undefined {
const lang = utils.getLanguageFromURL(Astro.url.pathname);
return function translate(key: UIDictionaryKeys): string | undefined {
const string: string | undefined = translations[lang]?.[key] || translations[FALLBACK_LANGUAGE][key];
if (string === undefined) console.error(`Cannot find any string for translation key "${key}" on "${Astro.url.pathname}" page.`);
const string: string | undefined = translations[lang]?.[key] || translations[DEFAULT_LANGUAGE][key];
if (string === undefined) console.log(`Cannot find any string for translation key "${key}" on "${Astro.url.pathname}" page.`);
return string;
};
}

View File

@ -1,32 +1,32 @@
---
import type { Props as BaseHeadProps } from '@root/components/BaseHead.astro';
import type { Props as BaseHeadProps } from "@root/components/BaseHead.astro";
import BaseHead from '@components/BaseHead.astro';
import BaseLayout from '@components/BaseLayout';
import ArticleContent from '@components/ArticleContent';
import ArticlePost from '@components/ArticlePost';
import '@styles/global.scss';
import BaseHead from "@components/BaseHead.astro";
import BaseLayout from "@components/BaseLayout";
import ArticleContent from "@components/ArticleContent";
import ArticlePost from "@components/ArticlePost";
import "@styles/global.scss";
export interface Props extends BaseHeadProps {
lang?: string | undefined;
content: ArticleType;
}
const { lang = 'ru', content } = Astro.props as Props;
const { lang = "ru", content } = Astro.props as Props;
---
<html lang={lang}>
<html lang={lang} translate="no">
<head>
<BaseHead title={content.title} description={content.description}>
<script is:inline defer src='/assets/js/load-code-highlight.js' />
<script is:inline defer src="/assets/js/load-code-highlight.js"></script>
</BaseHead>
</head>
<BaseLayout>
<div class='container'>
<ArticlePost data={content}>
<div class="container">
<ArticlePost lang={lang} data={content}>
<ArticleContent>
<slot></slot>
<slot />
</ArticleContent>
</ArticlePost>
</div>

View File

@ -1,29 +1,29 @@
---
import type { Props as BaseHeadProps } from '@components/BaseHead.astro';
import type { Props as BaseHeadProps } from "@components/BaseHead.astro";
import { SITE_METADATA } from '@root/config';
import BaseHead from '@components/BaseHead.astro';
import BaseLayout from '@components/BaseLayout';
import PageHeading from '@components/PageHeading';
import '@styles/global.scss';
import { SITE_METADATA } from "@root/config";
import BaseHead from "@components/BaseHead.astro";
import BaseLayout from "@components/BaseLayout";
import PageHeading from "@components/PageHeading";
import "@styles/global.scss";
export interface Props extends BaseHeadProps {
lang?: string | undefined;
showPageHeading?: boolean | undefined;
}
const { title, description, lang = 'ru', showPageHeading = true } = Astro.props as Props;
const { title, description, lang = "ru", showPageHeading = true } = Astro.props as Props;
---
<html lang={lang}>
<html lang={lang} translate="no">
<head>
<BaseHead title={title ? `${title} | ${SITE_METADATA.title}` : SITE_METADATA.title} description={description} />
</head>
<BaseLayout>
<div class='container'>
<div class="container">
{showPageHeading && <PageHeading heading={title} description={description} />}
<slot></slot>
<slot />
</div>
</BaseLayout>
</html>

View File

@ -1,11 +1,11 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
import About from "@templates/About.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== FALLBACK_LANGUAGE)
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---

View File

@ -1,11 +1,11 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
import Articles from "@templates/Articles.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== FALLBACK_LANGUAGE)
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---

View File

@ -1,11 +1,11 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
import Contacts from "@templates/Contacts.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== FALLBACK_LANGUAGE)
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---

View File

@ -1,11 +1,11 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
import Home from "@templates/Home.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== FALLBACK_LANGUAGE)
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---

View File

@ -1,11 +1,11 @@
---
import { getLanguageKeys } from "@i18n/utils";
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
import Projects from "@templates/Projects.astro";
export function getStaticPaths() {
return getLanguageKeys()
.filter((lang) => lang !== FALLBACK_LANGUAGE)
.filter((lang) => lang !== DEFAULT_LANGUAGE)
.map((lang) => ({ params: { lang } }));
}
---

View File

@ -2,8 +2,10 @@
import type { MDXInstance } from "astro";
import ArticleLayout from "@layouts/article.astro";
import { getLanguageFromURL } from "@root/utils";
const lang = getLanguageFromURL(Astro.url.pathname);
// TODO: generate pages based on user language
export async function getStaticPaths() {
const data = await Astro.glob<ArticleType>("../../../content/articles/**/*.mdx");
return data.map((article) => {
@ -16,6 +18,6 @@ export async function getStaticPaths() {
const { Content, frontmatter } = Astro.props as MDXInstance<ArticleType>;
---
<ArticleLayout content={frontmatter}>
<ArticleLayout lang={lang} content={frontmatter}>
<Content />
</ArticleLayout>

View File

@ -13,10 +13,6 @@ body {
min-height: 100vh;
}
body[data-is-mobile-open="true"] {
overflow-y: hidden;
}
*,
*:before,
*:after {

View File

@ -1,4 +1,4 @@
import { FALLBACK_LANGUAGE } from "@root/config";
import { DEFAULT_LANGUAGE } from "@root/config";
export const encode = (data: object) => {
return Object.keys(data)
@ -8,5 +8,5 @@ export const encode = (data: object) => {
export const getLanguageFromURL = (pathname: string) => {
const langCodeMatch = pathname.match(/\/([a-z]{1}-?[a-z]{1})\//);
return langCodeMatch ? langCodeMatch[1] : FALLBACK_LANGUAGE;
return langCodeMatch ? langCodeMatch[1] : DEFAULT_LANGUAGE;
};