translate pages

This commit is contained in:
Sergey Yarkov 2023-02-18 14:09:31 +03:00
parent cb0ae84515
commit c311050b65
No known key found for this signature in database
GPG Key ID: E91CE1FABC619B6F
11 changed files with 108 additions and 100 deletions

View File

@ -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>

View File

@ -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" /> &copy; {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>

View File

@ -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>

View File

@ -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">

View File

@ -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",
}; };

View File

@ -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 Лента",
}; };

View File

@ -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);
}

View File

@ -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 />}

View File

@ -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

View File

@ -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 />

View File

@ -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} />)}