@jjlmoya/utils-developer 1.30.0 → 1.31.0

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.
Files changed (140) hide show
  1. package/package.json +70 -70
  2. package/src/category/seo.astro +1 -1
  3. package/src/components/PreviewNavSidebar.astro +1 -1
  4. package/src/components/PreviewToolbar.astro +1 -1
  5. package/src/data.ts +1 -1
  6. package/src/env.d.ts +1 -1
  7. package/src/layouts/PreviewLayout.astro +1 -1
  8. package/src/pages/[locale]/[slug].astro +162 -162
  9. package/src/pages/index.astro +1 -1
  10. package/src/tests/entry_contract.test.ts +8 -2
  11. package/src/tests/faq_count.test.ts +1 -1
  12. package/src/tests/i18n_coverage.test.ts +2 -6
  13. package/src/tests/inverted_punctuation.test.ts +113 -113
  14. package/src/tests/mocks/astro_mock.js +1 -1
  15. package/src/tests/no_h1_in_components.test.ts +1 -1
  16. package/src/tests/promote_this_website_i18n.test.ts +33 -0
  17. package/src/tests/slug_language_code_format.test.ts +25 -25
  18. package/src/tests/slug_uniqueness.test.ts +86 -86
  19. package/src/tests/tool_validation.test.ts +15 -15
  20. package/src/tool/aspectRatio/i18n/de.ts +164 -164
  21. package/src/tool/aspectRatio/i18n/ja.ts +1 -1
  22. package/src/tool/aspectRatio/i18n/ko.ts +1 -1
  23. package/src/tool/aspectRatio/i18n/pl.ts +164 -164
  24. package/src/tool/aspectRatio/i18n/ru.ts +164 -164
  25. package/src/tool/aspectRatio/i18n/zh.ts +165 -165
  26. package/src/tool/aspectRatio/seo.astro +15 -15
  27. package/src/tool/calculadoraTiempoDatos/i18n/de.ts +143 -143
  28. package/src/tool/calculadoraTiempoDatos/i18n/ja.ts +1 -1
  29. package/src/tool/calculadoraTiempoDatos/i18n/ko.ts +1 -1
  30. package/src/tool/calculadoraTiempoDatos/i18n/pl.ts +143 -143
  31. package/src/tool/calculadoraTiempoDatos/i18n/ru.ts +144 -144
  32. package/src/tool/calculadoraTiempoDatos/i18n/sv.ts +143 -143
  33. package/src/tool/calculadoraTiempoDatos/i18n/zh.ts +144 -144
  34. package/src/tool/calculadoraTiempoDatos/seo.astro +15 -15
  35. package/src/tool/colorConverter/i18n/de.ts +120 -120
  36. package/src/tool/colorConverter/i18n/ja.ts +1 -1
  37. package/src/tool/colorConverter/i18n/ko.ts +1 -1
  38. package/src/tool/colorConverter/i18n/ru.ts +120 -120
  39. package/src/tool/colorConverter/i18n/zh.ts +1 -1
  40. package/src/tool/colorConverter/seo.astro +15 -15
  41. package/src/tool/conversorExcelCsvHtml/i18n/ja.ts +1 -1
  42. package/src/tool/conversorExcelCsvHtml/i18n/ko.ts +1 -1
  43. package/src/tool/conversorExcelCsvHtml/i18n/pl.ts +124 -124
  44. package/src/tool/conversorExcelCsvHtml/i18n/ru.ts +124 -124
  45. package/src/tool/conversorExcelCsvHtml/i18n/zh.ts +125 -125
  46. package/src/tool/conversorExcelCsvHtml/seo.astro +15 -15
  47. package/src/tool/cronGenerator/i18n/ja.ts +1 -1
  48. package/src/tool/cronGenerator/i18n/ko.ts +1 -1
  49. package/src/tool/cronGenerator/i18n/zh.ts +183 -183
  50. package/src/tool/cronGenerator/seo.astro +15 -15
  51. package/src/tool/cssSpecificityCalculator/i18n/ja.ts +1 -1
  52. package/src/tool/cssSpecificityCalculator/i18n/ko.ts +1 -1
  53. package/src/tool/cssSpecificityCalculator/i18n/zh.ts +1 -1
  54. package/src/tool/cssSpecificityCalculator/seo.astro +15 -15
  55. package/src/tool/cssToInlineConverter/i18n/ja.ts +1 -1
  56. package/src/tool/cssToInlineConverter/i18n/ko.ts +1 -1
  57. package/src/tool/cssToInlineConverter/i18n/zh.ts +1 -1
  58. package/src/tool/cssToInlineConverter/seo.astro +15 -15
  59. package/src/tool/duplicateCssRemover/i18n/ja.ts +1 -1
  60. package/src/tool/duplicateCssRemover/i18n/ko.ts +1 -1
  61. package/src/tool/duplicateCssRemover/i18n/zh.ts +1 -1
  62. package/src/tool/duplicateCssRemover/seo.astro +15 -15
  63. package/src/tool/generadorSecurityTxt/i18n/ja.ts +1 -1
  64. package/src/tool/generadorSecurityTxt/i18n/ko.ts +1 -1
  65. package/src/tool/generadorSecurityTxt/i18n/zh.ts +1 -1
  66. package/src/tool/generadorSecurityTxt/seo.astro +15 -15
  67. package/src/tool/hashGenerator/i18n/ja.ts +1 -1
  68. package/src/tool/hashGenerator/i18n/ko.ts +1 -1
  69. package/src/tool/hashGenerator/seo.astro +15 -15
  70. package/src/tool/inspectorCertificadosSsl/i18n/ja.ts +1 -1
  71. package/src/tool/inspectorCertificadosSsl/i18n/ko.ts +1 -1
  72. package/src/tool/inspectorCertificadosSsl/i18n/zh.ts +1 -1
  73. package/src/tool/inspectorCertificadosSsl/seo.astro +15 -15
  74. package/src/tool/jsonFormatter/seo.astro +15 -15
  75. package/src/tool/jwtDecoder/i18n/es.ts +248 -248
  76. package/src/tool/jwtDecoder/i18n/fr.ts +248 -248
  77. package/src/tool/jwtDecoder/i18n/tr.ts +248 -248
  78. package/src/tool/keycode/seo.astro +15 -15
  79. package/src/tool/llmCostCalculator/i18n/fr.ts +149 -149
  80. package/src/tool/llmCostCalculator/i18n/ja.ts +1 -1
  81. package/src/tool/llmCostCalculator/i18n/ko.ts +1 -1
  82. package/src/tool/llmCostCalculator/seo.astro +15 -15
  83. package/src/tool/mobileMockupGenerator/i18n/ja.ts +1 -1
  84. package/src/tool/mobileMockupGenerator/i18n/ko.ts +1 -1
  85. package/src/tool/mobileMockupGenerator/i18n/zh.ts +1 -1
  86. package/src/tool/mobileMockupGenerator/seo.astro +15 -15
  87. package/src/tool/musicalTypography/i18n/ja.ts +1 -1
  88. package/src/tool/musicalTypography/i18n/ko.ts +1 -1
  89. package/src/tool/musicalTypography/i18n/zh.ts +1 -1
  90. package/src/tool/musicalTypography/seo.astro +15 -15
  91. package/src/tool/placeholderGenerator/i18n/ja.ts +1 -1
  92. package/src/tool/placeholderGenerator/i18n/ko.ts +1 -1
  93. package/src/tool/placeholderGenerator/i18n/zh.ts +1 -1
  94. package/src/tool/placeholderGenerator/seo.astro +15 -15
  95. package/src/tool/promoteThisWebsite/entry.ts +13 -0
  96. package/src/tool/promoteThisWebsite/i18n/de.ts +95 -0
  97. package/src/tool/promoteThisWebsite/i18n/fr.ts +95 -0
  98. package/src/tool/promoteThisWebsite/i18n/id.ts +95 -0
  99. package/src/tool/promoteThisWebsite/i18n/it.ts +95 -0
  100. package/src/tool/promoteThisWebsite/i18n/ja.ts +95 -0
  101. package/src/tool/promoteThisWebsite/i18n/ko.ts +95 -0
  102. package/src/tool/promoteThisWebsite/i18n/nl.ts +95 -0
  103. package/src/tool/promoteThisWebsite/i18n/pl.ts +95 -0
  104. package/src/tool/promoteThisWebsite/i18n/pt.ts +95 -0
  105. package/src/tool/promoteThisWebsite/i18n/ru.ts +95 -0
  106. package/src/tool/promoteThisWebsite/i18n/sv.ts +95 -0
  107. package/src/tool/promoteThisWebsite/i18n/tr.ts +95 -0
  108. package/src/tool/promoteThisWebsite/i18n/zh.ts +95 -0
  109. package/src/tool/promptLibrary/i18n/ja.ts +1 -1
  110. package/src/tool/promptLibrary/i18n/ko.ts +1 -1
  111. package/src/tool/promptLibrary/i18n/zh.ts +1 -1
  112. package/src/tool/promptLibrary/seo.astro +15 -15
  113. package/src/tool/readabilityCalculator/i18n/ja.ts +1 -1
  114. package/src/tool/readabilityCalculator/i18n/ko.ts +1 -1
  115. package/src/tool/readabilityCalculator/i18n/zh.ts +1 -1
  116. package/src/tool/readabilityCalculator/seo.astro +15 -15
  117. package/src/tool/serpPixelSimulator/entry.ts +30 -30
  118. package/src/tool/serpPixelSimulator/i18n/en.ts +211 -211
  119. package/src/tool/serpPixelSimulator/i18n/fr.ts +214 -214
  120. package/src/tool/serpPixelSimulator/i18n/ru.ts +214 -214
  121. package/src/tool/serpPixelSimulator/logic.ts +18 -18
  122. package/src/tool/serpPixelSimulator/serp-pixel-simulator.css +195 -195
  123. package/src/tool/svgSanitizer/i18n/ja.ts +1 -1
  124. package/src/tool/svgSanitizer/i18n/ko.ts +1 -1
  125. package/src/tool/svgSanitizer/seo.astro +15 -15
  126. package/src/tool/svgToCss/i18n/ja.ts +1 -1
  127. package/src/tool/svgToCss/i18n/ko.ts +1 -1
  128. package/src/tool/svgToCss/seo.astro +15 -15
  129. package/src/tool/urlCleaner/i18n/ja.ts +1 -1
  130. package/src/tool/urlCleaner/i18n/ko.ts +1 -1
  131. package/src/tool/urlCleaner/i18n/zh.ts +1 -1
  132. package/src/tool/urlCleaner/seo.astro +15 -15
  133. package/src/tool/urlEncoderDecoder/i18n/es.ts +146 -146
  134. package/src/tool/urlEncoderDecoder/i18n/ko.ts +1 -1
  135. package/src/tool/urlEncoderDecoder/seo.astro +15 -15
  136. package/src/tool/utmGenerator/i18n/ja.ts +1 -1
  137. package/src/tool/utmGenerator/i18n/ko.ts +1 -1
  138. package/src/tool/utmGenerator/i18n/zh.ts +1 -1
  139. package/src/tool/utmGenerator/seo.astro +15 -15
  140. package/src/types.ts +1 -1
@@ -1,214 +1,214 @@
1
- import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
- import type { ToolLocaleContent } from '../../../types';
3
- import type { SerpPixelSimulatorUI } from '../ui';
4
- import { bibliography } from '../bibliography';
5
-
6
- const slug = 'simulyator-serp-schetchik-pikselej';
7
- const title = 'Симулятор SERP и Счётчик Пикселей для SEO';
8
- const description = 'Предпросматривайте поисковые сниппеты в стиле Google в реальном времени, измеряйте ширину заголовка и мета-описания в пикселях и точно видите, где ваш текст будет обрезан.';
9
-
10
- const howTo = [
11
- {
12
- name: 'Введите тег title',
13
- text: 'Введите или вставьте заголовок страницы, который хотите протестировать. Предпросмотр SERP и счётчик пикселей обновляются при каждом нажатии клавиши.',
14
- },
15
- {
16
- name: 'Добавьте видимый URL',
17
- text: 'Используйте реалистичный домен и путь, чтобы сниппет был похож на результат, который просканирует пользователь.',
18
- },
19
- {
20
- name: 'Напишите мета-описание',
21
- text: 'Добавьте текст описания и следите за полосой пикселей. Когда она превышает рекомендуемую визуальную ширину, предпросмотр обрезает её многоточием.',
22
- },
23
- {
24
- name: 'Переключайтесь между компьютером и мобильным',
25
- text: 'Сравните отображение заголовка с шириной карточки на компьютере или мобильном устройстве перед публикацией метаданных.',
26
- },
27
- ];
28
-
29
- const faq = [
30
- {
31
- question: 'Почему считать пиксели, а не символы для SEO-заголовков?',
32
- answer: 'Карточки результатов Google ограничены визуальной шириной. Заголовок с большим количеством узких букв может вместить больше символов, чем заголовок с широкими буквами, прописными или жирно выглядящими глифами. Измерение в пикселях даёт более точное представление о видимом результате.',
33
- },
34
- {
35
- question: 'Гарантирует ли это, как именно Google обрежет мой сниппет?',
36
- answer: 'Нет. Google может переписывать ссылки заголовков и сниппеты, а отображение может варьироваться в зависимости от запроса, устройства, языка и экспериментов. Инструмент задуман как практическое визуальное руководство для написания метаданных, которые с меньшей вероятностью будут обрезаны.',
37
- },
38
- {
39
- question: 'Какие лимиты пикселей использует симулятор?',
40
- answer: 'Лимит заголовка по умолчанию для компьютера составляет 580 пикселей, для мобильных - 600 пикселей, а лимит мета-описания - 920 пикселей. Это цели написания, а не официальные лимиты Google.',
41
- },
42
- {
43
- question: 'Почему предпросмотр добавляет многоточие?',
44
- answer: 'Когда измеренный текст превышает доступную ширину в пикселях, симулятор обрезает строку по последнему вмещающемуся символу и добавляет три точки, имитируя практическое поведение, необходимое SEO-командам для обнаружения потери смысла.',
45
- },
46
- ];
47
-
48
- const ui: SerpPixelSimulatorUI = {
49
- titleLabel: 'Тег title',
50
- titlePlaceholder: 'GameBob | Независимая Студия Разработки',
51
- urlLabel: 'Видимый URL',
52
- urlPlaceholder: 'https://www.gamebob.dev/ru/',
53
- descriptionLabel: 'Мета-описание',
54
- descriptionPlaceholder: 'Откройте для себя нашу коллекцию инструментов и игр, созданных для улучшения вашего цифрового рабочего процесса и развлечений.',
55
- deviceLabel: 'Режим предпросмотра',
56
- desktopLabel: 'Компьютер',
57
- mobileLabel: 'Мобильный',
58
- titlePixelsLabel: 'Ширина заголовка',
59
- descriptionPixelsLabel: 'Ширина описания',
60
- charactersLabel: 'символов',
61
- previewLabel: 'Предпросмотр в стиле Google',
62
- tooLongLabel: 'Слишком широкий',
63
- goodLabel: 'Помещается',
64
- emptyTitle: 'Ваш заголовок появится здесь',
65
- emptyDescription: 'Предпросмотр вашего мета-описания появится здесь по мере ввода.',
66
- defaultTitle: 'GameBob | Независимая Студия Разработки',
67
- defaultUrl: 'https://www.gamebob.dev/ru/',
68
- defaultDescription: 'Откройте для себя нашу коллекцию инструментов и игр, созданных для улучшения вашего цифрового рабочего процесса и развлечений.',
69
- fallbackUrl: 'пример.рф',
70
- fallbackFaviconText: 'G',
71
- pixelUnit: 'px',
72
- ellipsis: '...',
73
- fetchButtonLabel: 'Загрузить',
74
- fetchLoadingLabel: 'Загрузка...',
75
- fetchSuccessLabel: 'Метаданные загружены с URL.',
76
- fetchCorsError: 'Браузер не смог прочитать эту страницу. Возможно, она заблокирована CORS, перенаправлением, смешанным содержимым или сетевым правилом. Вы всё ещё можете вставить или отредактировать метаданные вручную.',
77
- fetchInvalidUrlError: 'Введите корректный URL перед загрузкой метаданных.',
78
- fetchNoMetadataError: 'Страница загружена, но заголовок или мета-описание не найдены.',
79
- fetchGenericError: 'Не удалось загрузить метаданные с этого URL. Проверьте адрес или заполните поля вручную.',
80
- };
81
-
82
- const faqSchema: WithContext<FAQPage> = {
83
- '@context': 'https://schema.org',
84
- '@type': 'FAQPage',
85
- mainEntity: faq.map((item) => ({
86
- '@type': 'Question',
87
- name: item.question,
88
- acceptedAnswer: {
89
- '@type': 'Answer',
90
- text: item.answer,
91
- },
92
- })),
93
- };
94
-
95
- const howToSchema: WithContext<HowTo> = {
96
- '@context': 'https://schema.org',
97
- '@type': 'HowTo',
98
- name: title,
99
- description,
100
- step: howTo.map((step, index) => ({
101
- '@type': 'HowToStep',
102
- position: index + 1,
103
- name: step.name,
104
- text: step.text,
105
- })),
106
- };
107
-
108
- const appSchema: WithContext<SoftwareApplication> = {
109
- '@context': 'https://schema.org',
110
- '@type': 'SoftwareApplication',
111
- name: title,
112
- description,
113
- applicationCategory: 'BusinessApplication',
114
- operatingSystem: 'Any',
115
- offers: {
116
- '@type': 'Offer',
117
- price: '0',
118
- priceCurrency: 'EUR',
119
- },
120
- inLanguage: 'ru',
121
- };
122
-
123
- export const content: ToolLocaleContent<SerpPixelSimulatorUI> = {
124
- slug,
125
- title,
126
- description,
127
- ui,
128
- faqTitle: 'Часто задаваемые вопросы о симуляторе SERP',
129
- faq,
130
- bibliographyTitle: 'Документация по поисковым результатам',
131
- bibliography,
132
- howTo,
133
- schemas: [appSchema, faqSchema, howToSchema],
134
- seo: [
135
- {
136
- type: 'title',
137
- text: 'Перестаньте гадать, как будет выглядеть ваш результат в Google',
138
- level: 2,
139
- },
140
- {
141
- type: 'paragraph',
142
- html: 'Тег title может выглядеть идеально в таблице и всё равно провалиться в поисковой выдаче. Google резервирует пространство не по количеству символов; он отображает текст внутри визуальной карточки. Это означает, что <strong>GameBob | Независимая Студия Разработки</strong> и другой заголовок с тем же количеством символов могут занимать очень разную ширину в зависимости от букв, регистра, пунктуации и пробелов.',
143
- },
144
- {
145
- type: 'tip',
146
- title: 'Правило, которое действительно помогает',
147
- html: 'Пишите сниппет так, чтобы важное обещание пережило многоточие. Разместите тип страницы, поисковое намерение и самую сильную причину кликнуть до пиксельного лимита. Названия брендов полезны, но они не должны вытеснять главное преимущество из поля зрения.',
148
- },
149
- {
150
- type: 'title',
151
- text: 'Что измеряет счётчик пикселей',
152
- level: 3,
153
- },
154
- {
155
- type: 'table',
156
- headers: ['Элемент', 'Что важно', 'Как использовать результат'],
157
- rows: [
158
- ['Тег title', 'Отображаемая ширина в пикселях, а не количество символов', 'Держите основное ключевое слово и обещание клика видимыми до обрезки.'],
159
- ['Видимый URL', 'Визуальное доверие и ясность темы', 'Используйте читаемый путь, который подкрепляет, куда ведёт результат.'],
160
- ['Мета-описание', 'Более широкая область сниппета с поведением, зависящим от запроса', 'Выносите преимущество вперёд, поскольку Google может сократить или переписать его.'],
161
- ['Режим устройства', 'Карточки на компьютере и мобильном могут выглядеть по-разному', 'Проверьте оба режима перед публикацией метаданных для важных страниц.'],
162
- ],
163
- },
164
- {
165
- type: 'title',
166
- text: 'Почему ограничения по символам - плохая SEO-привычка',
167
- level: 3,
168
- },
169
- {
170
- type: 'paragraph',
171
- html: 'Традиционный совет вроде "держите заголовки до 60 символов" удобен, но скрывает реальную проблему. Широкие буквы, такие как W и M, заглавные буквы, разделители, цифры и длинные названия брендов занимают разное пространство. Измерение в пикселях немедленно делает компромисс видимым: вы можете увидеть, заслуживает ли фраза своего места или крадёт пространство у более сильного сообщения.',
172
- },
173
- {
174
- type: 'title',
175
- text: 'Практический рабочий процесс для лучших сниппетов',
176
- level: 3,
177
- },
178
- {
179
- type: 'list',
180
- items: [
181
- '<strong>Начните с намерения:</strong> опишите, что получает пользователь, а не только как называется страница.',
182
- '<strong>Протестируйте полный заголовок:</strong> вставьте его в симулятор и следите за полосой перед публикацией.',
183
- '<strong>Уберите слабые слова:</strong> если полоса стала красной, удалите наполнители, прежде чем обрезать ценные термины.',
184
- '<strong>Проверьте многоточие:</strong> если обрезанный предпросмотр теряет смысл, перепишите заголовок, а не принимайте обрезку.',
185
- '<strong>Повторите для описания:</strong> убедитесь, что первое предложение само по себе несёт ценностное предложение.',
186
- ],
187
- },
188
- {
189
- type: 'diagnostic',
190
- variant: 'info',
191
- title: 'Когда полоса становится красной',
192
- html: 'Красная полоса не является предупреждением о штрафе. Она означает, что текущий текст шире выбранной визуальной цели, поэтому симулятор обрезает его точками. Относитесь к этому как к редакционному сигналу: решите, можно ли пожертвовать скрытыми словами, или сниппет нуждается в более чёткой структуре.',
193
- },
194
- {
195
- type: 'title',
196
- text: 'Ограничения, переписывания и реалистичные ожидания',
197
- level: 3,
198
- },
199
- {
200
- type: 'paragraph',
201
- html: 'Ни один симулятор не может гарантировать точный сниппет, который покажет Google. Google может переписывать ссылки заголовков, выделять жирным термины запроса, выбирать текст страницы вместо мета-описания или показывать разные сниппеты для разных поисковых запросов. Этот инструмент лучше всего использовать как быстрый этап написания и контроля качества: он выявляет явные визуальные переполнения до того, как страница попадёт в продакшн.',
202
- },
203
- {
204
- type: 'summary',
205
- title: 'Наилучшее использование этого симулятора SERP',
206
- items: [
207
- 'Используйте полосу пикселей для обнаружения визуальных переполнений перед публикацией метаданных.',
208
- 'Держите основное поисковое намерение и обещание клика видимыми до любого многоточия.',
209
- 'Загружайте метаданные с URL, которые разрешают CORS, затем редактируйте результат вручную при необходимости.',
210
- 'Рассматривайте предпросмотр как руководство по написанию, поскольку Google всё ещё может переписывать сниппеты под каждый запрос.',
211
- ],
212
- },
213
- ],
214
- };
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { SerpPixelSimulatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
5
+
6
+ const slug = 'simulyator-serp-schetchik-pikselej';
7
+ const title = 'Симулятор SERP и Счётчик Пикселей для SEO';
8
+ const description = 'Предпросматривайте поисковые сниппеты в стиле Google в реальном времени, измеряйте ширину заголовка и мета-описания в пикселях и точно видите, где ваш текст будет обрезан.';
9
+
10
+ const howTo = [
11
+ {
12
+ name: 'Введите тег title',
13
+ text: 'Введите или вставьте заголовок страницы, который хотите протестировать. Предпросмотр SERP и счётчик пикселей обновляются при каждом нажатии клавиши.',
14
+ },
15
+ {
16
+ name: 'Добавьте видимый URL',
17
+ text: 'Используйте реалистичный домен и путь, чтобы сниппет был похож на результат, который просканирует пользователь.',
18
+ },
19
+ {
20
+ name: 'Напишите мета-описание',
21
+ text: 'Добавьте текст описания и следите за полосой пикселей. Когда она превышает рекомендуемую визуальную ширину, предпросмотр обрезает её многоточием.',
22
+ },
23
+ {
24
+ name: 'Переключайтесь между компьютером и мобильным',
25
+ text: 'Сравните отображение заголовка с шириной карточки на компьютере или мобильном устройстве перед публикацией метаданных.',
26
+ },
27
+ ];
28
+
29
+ const faq = [
30
+ {
31
+ question: 'Почему считать пиксели, а не символы для SEO-заголовков?',
32
+ answer: 'Карточки результатов Google ограничены визуальной шириной. Заголовок с большим количеством узких букв может вместить больше символов, чем заголовок с широкими буквами, прописными или жирно выглядящими глифами. Измерение в пикселях даёт более точное представление о видимом результате.',
33
+ },
34
+ {
35
+ question: 'Гарантирует ли это, как именно Google обрежет мой сниппет?',
36
+ answer: 'Нет. Google может переписывать ссылки заголовков и сниппеты, а отображение может варьироваться в зависимости от запроса, устройства, языка и экспериментов. Инструмент задуман как практическое визуальное руководство для написания метаданных, которые с меньшей вероятностью будут обрезаны.',
37
+ },
38
+ {
39
+ question: 'Какие лимиты пикселей использует симулятор?',
40
+ answer: 'Лимит заголовка по умолчанию для компьютера составляет 580 пикселей, для мобильных - 600 пикселей, а лимит мета-описания - 920 пикселей. Это цели написания, а не официальные лимиты Google.',
41
+ },
42
+ {
43
+ question: 'Почему предпросмотр добавляет многоточие?',
44
+ answer: 'Когда измеренный текст превышает доступную ширину в пикселях, симулятор обрезает строку по последнему вмещающемуся символу и добавляет три точки, имитируя практическое поведение, необходимое SEO-командам для обнаружения потери смысла.',
45
+ },
46
+ ];
47
+
48
+ const ui: SerpPixelSimulatorUI = {
49
+ titleLabel: 'Тег title',
50
+ titlePlaceholder: 'GameBob | Независимая Студия Разработки',
51
+ urlLabel: 'Видимый URL',
52
+ urlPlaceholder: 'https://www.gamebob.dev/ru/',
53
+ descriptionLabel: 'Мета-описание',
54
+ descriptionPlaceholder: 'Откройте для себя нашу коллекцию инструментов и игр, созданных для улучшения вашего цифрового рабочего процесса и развлечений.',
55
+ deviceLabel: 'Режим предпросмотра',
56
+ desktopLabel: 'Компьютер',
57
+ mobileLabel: 'Мобильный',
58
+ titlePixelsLabel: 'Ширина заголовка',
59
+ descriptionPixelsLabel: 'Ширина описания',
60
+ charactersLabel: 'символов',
61
+ previewLabel: 'Предпросмотр в стиле Google',
62
+ tooLongLabel: 'Слишком широкий',
63
+ goodLabel: 'Помещается',
64
+ emptyTitle: 'Ваш заголовок появится здесь',
65
+ emptyDescription: 'Предпросмотр вашего мета-описания появится здесь по мере ввода.',
66
+ defaultTitle: 'GameBob | Независимая Студия Разработки',
67
+ defaultUrl: 'https://www.gamebob.dev/ru/',
68
+ defaultDescription: 'Откройте для себя нашу коллекцию инструментов и игр, созданных для улучшения вашего цифрового рабочего процесса и развлечений.',
69
+ fallbackUrl: 'пример.рф',
70
+ fallbackFaviconText: 'G',
71
+ pixelUnit: 'px',
72
+ ellipsis: '...',
73
+ fetchButtonLabel: 'Загрузить',
74
+ fetchLoadingLabel: 'Загрузка...',
75
+ fetchSuccessLabel: 'Метаданные загружены с URL.',
76
+ fetchCorsError: 'Браузер не смог прочитать эту страницу. Возможно, она заблокирована CORS, перенаправлением, смешанным содержимым или сетевым правилом. Вы всё ещё можете вставить или отредактировать метаданные вручную.',
77
+ fetchInvalidUrlError: 'Введите корректный URL перед загрузкой метаданных.',
78
+ fetchNoMetadataError: 'Страница загружена, но заголовок или мета-описание не найдены.',
79
+ fetchGenericError: 'Не удалось загрузить метаданные с этого URL. Проверьте адрес или заполните поля вручную.',
80
+ };
81
+
82
+ const faqSchema: WithContext<FAQPage> = {
83
+ '@context': 'https://schema.org',
84
+ '@type': 'FAQPage',
85
+ mainEntity: faq.map((item) => ({
86
+ '@type': 'Question',
87
+ name: item.question,
88
+ acceptedAnswer: {
89
+ '@type': 'Answer',
90
+ text: item.answer,
91
+ },
92
+ })),
93
+ };
94
+
95
+ const howToSchema: WithContext<HowTo> = {
96
+ '@context': 'https://schema.org',
97
+ '@type': 'HowTo',
98
+ name: title,
99
+ description,
100
+ step: howTo.map((step, index) => ({
101
+ '@type': 'HowToStep',
102
+ position: index + 1,
103
+ name: step.name,
104
+ text: step.text,
105
+ })),
106
+ };
107
+
108
+ const appSchema: WithContext<SoftwareApplication> = {
109
+ '@context': 'https://schema.org',
110
+ '@type': 'SoftwareApplication',
111
+ name: title,
112
+ description,
113
+ applicationCategory: 'BusinessApplication',
114
+ operatingSystem: 'Any',
115
+ offers: {
116
+ '@type': 'Offer',
117
+ price: '0',
118
+ priceCurrency: 'EUR',
119
+ },
120
+ inLanguage: 'ru',
121
+ };
122
+
123
+ export const content: ToolLocaleContent<SerpPixelSimulatorUI> = {
124
+ slug,
125
+ title,
126
+ description,
127
+ ui,
128
+ faqTitle: 'Часто задаваемые вопросы о симуляторе SERP',
129
+ faq,
130
+ bibliographyTitle: 'Документация по поисковым результатам',
131
+ bibliography,
132
+ howTo,
133
+ schemas: [appSchema, faqSchema, howToSchema],
134
+ seo: [
135
+ {
136
+ type: 'title',
137
+ text: 'Перестаньте гадать, как будет выглядеть ваш результат в Google',
138
+ level: 2,
139
+ },
140
+ {
141
+ type: 'paragraph',
142
+ html: 'Тег title может выглядеть идеально в таблице и всё равно провалиться в поисковой выдаче. Google резервирует пространство не по количеству символов; он отображает текст внутри визуальной карточки. Это означает, что <strong>GameBob | Независимая Студия Разработки</strong> и другой заголовок с тем же количеством символов могут занимать очень разную ширину в зависимости от букв, регистра, пунктуации и пробелов.',
143
+ },
144
+ {
145
+ type: 'tip',
146
+ title: 'Правило, которое действительно помогает',
147
+ html: 'Пишите сниппет так, чтобы важное обещание пережило многоточие. Разместите тип страницы, поисковое намерение и самую сильную причину кликнуть до пиксельного лимита. Названия брендов полезны, но они не должны вытеснять главное преимущество из поля зрения.',
148
+ },
149
+ {
150
+ type: 'title',
151
+ text: 'Что измеряет счётчик пикселей',
152
+ level: 3,
153
+ },
154
+ {
155
+ type: 'table',
156
+ headers: ['Элемент', 'Что важно', 'Как использовать результат'],
157
+ rows: [
158
+ ['Тег title', 'Отображаемая ширина в пикселях, а не количество символов', 'Держите основное ключевое слово и обещание клика видимыми до обрезки.'],
159
+ ['Видимый URL', 'Визуальное доверие и ясность темы', 'Используйте читаемый путь, который подкрепляет, куда ведёт результат.'],
160
+ ['Мета-описание', 'Более широкая область сниппета с поведением, зависящим от запроса', 'Выносите преимущество вперёд, поскольку Google может сократить или переписать его.'],
161
+ ['Режим устройства', 'Карточки на компьютере и мобильном могут выглядеть по-разному', 'Проверьте оба режима перед публикацией метаданных для важных страниц.'],
162
+ ],
163
+ },
164
+ {
165
+ type: 'title',
166
+ text: 'Почему ограничения по символам - плохая SEO-привычка',
167
+ level: 3,
168
+ },
169
+ {
170
+ type: 'paragraph',
171
+ html: 'Традиционный совет вроде "держите заголовки до 60 символов" удобен, но скрывает реальную проблему. Широкие буквы, такие как W и M, заглавные буквы, разделители, цифры и длинные названия брендов занимают разное пространство. Измерение в пикселях немедленно делает компромисс видимым: вы можете увидеть, заслуживает ли фраза своего места или крадёт пространство у более сильного сообщения.',
172
+ },
173
+ {
174
+ type: 'title',
175
+ text: 'Практический рабочий процесс для лучших сниппетов',
176
+ level: 3,
177
+ },
178
+ {
179
+ type: 'list',
180
+ items: [
181
+ '<strong>Начните с намерения:</strong> опишите, что получает пользователь, а не только как называется страница.',
182
+ '<strong>Протестируйте полный заголовок:</strong> вставьте его в симулятор и следите за полосой перед публикацией.',
183
+ '<strong>Уберите слабые слова:</strong> если полоса стала красной, удалите наполнители, прежде чем обрезать ценные термины.',
184
+ '<strong>Проверьте многоточие:</strong> если обрезанный предпросмотр теряет смысл, перепишите заголовок, а не принимайте обрезку.',
185
+ '<strong>Повторите для описания:</strong> убедитесь, что первое предложение само по себе несёт ценностное предложение.',
186
+ ],
187
+ },
188
+ {
189
+ type: 'diagnostic',
190
+ variant: 'info',
191
+ title: 'Когда полоса становится красной',
192
+ html: 'Красная полоса не является предупреждением о штрафе. Она означает, что текущий текст шире выбранной визуальной цели, поэтому симулятор обрезает его точками. Относитесь к этому как к редакционному сигналу: решите, можно ли пожертвовать скрытыми словами, или сниппет нуждается в более чёткой структуре.',
193
+ },
194
+ {
195
+ type: 'title',
196
+ text: 'Ограничения, переписывания и реалистичные ожидания',
197
+ level: 3,
198
+ },
199
+ {
200
+ type: 'paragraph',
201
+ html: 'Ни один симулятор не может гарантировать точный сниппет, который покажет Google. Google может переписывать ссылки заголовков, выделять жирным термины запроса, выбирать текст страницы вместо мета-описания или показывать разные сниппеты для разных поисковых запросов. Этот инструмент лучше всего использовать как быстрый этап написания и контроля качества: он выявляет явные визуальные переполнения до того, как страница попадёт в продакшн.',
202
+ },
203
+ {
204
+ type: 'summary',
205
+ title: 'Наилучшее использование этого симулятора SERP',
206
+ items: [
207
+ 'Используйте полосу пикселей для обнаружения визуальных переполнений перед публикацией метаданных.',
208
+ 'Держите основное поисковое намерение и обещание клика видимыми до любого многоточия.',
209
+ 'Загружайте метаданные с URL, которые разрешают CORS, затем редактируйте результат вручную при необходимости.',
210
+ 'Рассматривайте предпросмотр как руководство по написанию, поскольку Google всё ещё может переписывать сниппеты под каждый запрос.',
211
+ ],
212
+ },
213
+ ],
214
+ };
@@ -1,18 +1,18 @@
1
- export const DESKTOP_TITLE_LIMIT = 580;
2
- export const MOBILE_TITLE_LIMIT = 600;
3
- export const DESCRIPTION_LIMIT = 920;
4
-
5
- export type SerpDevice = 'desktop' | 'mobile';
6
-
7
- export function getTitleLimit(device: SerpDevice): number {
8
- return device === 'mobile' ? MOBILE_TITLE_LIMIT : DESKTOP_TITLE_LIMIT;
9
- }
10
-
11
- export function ratioToPercent(width: number, limit: number): number {
12
- if (!Number.isFinite(width) || !Number.isFinite(limit) || limit <= 0) return 0;
13
- return Math.min(100, Math.max(0, (width / limit) * 100));
14
- }
15
-
16
- export function needsPixelTrim(width: number, limit: number): boolean {
17
- return width > limit;
18
- }
1
+ export const DESKTOP_TITLE_LIMIT = 580;
2
+ export const MOBILE_TITLE_LIMIT = 600;
3
+ export const DESCRIPTION_LIMIT = 920;
4
+
5
+ export type SerpDevice = 'desktop' | 'mobile';
6
+
7
+ export function getTitleLimit(device: SerpDevice): number {
8
+ return device === 'mobile' ? MOBILE_TITLE_LIMIT : DESKTOP_TITLE_LIMIT;
9
+ }
10
+
11
+ export function ratioToPercent(width: number, limit: number): number {
12
+ if (!Number.isFinite(width) || !Number.isFinite(limit) || limit <= 0) return 0;
13
+ return Math.min(100, Math.max(0, (width / limit) * 100));
14
+ }
15
+
16
+ export function needsPixelTrim(width: number, limit: number): boolean {
17
+ return width > limit;
18
+ }