core-maugli 1.2.30 → 1.2.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "core-maugli",
3
3
  "description": "Astro & Tailwind CSS blog theme for Maugli.",
4
4
  "type": "module",
5
- "version": "1.2.30",
5
+ "version": "1.2.32",
6
6
  "license": "GPL-3.0-or-later OR Commercial",
7
7
  "repository": {
8
8
  "type": "git",
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  import { type CollectionEntry } from 'astro:content';
3
- import { getFilteredCollection } from '../utils/content-loader';
4
3
  import { maugliConfig } from '../config/maugli.config';
5
4
  import { LANGUAGES } from '../i18n/languages';
5
+ import { getFilteredCollection } from '../utils/content-loader';
6
6
  import FormattedDate from './FormattedDate.astro';
7
7
  // Универсальный импорт словарей по доступным языкам
8
8
  const dicts: Record<string, any> = {};
@@ -72,12 +72,12 @@ let authorImg = authorData.data.avatar || '/img/default/autor_default.webp';
72
72
  data-astro-reload
73
73
  style="text-decoration: none; color: inherit; z-index: 10; position: relative;"
74
74
  >
75
- <img src={authorImg} alt={authorName} class="w-8 h-8 rounded-full" decoding="async" />
75
+ <img src={authorImg} alt={dict.ui?.authorAvatar || 'Author avatar'} class="w-8 h-8 rounded-full" width="32" height="32" decoding="async" />
76
76
  <span class="font-medium hover:text-[var(--brand-color)] transition-colors duration-200">{authorName}</span>
77
77
  </a>
78
78
  ) : (
79
79
  <>
80
- <img src={authorImg} alt={authorName} class="w-8 h-8 rounded-full" decoding="async" />
80
+ <img src={authorImg} alt={dict.ui?.authorAvatar || 'Author avatar'} class="w-8 h-8 rounded-full" width="32" height="32" decoding="async" />
81
81
  <span class="font-medium">{authorName}</span>
82
82
  </>
83
83
  )
@@ -1,11 +1,22 @@
1
1
  ---
2
2
  import { maugliConfig } from '../config/maugli.config';
3
+ import { LANGUAGES } from '../i18n/languages';
3
4
  import { getFilteredCollection } from '../utils/content-loader';
4
5
  import { getPostsByAuthor } from '../utils/data-utils';
5
6
  import AuthorLinksGroup from './AuthorLinksGroup.astro';
6
7
  import Avatar from './Avatar.astro';
7
8
  import CountBadge from './CountBadge.astro';
8
9
 
10
+ // Универсальный импорт словарей по доступным языкам
11
+ const dicts: Record<string, any> = {};
12
+ for (const lang of LANGUAGES) {
13
+ try {
14
+ dicts[lang.code] = await import(`../i18n/${lang.code}.json`).then((m) => m.default);
15
+ } catch {}
16
+ }
17
+ const lang = maugliConfig.defaultLang || 'en';
18
+ const dict = dicts[lang] || dicts['en'] || {};
19
+
9
20
  export interface Props {
10
21
  author: {
11
22
  name: string;
@@ -58,7 +69,7 @@ const postCount = getPostsByAuthor(posts, slug).length;
58
69
  <!-- Аватар автора слева -->
59
70
  <Avatar
60
71
  src={avatar || maugliConfig.defaultAuthorImage || '/img/default/autor_default.webp'}
61
- alt={name}
72
+ alt={dict.ui?.authorAvatar || 'Author avatar'}
62
73
  size="clamp(70px, 15vw, 100px)"
63
74
  class="no-border"
64
75
  />
@@ -13,13 +13,7 @@ const avatarSize = typeof size === 'number' ? `${size}px` : size;
13
13
  ---
14
14
 
15
15
  <div class:list={['avatar-container', className]} style={`width: ${avatarSize}; height: ${avatarSize};`}>
16
- <img
17
- src={src || '/img/default/autor_default.webp'}
18
- alt={alt}
19
- loading="lazy"
20
- decoding="async"
21
- class="w-full h-full object-cover"
22
- />
16
+ <img src={src || '/img/default/autor_default.webp'} alt={alt} decoding="async" class="w-full h-full object-cover" />
23
17
  </div>
24
18
 
25
19
  <style>
@@ -62,7 +62,7 @@ const isTagPage = currentUrl.startsWith('/tags/') && pathParts.length === 1;
62
62
  idx === 0 ? (
63
63
  <a href={crumb.href} class="flex-shrink-0" target="_blank" rel="noopener noreferrer">
64
64
  <picture>
65
- <img src={crumb.icon} alt="Maugli" class="w-9 h-9" decoding="async" />
65
+ <img src={crumb.icon} alt="Maugli" class="w-9 h-9" width="36" height="36" decoding="async" />
66
66
  </picture>
67
67
  </a>
68
68
  ) : (
@@ -55,7 +55,7 @@ const copyrightYears = yearStart === yearEnd ? `${yearStart}` : `${yearStart}–
55
55
  <div class="flex flex-wrap gap-x-6 gap-y-1 items-center">
56
56
  {lang === 'ru' ? (
57
57
  <a
58
- class="footer-link flex items-center text-muted mr-2"
58
+ class="footer-link flex items-center text-muted mr-2 min-w-12 min-h-12 sm:min-w-0 sm:min-h-0"
59
59
  href="/rss.xml"
60
60
  target="_blank"
61
61
  rel="noopener"
@@ -64,7 +64,13 @@ const copyrightYears = yearStart === yearEnd ? `${yearStart}` : `${yearStart}–
64
64
  RSS
65
65
  </a>
66
66
  ) : (
67
- <a class="footer-link flex items-center mr-2" href="/rss.xml" aria-label="RSS" target="_blank" rel="noopener">
67
+ <a
68
+ class="footer-link flex items-center mr-2 min-w-12 min-h-12 sm:min-w-0 sm:min-h-0"
69
+ href="/rss.xml"
70
+ aria-label="RSS"
71
+ target="_blank"
72
+ rel="noopener"
73
+ >
68
74
  <svg
69
75
  width="20"
70
76
  height="20"
@@ -83,7 +89,7 @@ const copyrightYears = yearStart === yearEnd ? `${yearStart}` : `${yearStart}–
83
89
  </a>
84
90
  )}
85
91
  {navLinks.map((link) => (
86
- <a class="footer-link font-serif text-heading" href={link.href}>
92
+ <a class="footer-link font-serif text-heading min-w-12 min-h-12 sm:min-w-0 sm:min-h-0 flex items-center" href={link.href}>
87
93
  {link.label}
88
94
  </a>
89
95
  ))}
@@ -112,15 +118,6 @@ const copyrightYears = yearStart === yearEnd ? `${yearStart}` : `${yearStart}–
112
118
  <a href="https://www.npmjs.com/package/core-maugli" target="_blank" rel="noopener" aria-label="Core Maugli на NPM">
113
119
  <img src="/footerlabel.svg" alt="Maugli Label" style="height:32px;width:auto;" loading="lazy" decoding="async" width="184" height="68" />
114
120
  </a>
115
- <a
116
- href="https://www.npmjs.com/package/core-maugli"
117
- target="_blank"
118
- rel="noopener noreferrer"
119
- class="footer-link text-xs opacity-60 hover:opacity-100"
120
- style="color:var(--text-muted);font-family:var(--font-sans);margin-left:8px;"
121
- >
122
- npm package
123
- </a>
124
121
  {
125
122
  Object.values(maugliConfig.links || {}).some(Boolean) && (
126
123
  <div class="flex flex-row items-center gap-2 ml-4">
@@ -25,15 +25,7 @@ const current = availableLanguages.find((l) => l.code === currentLang) || availa
25
25
  style="min-width:28px; min-height:28px; max-width:28px; max-height:28px;"
26
26
  >
27
27
  {current && (
28
- <img
29
- src={current.icon}
30
- alt={current.code.toUpperCase()}
31
- width="28"
32
- height="28"
33
- style="pointer-events:none;"
34
- loading="lazy"
35
- decoding="async"
36
- />
28
+ <img src={current.icon} alt={current.code.toUpperCase()} width="28" height="28" style="pointer-events:none;" decoding="async" />
37
29
  )}
38
30
  </span>
39
31
  <svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
@@ -67,15 +59,7 @@ const current = availableLanguages.find((l) => l.code === currentLang) || availa
67
59
  class="flag-box flex items-center justify-center w-7 h-7 rounded-[var(--radius-main)] shadow-[var(--shadow-main)]"
68
60
  style="min-width:28px; min-height:28px; max-width:28px; max-height:28px;"
69
61
  >
70
- <img
71
- src={lang.icon}
72
- alt={lang.code.toUpperCase()}
73
- width="28"
74
- height="28"
75
- style="pointer-events:none;"
76
- loading="lazy"
77
- decoding="async"
78
- />
62
+ <img src={lang.icon} alt={lang.code.toUpperCase()} width="28" height="28" style="pointer-events:none;" decoding="async" />
79
63
  </span>
80
64
  <span class="text-heading">{lang.label}</span>
81
65
  </a>
@@ -16,7 +16,7 @@ const netlifyUrl = `https://app.netlify.com/start/deploy?repository=${repository
16
16
  {
17
17
  netlifyEnabled && (
18
18
  <a href={netlifyUrl} target="_blank" rel="noopener noreferrer" class={className}>
19
- <img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" loading="lazy" decoding="async" />
19
+ <img src="https://www.netlify.com/img/deploy/button.svg" alt="Deploy to Netlify" width="114" height="32" loading="lazy" decoding="async" />
20
20
  </a>
21
21
  )
22
22
  }
@@ -43,6 +43,8 @@ const rubricImageFinal = rubricImage && rubricImage.length > 0 ? rubricImage : m
43
43
  alt={rubricName || tagsSection.rubricAlt}
44
44
  class="w-20 h-20 sm:w-40 sm:h-40 object-cover rounded-[16px_48px_16px_48px] border border-[var(--border-main)]"
45
45
  style="flex-shrink:0;"
46
+ width="160"
47
+ height="160"
46
48
  decoding="async"
47
49
  />
48
50
  <div class="flex flex-col items-center sm:items-start text-center sm:text-left">
package/src/i18n/de.json CHANGED
@@ -13,7 +13,9 @@
13
13
  "emailLabel": "E-Mail",
14
14
  "subscribeAriaLabel": "Abonnement-Formular"
15
15
  },
16
-
16
+ "ui": {
17
+ "authorAvatar": "Autor-Avatar"
18
+ },
17
19
  "nav": {
18
20
  "blog": "Blog",
19
21
  "products": "Produkte",
package/src/i18n/en.json CHANGED
@@ -13,7 +13,9 @@
13
13
  "emailLabel": "Email",
14
14
  "subscribeAriaLabel": "Subscription form"
15
15
  },
16
-
16
+ "ui": {
17
+ "authorAvatar": "Author avatar"
18
+ },
17
19
  "nav": {
18
20
  "blog": "Blog",
19
21
  "products": "Products",
package/src/i18n/es.json CHANGED
@@ -13,7 +13,9 @@
13
13
  "emailLabel": "Correo electrónico",
14
14
  "subscribeAriaLabel": "Formulario de suscripción"
15
15
  },
16
-
16
+ "ui": {
17
+ "authorAvatar": "Avatar del autor"
18
+ },
17
19
  "nav": {
18
20
  "blog": "Blog",
19
21
  "products": "Productos",
package/src/i18n/fr.json CHANGED
@@ -13,7 +13,9 @@
13
13
  "emailLabel": "Email",
14
14
  "subscribeAriaLabel": "Formulaire d'abonnement"
15
15
  },
16
-
16
+ "ui": {
17
+ "authorAvatar": "Avatar de l'auteur"
18
+ },
17
19
  "nav": {
18
20
  "blog": "Blog",
19
21
  "products": "Produits",
package/src/i18n/ja.json CHANGED
@@ -13,7 +13,9 @@
13
13
  "emailLabel": "メール",
14
14
  "subscribeAriaLabel": "購読フォーム"
15
15
  },
16
-
16
+ "ui": {
17
+ "authorAvatar": "著者のアバター"
18
+ },
17
19
  "nav": {
18
20
  "blog": "ブログ",
19
21
  "products": "製品",
package/src/i18n/pt.json CHANGED
@@ -13,7 +13,9 @@
13
13
  "emailLabel": "Email",
14
14
  "subscribeAriaLabel": "Formulário de inscrição"
15
15
  },
16
-
16
+ "ui": {
17
+ "authorAvatar": "Avatar do autor"
18
+ },
17
19
  "nav": {
18
20
  "blog": "Blog",
19
21
  "products": "Produtos",
package/src/i18n/ru.json CHANGED
@@ -13,7 +13,9 @@
13
13
  "emailLabel": "Почта",
14
14
  "subscribeAriaLabel": "Форма подписки"
15
15
  },
16
-
16
+ "ui": {
17
+ "authorAvatar": "Аватар автора"
18
+ },
17
19
  "nav": {
18
20
  "blog": "Блог",
19
21
  "products": "Продукты",
package/src/i18n/zh.json CHANGED
@@ -13,7 +13,9 @@
13
13
  "emailLabel": "电子邮件",
14
14
  "subscribeAriaLabel": "订阅表单"
15
15
  },
16
-
16
+ "ui": {
17
+ "authorAvatar": "作者头像"
18
+ },
17
19
  "nav": {
18
20
  "blog": "博客",
19
21
  "products": "产品",
@@ -5,6 +5,7 @@ import PostPreview from '../../components/PostPreview.astro';
5
5
  import Subscribe from '../../components/Subscribe.astro';
6
6
  import TagsSection from '../../components/TagsSection.astro';
7
7
  import { maugliConfig } from '../../config/maugli.config';
8
+ import { LANGUAGES } from '../../i18n/languages';
8
9
  import BlueskyIcon from '../../icons/socials/BlueskyIcon.astro';
9
10
  import EmailIcon from '../../icons/socials/EmailIcon.astro';
10
11
  import LinkedinIcon from '../../icons/socials/LinkedinIcon.astro';
@@ -33,6 +34,16 @@ const { name, position, description, avatar: rawAvatar, socials, seo } = author.
33
34
  const avatar = rawAvatar ? rawAvatar.replace('/img/examples/authors/', '/img/authors/') : undefined;
34
35
  const { Content } = await render(author);
35
36
 
37
+ // Универсальный импорт словарей по доступным языкам
38
+ const dicts: Record<string, any> = {};
39
+ for (const lang of LANGUAGES) {
40
+ try {
41
+ dicts[lang.code] = await import(`../../i18n/${lang.code}.json`).then((m) => m.default);
42
+ } catch {}
43
+ }
44
+ const lang = maugliConfig.defaultLang || 'en';
45
+ const dict = dicts[lang] || dicts['en'] || {};
46
+
36
47
  // Получаем все статьи блога
37
48
  let allPosts = await getFilteredCollection('blog');
38
49
 
@@ -80,7 +91,7 @@ const pageTitle = seo?.title || `${name} - ${position} | Maugli Content Farm`;
80
91
  const pageDescription = seo?.description || description;
81
92
  ---
82
93
 
83
- <BaseLayout title={pageTitle} description={pageDescription} image={{ src: avatar || '/img/default/autor_default.webp', alt: `Фото ${name}` }} showHeader={false} fullWidth={true}>
94
+ <BaseLayout title={pageTitle} description={pageDescription} image={{ src: avatar || '/img/default/autor_default.webp', alt: dict.ui?.authorAvatar || 'Author avatar' }} showHeader={false} fullWidth={true}>
84
95
  <div class="max-w-[1280px] mx-auto">
85
96
  <!-- Хлебные крошки -->
86
97
  <Breadcrumbs />
@@ -91,7 +102,7 @@ const pageDescription = seo?.description || description;
91
102
  <div class="flex flex-col sm:flex-row gap-6 sm:gap-8 items-start">
92
103
  <!-- Аватар -->
93
104
  <div class="w-32 h-32 sm:w-40 sm:h-40 bg-[var(--bg-main)] rounded-full overflow-hidden flex-shrink-0">
94
- <img src={avatar || '/img/default/autor_default.webp'} alt={`Фото ${name}`} class="w-full h-full object-cover" decoding="async" />
105
+ <img src={avatar || '/img/default/autor_default.webp'} alt={dict.ui?.authorAvatar || 'Author avatar'} class="w-full h-full object-cover" width="160" height="160" decoding="async" />
95
106
  </div>
96
107
 
97
108
  <!-- Информация -->