mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 18:39:14 +03:00
translate pages
This commit is contained in:
parent
cb0ae84515
commit
c311050b65
@ -1,5 +1,9 @@
|
|||||||
---
|
---
|
||||||
import Button from "@components/Button.astro";
|
import Button from "@components/Button.astro";
|
||||||
|
import UIString from "./UIString.astro";
|
||||||
|
import { useTranslation } from "@i18n/utils";
|
||||||
|
|
||||||
|
const t = useTranslation(Astro);
|
||||||
---
|
---
|
||||||
|
|
||||||
<form name="contact" method="post" netlify-honeypot="bot-field" data-netlify="true">
|
<form name="contact" method="post" netlify-honeypot="bot-field" data-netlify="true">
|
||||||
@ -9,17 +13,44 @@ import Button from "@components/Button.astro";
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div>
|
<div>
|
||||||
<input name="name" placeholder={"Имя"} type="text" maxlength="20" autocomplete="off" required />
|
<input
|
||||||
|
name="name"
|
||||||
|
placeholder={t("input.nameField")}
|
||||||
|
type="text"
|
||||||
|
maxlength="20"
|
||||||
|
autocomplete="off"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<input name="email" placeholder="example@email.com" autocomplete="off" type="email" required />
|
<input
|
||||||
|
name="email"
|
||||||
|
placeholder="example@email.com"
|
||||||
|
autocomplete="off"
|
||||||
|
type="email"
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input name="subject" placeholder={"Тема"} type="text" autocomplete="off" required />
|
<input
|
||||||
<textarea name="message" placeholder={"Сообщение"} id="message" autocomplete="off" cols={30} rows={10} required
|
name="subject"
|
||||||
></textarea>
|
placeholder={t("input.subjectField")}
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<textarea
|
||||||
|
name="message"
|
||||||
|
placeholder={t("input.messageField")}
|
||||||
|
id="message"
|
||||||
|
autocomplete="off"
|
||||||
|
cols={30}
|
||||||
|
rows={10}
|
||||||
|
required></textarea>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<Button type="submit">{"Отправить"}</Button>
|
<Button type="submit">
|
||||||
|
<UIString key={"btn.submit"} />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|||||||
@ -1,59 +1,47 @@
|
|||||||
---
|
---
|
||||||
|
import { SITE_METADATA } from "@root/constants";
|
||||||
import { Icon } from "astro-icon";
|
import { Icon } from "astro-icon";
|
||||||
|
import TranslatedLink from "./TranslatedLink.astro";
|
||||||
|
import UIString from "./UIString.astro";
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
---
|
---
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<ul>
|
<div>
|
||||||
<li>
|
<i>
|
||||||
<a href="https://astro.build/" target="_blank">Astro</a>
|
Разработка: <TranslatedLink to="/contacts">{SITE_METADATA.author}</TranslatedLink>
|
||||||
<Icon name="astro" width="20" height="20" />
|
© {date.getFullYear()}
|
||||||
</li>
|
</i>
|
||||||
<li>
|
</div>
|
||||||
<a href="https://www.netlify.com/" target="_blank">Netlify</a>
|
<div>
|
||||||
<Icon name="netlify" width="20" height="20" />
|
<a href="/rss.xml" target="_blank"><UIString key={"rss.feed"} /></a>
|
||||||
</li>
|
<span>
|
||||||
<li>
|
|
||||||
<a href="/rss.xml" target="_blank">RSS</a>
|
|
||||||
<Icon name="rss" width="20" height="20" />
|
<Icon name="rss" width="20" height="20" />
|
||||||
</li>
|
</span>
|
||||||
</ul>
|
</div>
|
||||||
<small>© Sergey Yarkov, {date.getFullYear()}</small>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
footer {
|
footer {
|
||||||
user-select: none;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-top: 5rem;
|
margin-top: 5rem;
|
||||||
|
color: var(--color-gray-200);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
& .container {
|
& .container {
|
||||||
padding: 25px 0 10px 0;
|
padding: 15px 0 15px 0;
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
row-gap: 15px;
|
justify-content: space-between;
|
||||||
}
|
& div:last-child {
|
||||||
& ul {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 25px;
|
|
||||||
& li {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
column-gap: 7px;
|
|
||||||
& a {
|
& a {
|
||||||
color: var(--font-color);
|
margin-right: 0.25rem;
|
||||||
font-size: var(--font-size-sm);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
& small {
|
|
||||||
text-align: center;
|
|
||||||
font-size: var(--font-size-xs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import TranslatedLink from "@components/TranslatedLink.astro";
|
|||||||
<TranslatedLink
|
<TranslatedLink
|
||||||
itemprop={"url"}
|
itemprop={"url"}
|
||||||
to={link.url}
|
to={link.url}
|
||||||
class:list={[{ active: link.url === Astro.url.pathname }]}
|
class:list={[{ active: Astro.url.pathname.includes(link.url) }]}
|
||||||
>
|
>
|
||||||
<UIString key={link.key} />
|
<UIString key={link.key} />
|
||||||
</TranslatedLink>
|
</TranslatedLink>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { Icon } from "astro-icon";
|
import { Icon } from "astro-icon";
|
||||||
// import TranslatedLink from "@components/TranslatedLink";
|
import TranslatedLink from "@components/TranslatedLink.astro";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
icon: string;
|
icon: string;
|
||||||
@ -12,9 +12,9 @@ const { icon, href } = Astro.props as Props;
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Icon name={icon} width="18" height="18" />
|
<Icon name={icon} width="18" height="18" />
|
||||||
<a href={href}>
|
<TranslatedLink to={href}>
|
||||||
<slot />
|
<slot />
|
||||||
</a>
|
</TranslatedLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@ -16,7 +16,8 @@ export default {
|
|||||||
/* Page descriptions */
|
/* Page descriptions */
|
||||||
"pageDescription.articles":
|
"pageDescription.articles":
|
||||||
"Technical articles, tutorials and various reference materials related to IT topics can be found here.",
|
"Technical articles, tutorials and various reference materials related to IT topics can be found here.",
|
||||||
"pageDescription.projects": "A few of my main projects that have been developed and published in open access.",
|
"pageDescription.projects":
|
||||||
|
"A few of my main projects that have been developed and published in open access.",
|
||||||
"pageDescription.contacts": "Use the feedback form or write to me on social networks.",
|
"pageDescription.contacts": "Use the feedback form or write to me on social networks.",
|
||||||
/* Navigation */
|
/* Navigation */
|
||||||
"navigation.articles": "Articles",
|
"navigation.articles": "Articles",
|
||||||
@ -52,4 +53,6 @@ export default {
|
|||||||
"form.contactError": "An error occurred while sending. Please try again later.",
|
"form.contactError": "An error occurred while sending. Please try again later.",
|
||||||
/* Error messages */
|
/* Error messages */
|
||||||
"error.invalidEmail": "Fill in the field in the e-mail format.",
|
"error.invalidEmail": "Fill in the field in the e-mail format.",
|
||||||
|
/* Other */
|
||||||
|
"rss.feed": "RSS Feed",
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,8 +16,10 @@ export default {
|
|||||||
/* Page descriptions */
|
/* Page descriptions */
|
||||||
"pageDescription.articles":
|
"pageDescription.articles":
|
||||||
"Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой находятся здесь.",
|
"Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой находятся здесь.",
|
||||||
"pageDescription.projects": "Несколько моих основных проектов которые были разработаны и выложены в открытый доступ.",
|
"pageDescription.projects":
|
||||||
"pageDescription.contacts": "Воспользуйтесь формой обратной связи или напишите мне в социальные сети.",
|
"Несколько моих основных проектов которые были разработаны и выложены в открытый доступ.",
|
||||||
|
"pageDescription.contacts":
|
||||||
|
"Воспользуйтесь формой обратной связи или напишите мне в социальные сети.",
|
||||||
/* Navigation */
|
/* Navigation */
|
||||||
"navigation.articles": "Статьи",
|
"navigation.articles": "Статьи",
|
||||||
"navigation.projects": "Проекты",
|
"navigation.projects": "Проекты",
|
||||||
@ -52,4 +54,6 @@ export default {
|
|||||||
"form.contactError": "При отправке произошла ошибка. Повторите запрос позже.",
|
"form.contactError": "При отправке произошла ошибка. Повторите запрос позже.",
|
||||||
/* Error messages */
|
/* Error messages */
|
||||||
"error.invalidEmail": "Заполните поле в формате e-mail.",
|
"error.invalidEmail": "Заполните поле в формате e-mail.",
|
||||||
|
/* Other */
|
||||||
|
"rss.feed": "RSS Лента",
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,13 +2,9 @@ import type { AstroGlobal } from "astro";
|
|||||||
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "@root/constants";
|
import { DEFAULT_LANGUAGE, SUPPORTED_LANGUAGES } from "@root/constants";
|
||||||
import languages from "./languages";
|
import languages from "./languages";
|
||||||
|
|
||||||
function transformExports<T>(modules: Record<string, { default: T }>): Record<string, string> {
|
export function useTranslation(Astro: Readonly<AstroGlobal>): (key: UIDictionaryKeys) => string {
|
||||||
const translations: Record<string, T> = {};
|
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||||
for (const [path, module] of Object.entries(modules)) {
|
return (key: UIDictionaryKeys) => translate(key, pageLang);
|
||||||
const [, lang] = path.split("/");
|
|
||||||
translations[lang] = module.default;
|
|
||||||
}
|
|
||||||
return translations;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLanguageKeys(): Array<string> {
|
export function getLanguageKeys(): Array<string> {
|
||||||
@ -27,19 +23,21 @@ export function removeLangFromURL(pathname: string): string {
|
|||||||
return pathname.split("/").slice(2).join("/");
|
return pathname.split("/").slice(2).join("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
const translations = transformExports(import.meta.glob("./*/ui.ts", { eager: true }));
|
function transformExports<T>(modules: Record<string, { default: T }>) {
|
||||||
|
const translations: Record<string, T> = {};
|
||||||
|
for (const [path, module] of Object.entries(modules)) {
|
||||||
|
const [, lang] = path.split("/");
|
||||||
|
translations[lang as LanguageKeys] = module.default;
|
||||||
|
}
|
||||||
|
return translations;
|
||||||
|
}
|
||||||
|
|
||||||
const s = translations["ru"];
|
const translations = transformExports<Record<string, UIDictionaryKeys>>(
|
||||||
|
import.meta.glob("./*/ui.ts", { eager: true })
|
||||||
|
);
|
||||||
|
|
||||||
function translate(key: UIDictionaryKeys, lang: LanguageKeys): string | undefined {
|
function translate(key: UIDictionaryKeys, lang: LanguageKeys): string {
|
||||||
const value = translations[lang]?.[key] || translations[DEFAULT_LANGUAGE][key];
|
const value = translations[lang]?.[key] || translations[DEFAULT_LANGUAGE][key];
|
||||||
if (value === undefined) console.log(`Cannot find any string for translation key "${key}".`);
|
if (value === undefined) console.log(`Cannot find any string for translation key "${key}".`);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTranslation(
|
|
||||||
Astro: Readonly<AstroGlobal>
|
|
||||||
): (key: UIDictionaryKeys) => string | undefined {
|
|
||||||
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
|
||||||
return (key: UIDictionaryKeys) => translate(key, pageLang);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import { getCollection, getEntryBySlug } from "astro:content";
|
import { getCollection, getEntryBySlug } from "astro:content";
|
||||||
import { getLanguageFromURL } from "@i18n/utils";
|
import { getLanguageFromURL, useTranslation } from "@i18n/utils";
|
||||||
import PageLayout from "@layouts/default.astro";
|
import PageLayout from "@layouts/default.astro";
|
||||||
import PageHeading from "@components/PageHeading.astro";
|
import PageHeading from "@components/PageHeading.astro";
|
||||||
import SkillsList from "@components/SkillsList.astro";
|
import SkillsList from "@components/SkillsList.astro";
|
||||||
@ -11,12 +11,11 @@ const pageLang = getLanguageFromURL(Astro.url.pathname);
|
|||||||
const skills = await getCollection("skills", (e) => e.id.startsWith(`${pageLang}/`));
|
const skills = await getCollection("skills", (e) => e.id.startsWith(`${pageLang}/`));
|
||||||
const about = await getEntryBySlug("authors", `${pageLang}/сергей-ярков`);
|
const about = await getEntryBySlug("authors", `${pageLang}/сергей-ярков`);
|
||||||
const data = await about?.render();
|
const data = await about?.render();
|
||||||
|
const t = useTranslation(Astro);
|
||||||
// const t = useTranslation(Astro);
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout noIndex lang={pageLang} title={"Обо мне"}>
|
<PageLayout noIndex lang={pageLang} title={t("pageTitle.about")}>
|
||||||
<PageHeading heading={"Обо мне"} />
|
<PageHeading heading={t("pageTitle.about")} />
|
||||||
<section>
|
<section>
|
||||||
<ArticleContent>
|
<ArticleContent>
|
||||||
{data && <data.Content />}
|
{data && <data.Content />}
|
||||||
|
|||||||
@ -1,29 +1,20 @@
|
|||||||
---
|
---
|
||||||
import PageLayout from "@layouts/default.astro";
|
import PageLayout from "@layouts/default.astro";
|
||||||
import PageHeading from "@components/PageHeading.astro";
|
import PageHeading from "@components/PageHeading.astro";
|
||||||
// import ArticlesList from "@components/ArticlesList";
|
import { useTranslation, getLanguageFromURL } from "@i18n/utils";
|
||||||
// import * as utils from "@utils/articles";
|
|
||||||
import { getLanguageFromURL } from "@i18n/utils";
|
const t = useTranslation(Astro);
|
||||||
// import { useTranslation } from "@i18n/utils";
|
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||||
// import * as API from "@root/lib/api";
|
|
||||||
// const t = useTranslation(Astro);
|
|
||||||
// const data = API.fetchTranslatedArticles(Astro);
|
|
||||||
// const tags = new Set(data.map((article) => article.frontmatter.tags).flat());
|
|
||||||
const lang = getLanguageFromURL(Astro.url.pathname);
|
|
||||||
// const articles = utils.transformArticles(data);
|
|
||||||
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout
|
<PageLayout
|
||||||
lang={lang}
|
lang={pageLang}
|
||||||
title={"Статьи"}
|
title={t("pageTitle.articles")}
|
||||||
description={"Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой находятся здесь."}
|
description={t("pageDescription.articles")}
|
||||||
canonicalUrl={canonicalUrl.href}
|
canonicalUrl={canonicalUrl.href}
|
||||||
>
|
>
|
||||||
<PageHeading
|
<PageHeading heading={t("pageTitle.articles")} description={t("pageDescription.articles")} />
|
||||||
heading={"Статьи"}
|
|
||||||
description={"Технические статьи, туториалы и различные справочные материалы которые связаны с IT тематикой находятся здесь."}
|
|
||||||
/>
|
|
||||||
<section>
|
<section>
|
||||||
...
|
...
|
||||||
<!-- <ArticlesList
|
<!-- <ArticlesList
|
||||||
|
|||||||
@ -3,21 +3,18 @@ import PageLayout from "@layouts/default.astro";
|
|||||||
import PageHeading from "@components/PageHeading.astro";
|
import PageHeading from "@components/PageHeading.astro";
|
||||||
import ContactForm from "@components/ContactForm.astro";
|
import ContactForm from "@components/ContactForm.astro";
|
||||||
import ContactsList from "@components/ContactsList.astro";
|
import ContactsList from "@components/ContactsList.astro";
|
||||||
import { getLanguageFromURL } from "@i18n/utils";
|
import { getLanguageFromURL, useTranslation } from "@i18n/utils";
|
||||||
// const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
const lang = getLanguageFromURL(Astro.url.pathname);
|
const lang = getLanguageFromURL(Astro.url.pathname);
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout
|
<PageLayout
|
||||||
noIndex
|
noIndex
|
||||||
lang={lang}
|
lang={lang}
|
||||||
title={"Контакты"}
|
title={t("pageTitle.contacts")}
|
||||||
description={"Воспользуйтесь формой обратной связи или напишите мне в социальные сети."}
|
description={t("pageDescription.contacts")}
|
||||||
>
|
>
|
||||||
<PageHeading
|
<PageHeading heading={t("pageTitle.contacts")} description={t("pageDescription.contacts")} />
|
||||||
heading={"Контакты"}
|
|
||||||
description={"Воспользуйтесь формой обратной связи или напишите мне в социальные сети."}
|
|
||||||
/>
|
|
||||||
<section>
|
<section>
|
||||||
<ContactForm />
|
<ContactForm />
|
||||||
<ContactsList />
|
<ContactsList />
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import PageLayout from "@layouts/default.astro";
|
|||||||
import ProjectsList from "@components/ProjectsList.astro";
|
import ProjectsList from "@components/ProjectsList.astro";
|
||||||
import ProjectItem from "@components/ProjectItem.astro";
|
import ProjectItem from "@components/ProjectItem.astro";
|
||||||
import PageHeading from "@components/PageHeading.astro";
|
import PageHeading from "@components/PageHeading.astro";
|
||||||
import { getLanguageFromURL } from "@i18n/utils";
|
import { getLanguageFromURL, useTranslation } from "@i18n/utils";
|
||||||
|
|
||||||
// const t = useTranslation(Astro);
|
const t = useTranslation(Astro);
|
||||||
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
const pageLang = getLanguageFromURL(Astro.url.pathname);
|
||||||
const projects = (await getCollection("projects", (e) => e.slug.startsWith(`${pageLang}/`))).sort(
|
const projects = (await getCollection("projects", (e) => e.slug.startsWith(`${pageLang}/`))).sort(
|
||||||
(a, b) => b.data.year - a.data.year
|
(a, b) => b.data.year - a.data.year
|
||||||
@ -16,13 +16,10 @@ const projects = (await getCollection("projects", (e) => e.slug.startsWith(`${pa
|
|||||||
<PageLayout
|
<PageLayout
|
||||||
noIndex
|
noIndex
|
||||||
lang={pageLang}
|
lang={pageLang}
|
||||||
title={"Проекты"}
|
title={t("pageTitle.projects")}
|
||||||
description={"Несколько моих основных проектов которые были разработаны и выложены в открытый доступ."}
|
description={t("pageDescription.projects")}
|
||||||
>
|
>
|
||||||
<PageHeading
|
<PageHeading heading={t("pageTitle.projects")} description={t("pageDescription.projects")} />
|
||||||
heading={"Проекты"}
|
|
||||||
description={"Несколько моих основных проектов которые были разработаны и выложены в открытый доступ."}
|
|
||||||
/>
|
|
||||||
<section>
|
<section>
|
||||||
<ProjectsList>
|
<ProjectsList>
|
||||||
{projects.map((project) => <ProjectItem data={project.data} />)}
|
{projects.map((project) => <ProjectItem data={project.data} />)}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user