@jjlmoya/utils-pets 1.5.0 → 1.7.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 (55) hide show
  1. package/package.json +59 -58
  2. package/src/category/i18n/de.ts +52 -0
  3. package/src/category/i18n/id.ts +52 -0
  4. package/src/category/i18n/it.ts +52 -0
  5. package/src/category/i18n/ja.ts +52 -0
  6. package/src/category/i18n/ko.ts +52 -0
  7. package/src/category/i18n/nl.ts +52 -0
  8. package/src/category/i18n/pl.ts +52 -0
  9. package/src/category/i18n/pt.ts +52 -0
  10. package/src/category/i18n/ru.ts +52 -0
  11. package/src/category/i18n/sv.ts +52 -0
  12. package/src/category/i18n/tr.ts +52 -0
  13. package/src/category/i18n/zh.ts +52 -0
  14. package/src/category/index.ts +13 -1
  15. package/src/index.ts +1 -1
  16. package/src/tests/i18n_coverage.test.ts +36 -0
  17. package/src/tests/locale_completeness.test.ts +42 -0
  18. package/src/tests/no_h1_in_components.test.ts +48 -0
  19. package/src/tests/schemas_fulfillment.test.ts +23 -0
  20. package/src/tests/slug_language_code_format.test.ts +23 -0
  21. package/src/tests/slug_uniqueness.test.ts +81 -0
  22. package/src/tests/title_quality.test.ts +55 -0
  23. package/src/tool/petAge/i18n/de.ts +189 -0
  24. package/src/tool/petAge/i18n/en.ts +73 -44
  25. package/src/tool/petAge/i18n/es.ts +74 -45
  26. package/src/tool/petAge/i18n/fr.ts +73 -44
  27. package/src/tool/petAge/i18n/id.ts +189 -0
  28. package/src/tool/petAge/i18n/it.ts +189 -0
  29. package/src/tool/petAge/i18n/ja.ts +189 -0
  30. package/src/tool/petAge/i18n/ko.ts +189 -0
  31. package/src/tool/petAge/i18n/nl.ts +189 -0
  32. package/src/tool/petAge/i18n/pl.ts +189 -0
  33. package/src/tool/petAge/i18n/pt.ts +189 -0
  34. package/src/tool/petAge/i18n/ru.ts +189 -0
  35. package/src/tool/petAge/i18n/sv.ts +189 -0
  36. package/src/tool/petAge/i18n/tr.ts +195 -0
  37. package/src/tool/petAge/i18n/zh.ts +189 -0
  38. package/src/tool/petAge/index.ts +13 -1
  39. package/src/tool/petRation/i18n/de.ts +186 -0
  40. package/src/tool/petRation/i18n/en.ts +57 -32
  41. package/src/tool/petRation/i18n/es.ts +70 -45
  42. package/src/tool/petRation/i18n/fr.ts +75 -50
  43. package/src/tool/petRation/i18n/id.ts +186 -0
  44. package/src/tool/petRation/i18n/it.ts +186 -0
  45. package/src/tool/petRation/i18n/ja.ts +186 -0
  46. package/src/tool/petRation/i18n/ko.ts +186 -0
  47. package/src/tool/petRation/i18n/nl.ts +186 -0
  48. package/src/tool/petRation/i18n/pl.ts +186 -0
  49. package/src/tool/petRation/i18n/pt.ts +186 -0
  50. package/src/tool/petRation/i18n/ru.ts +186 -0
  51. package/src/tool/petRation/i18n/sv.ts +186 -0
  52. package/src/tool/petRation/i18n/tr.ts +186 -0
  53. package/src/tool/petRation/i18n/zh.ts +186 -0
  54. package/src/tool/petRation/index.ts +13 -1
  55. package/src/tools.ts +2 -0
@@ -0,0 +1,52 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'domashnie-zhivotnye',
5
+ title: 'Инструменты для Питомцев',
6
+ description: 'Бесплатные калькуляторы и утилиты для владельцев домашних животных. Узнайте реальный возраст вашей собаки или кошки и рассчитайте их дневной рацион с ветеринарной точностью.',
7
+ seo: [
8
+ { type: 'title', text: 'Питание и Благополучие Животных: Наука на Службе Вашего Питомца', level: 2 },
9
+ {
10
+ type: 'paragraph',
11
+ html: 'Забота о собаке, кошке или кролике — это гораздо больше, чем просто еда и ласка. Это требует понимания их биологии, ритма старения и специфических потребностей в энергии. В этом разделе мы предлагаем <strong>бесплатные ветеринарные калькуляторы</strong>, которые применяют проверенные научные формулы, чтобы помочь вам принимать более обоснованные решения в вопросах здоровья вашего спутника.',
12
+ },
13
+ {
14
+ type: 'paragraph',
15
+ html: 'От любопытства узнать, сколько человеческих лет на самом деле вашей собаке, до точного расчета дневного рациона на основе RER (Resting Energy Requirement) — наши инструменты превращают сложные ветеринарные концепции в четкие и практичные ответы для ответственного владельца домашнего животного.',
16
+ },
17
+ { type: 'title', text: 'Реальный Возраст Вашего Питомца: За Пределами Мифа об "Умножении на 7"', level: 2 },
18
+ {
19
+ type: 'paragraph',
20
+ html: 'Популярное убеждение, что один собачий год равен семи человеческим, десятилетиями опровергалось современной ветеринарной наукой. Реальность такова, что собаки взрослеют экспоненциально в первые два года жизни, достигая эквивалента 15 человеческих лет в 12 месяцев и 24 лет в возрасте 24 месяцев. После этого темп замедляется и во многом зависит от <strong>размера животного</strong>: гигантская собака весом более 45 кг может стареть на 60% быстрее, чем маленькая порода.',
21
+ },
22
+ {
23
+ type: 'paragraph',
24
+ html: 'Кошки, с другой стороны, следуют более линейной кривой старения: после первых двух лет ускоренного развития каждый кошачий год примерно эквивалентен четырем человеческим годам. Знание этапа жизни вашего питомца — это не просто любопытство, это важная медицинская информация: она определяет, когда планировать гериатрические осмотры, когда корректировать диету и какие признаки здоровья следует контролировать.',
25
+ },
26
+ { type: 'title', text: 'Точное Питание: Рассчитайте Точный Дневной Рацион', level: 2 },
27
+ {
28
+ type: 'paragraph',
29
+ html: 'Ожирение затрагивает более 50% домашних животных в развитых странах и является прямой причиной проблем с суставами и сердечно-сосудистой системой, а также сокращения продолжительности жизни. Проблема не в отсутствии любви со стороны владельцев, а в <strong>отсутствии точных калорийных ориентиров</strong>. Наш калькулятор дневного рациона применяет стандартную ветеринарную формулу RER (70 × вес^0,75) и умножает ее на соответствующий коэффициент DER для вида вашего животного, этапа его жизни и уровня активности.',
30
+ },
31
+ {
32
+ type: 'list',
33
+ items: [
34
+ '<strong>Калькулятор Возраста Животных:</strong> Преобразует хронологические годы в человеческий эквивалент на основе вида и размера, с указанием этапа жизни и следующего ветеринарного рубежа.',
35
+ '<strong>Калькулятор Дневного Рациона:</strong> Рассчитывает граммы сухого и влажного корма на основе RER и DER для собак, кошек и кроликов.',
36
+ ],
37
+ },
38
+ {
39
+ type: 'tip',
40
+ title: 'Ветеринарный Совет',
41
+ html: 'Эти инструменты предоставляют научные ориентиры, но каждое животное уникально. Всегда консультируйтесь со своим ветеринаром, чтобы адаптировать диету и план здоровья к индивидуальным потребностям вашего питомца.',
42
+ },
43
+ {
44
+ type: 'paragraph',
45
+ html: 'Инвестиции в знания о биологии вашего питомца — самая эффективная форма профилактической медицины. Здоровый вес и своевременная ветеринарная помощь могут добавить годы качественной жизни вашему верному спутнику. Научный подход к уходу гарантирует, что ваш питомец не просто выживет, но и преуспеет на каждом этапе своей жизни.',
46
+ },
47
+ {
48
+ type: 'paragraph',
49
+ html: 'Важность индивидуального подхода невозможно переоценить. В мире, полном общих советов, наши калькуляторы обеспечивают прочную основу для персонализированного ухода за вашим питомцем. Используйте эти ресурсы, чтобы укрепить связь с вашим животным благодаря более глубокому пониманию его физических потребностей.',
50
+ },
51
+ ],
52
+ };
@@ -0,0 +1,52 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'husdjur',
5
+ title: 'Husdjur Tools',
6
+ description: 'Gratis kalkylatorer och verktyg för djurägare. Ta reda på din hund eller katts verkliga ålder och beräkna deras dagliga ranson med veterinär precision.',
7
+ seo: [
8
+ { type: 'title', text: 'Djur Nutrition och Välbefinnande: Vetenskap i Tjänst för Ditt Husdjur', level: 2 },
9
+ {
10
+ type: 'paragraph',
11
+ html: 'Att ta hand om en hund, katt eller kanin är mycket mer än att bara ge dem mat och kärlek. Det innebär att förstå deras biologi, deras åldrande rytm och deras specifika energibehov. I det här avsnittet erbjuder vi <strong>gratis veterinära kalkylatorer</strong> som tillämpar validerade vetenskapliga formler för att hjälpa dig att fatta bättre beslut om din följeslagares hälsa.',
12
+ },
13
+ {
14
+ type: 'paragraph',
15
+ html: 'Från nyfikenheten att veta hur många människoår din hund verkligen har, till den exakta beräkningen av den dagliga ransonen baserat på RER (Resting Energy Requirement), förvandlar våra verktyg komplexa veterinära koncept till tydliga, handlingskraftiga svar för den ansvarsfulla djurägaren.',
16
+ },
17
+ { type: 'title', text: 'Ditt Husdjurs Verkliga Ålder: Bortom Myten om "Gånger 7"', level: 2 },
18
+ {
19
+ type: 'paragraph',
20
+ html: 'Den populära tron att ett hundår är lika med sju människoår har motbevisats av modern veterinärvetenskap sedan decennier. Verkligheten är att hundar mognar exponentiellt under sina första två levnadsår och når motsvarande 15 människoår vid 12 månader och 24 år vid 24 månader. Därefter saktar tempot ner och beror enormt på <strong>djurets storlek</strong>: en jätte hund på över 45 kg kan åldras upp till 60 % snabbare än en liten hundras.',
21
+ },
22
+ {
23
+ type: 'paragraph',
24
+ html: 'Katter å andra sidan följer en mer linjär ålderskurva: efter de första två åren av accelererad utveckling är varje kattår ungefär lika med fyra människoår. Att känna till ditt husdjurs livsstadie är inte bara nyfikenhet, det är relevant medicinsk information: det avgör när man ska planera geriatriska kontroller, när man ska justera kosten och vilka hälsotecken man ska övervaka.',
25
+ },
26
+ { type: 'title', text: 'Precision Nutrition: Beräkna den Exakta Dagliga Ransonen', level: 2 },
27
+ {
28
+ type: 'paragraph',
29
+ html: 'Fetma drabbar mer än 50 % av alla tamdjur i utvecklade länder och är den direkta orsaken till led- och kardiovaskulära problem och en minskad livslängd. Problemet är inte brist på kärlek från ägare, utan <strong>frånvaron av exakta kalorireferenser</strong>. Vår kalkylator för daglig ranson tillämpar standardveterinärens RER-formel (70 × vikt^0,75) och multiplicerar den med motsvarande DER-faktor för ditt djurs art, livsstadium och aktivitetsnivå.',
30
+ },
31
+ {
32
+ type: 'list',
33
+ items: [
34
+ '<strong>Husdjur Ålder Kalkylator:</strong> Konverterar kronologiska år till mänsklig motsvarighet baserat på art och storlek, med livsstadium och nästa veterinära milstolpe.',
35
+ '<strong>Daglig Ranson Kalkylator:</strong> Beräknar gram foder baserat på RER och DER för hundar, katter och kaniner.',
36
+ ],
37
+ },
38
+ {
39
+ type: 'tip',
40
+ title: 'Veterinärt Råd',
41
+ html: 'Dessa verktyg tillhandahåller vetenskapliga startreferenser, men varje djur är unikt. Konsultera alltid din veterinär för att anpassa kosten och hälsoplanen till ditt husdjurs individuella behov.',
42
+ },
43
+ {
44
+ type: 'paragraph',
45
+ html: 'Att investera i kunskap om ditt husdjurs biologi är den mest effektiva formen av förebyggande medicin. En hälsosam vikt och veterinärvård i rätt tid kan lägga till år av kvalitetsliv för din trogna följeslagare. Det vetenskapliga förhållningssättet till vård säkerställer att ditt husdjur inte bara överlever utan blomstrar i alla stadier av livet.',
46
+ },
47
+ {
48
+ type: 'paragraph',
49
+ html: 'Vikten av individuell vård kan inte överskattas. I en värld full av allmänna råd ger våra kalkylatorer en solid grund för den personliga vården av ditt husdjur. Använd dessa resurser för att stärka bandet med ditt djur genom en djupare förståelse för deras fysiska behov.',
50
+ },
51
+ ],
52
+ };
@@ -0,0 +1,52 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'evcil-hayvanlar',
5
+ title: 'Evcil Hayvan Araçları',
6
+ description: 'Evcil hayvan sahipleri için ücretsiz hesaplayıcılar ve yardımcı programlar. Köpeğinizin veya kedinizin gerçek yaşını öğrenin ve günlük mama miktarını veteriner hassasiyetiyle hesaplayın.',
7
+ seo: [
8
+ { type: 'title', text: 'Hayvan Beslenmesi ve Refahı: Evcil Hayvanınızın Hizmetinde Bilim', level: 2 },
9
+ {
10
+ type: 'paragraph',
11
+ html: 'Bir köpeğe, kediye veya tavşana bakmak onlara yiyecek ve sevgi vermekten çok daha fazlasıdır. Bu, biyolojilerini, yaşlanma ritimlerini ve özel enerji ihtiyaçlarını anlamayı içerir. Bu bölümde, arkadaşınızın sağlığı hakkında daha iyi kararlar vermenize yardımcı olmak için doğrulanmış bilimsel formülleri uygulayan <strong>ücretsiz veteriner hesaplayıcıları</strong> sunuyoruz.',
12
+ },
13
+ {
14
+ type: 'paragraph',
15
+ html: 'Köpeğinizin gerçekte kaç insan yaşında olduğunu bilme merakından, RER\'e (Resting Energy Requirement) dayalı günlük rasyonun kesin hesaplanmasına kadar, araçlarımız karmaşık veteriner kavramlarını sorumlu evcil hayvan sahibi için net, uygulanabilir cevaplara dönüştürür.',
16
+ },
17
+ { type: 'title', text: 'Evcil Hayvanınızın Gerçek Yaşı: "7 ile Çarpma" Efsanesinin Ötesinde', level: 2 },
18
+ {
19
+ type: 'paragraph',
20
+ html: 'Bir köpek yılının yedi insan yılına eşit olduğu yönündeki popüler inanış, modern veteriner bilimi tarafından onlarca yıldır çürütülmüştür. Gerçek şu ki, köpekler yaşamlarının ilk iki yılında katlanarak olgunlaşır, 12 ayda 15 insan yılına ve 24 ayda 24 yıla eşdeğer hale gelirler. Daha sonra tempo yavaşlar ve büyük ölçüde <strong>hayvanın boyutuna</strong> bağlıdır: 45 kg\'ın üzerindeki dev bir köpek, küçük bir cinsten %60\'a kadar daha hızlı yaşlanabilir.',
21
+ },
22
+ {
23
+ type: 'paragraph',
24
+ html: 'Kediler ise daha doğrusal bir yaşlanma eğrisi izler: ilk iki yıllık hızlandırılmış gelişimden sonra, her kedi yılı yaklaşık dört insan yılına eşdeğerdir. Evcil hayvanınızın yaşam evresini bilmek sadece merak değildir, aynı zamanda ilgili tıbbi bilgidir: ne zaman geriatrik kontroller planlanacağını, diyetin ne zaman ayarlanacağını ve hangi sağlık işaretlerini izlemeniz gerektiğini belirler.',
25
+ },
26
+ { type: 'title', text: 'Hassas Beslenme: Tam Günlük Rasyonu Hesaplayın', level: 2 },
27
+ {
28
+ type: 'paragraph',
29
+ html: 'Obezite, gelişmiş ülkelerdeki evcil hayvanların %50\'den fazlasını etkilemektedir ve eklem ve kardiyovasküler problemlerin ve kısalmış yaşam beklentisinin doğrudan nedenidir. Sorun sahiplerin sevgi eksikliği değil, <strong>kesin kalorik referansların yokluğudur</strong>. Günlük rasyon hesaplayıcımız standart veteriner RER formülünü (70 × ağırlık^0,75) uygular ve bunu hayvanınızın türü, yaşam evresi ve aktivite seviyesi için karşılık gelen DER faktörüyle çarpar.',
30
+ },
31
+ {
32
+ type: 'list',
33
+ items: [
34
+ '<strong>Evcil Hayvan Yaş Hesaplayıcı:</strong> Tür ve boyuta göre kronolojik yılları insan eşdeğerine dönüştürür, yaşam evresini ve bir sonraki veteriner dönüm noktasını gösterir.',
35
+ '<strong>Günlük Rasyon Hesaplayıcı:</strong> Köpekler, kediler ve tavşanlar için RER ve DER\'e göre gram cinsinden günlük mama miktarını hesaplar.',
36
+ ],
37
+ },
38
+ {
39
+ type: 'tip',
40
+ title: 'Veteriner Tavsiyesi',
41
+ html: 'Bu araçlar bilimsel başlangıç referansları sağlar, ancak her hayvan benzersizdir. Diyeti ve sağlık planını evcil hayvanınızın bireysel ihtiyaçlarına göre ayarlamak için her zaman veterinerinize danışın.',
42
+ },
43
+ {
44
+ type: 'paragraph',
45
+ html: 'Evcil hayvanınızın biyolojisi hakkında bilgiye yatırım yapmak, koruyucu hekimliğin en etkili şeklidir. Sağlıklı bir ağırlık ve zamanında veteriner bakımı, sadık arkadaşınıza yıllarca kaliteli yaşam katabilir. Bilimsel bir yaklaşım, evcil hayvanınızın yaşamının her aşamasında sadece hayatta kalmasını değil, aynı zamanda gelişmesini sağlar.',
46
+ },
47
+ {
48
+ type: 'paragraph',
49
+ html: 'Bireysel bakımın önemi küçümsenemez. Genel tavsiyelerle dolu bir dünyada, hesaplayıcılarımız evcil hayvanınızın kişiselleştirilmiş bakımı için sağlam bir temel sağlar. Fiziksel ihtiyaçlarını daha derinlemesine anlayarak hayvanınızla olan bağı güçlendirmek için bu kaynakları kullanın.',
50
+ },
51
+ ],
52
+ };
@@ -0,0 +1,52 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'pets',
5
+ title: '宠物 工具',
6
+ description: '为 宠物 主人 提供 的 免费 计算器 和 实用 程序 。 了解 您 的 爱犬 或 爱猫 的 真实 年龄 , 并 以 兽医 般的 精度 计算 它们 的 每日 喂食量 。',
7
+ seo: [
8
+ { type: 'title', text: '动物 营养 与 福祉 : 为 您 的 宠物 提供 科学 服务', level: 2 },
9
+ {
10
+ type: 'paragraph',
11
+ html: '照顾 狗 、 猫 或 兔子 不仅仅 是 给 它们 食物 和 关爱 。 它 涉及 了解 它们 的 生物学 、 它们 的 衰老 节奏 以及 它们 特定 的 能量 需求 。 在 本 章节 中 , 我们 提供 <strong>免费 的 兽医 计算器</strong> , 应用 经过 验证 的 科学 公式 , 帮助 您 就 伴侣 的 健康 做出 更好 的 决定 。',
12
+ },
13
+ {
14
+ type: 'paragraph',
15
+ html: '从 好奇 想 知道 您 的 狗 实际上 有 多少 岁 的 人类 年龄 , 到 基于 RER ( 静息 能量 需求 ) 的 每日 喂食量 的 精确 计算 , 我们 的 工具 将 复杂 的 兽医学 概念 转化 为 对 负责任 的 宠物 主人 而言 清晰 、 可 操作 的 答案 。',
16
+ },
17
+ { type: 'title', text: '您 宠物 的 真实 年龄 : 超越 “ 乘以 7 ” 的 误区', level: 2 },
18
+ {
19
+ type: 'paragraph',
20
+ html: '一个 狗 年 等于 七个 人类 年 的 流行 观念 几十年来 一直 被 现代 兽医学 所 否定 。 现实 情况 是 , 狗 在 生命 的 前 两年 呈 指数 级 成熟 , 在 12 个月 时 达到 相当于 人类 15 岁 的 水平 , 在 24 个月 时 达到 24 岁 。 随后 , 速度 会 变慢 , 并 极大 地 取决于 <strong>动物 的 体型</strong> : 体重 超过 45 公斤 的 巨型 犬 的 衰老 速度 比 小型 犬 快 多 达 60% 。',
21
+ },
22
+ {
23
+ type: 'paragraph',
24
+ html: '另一方面 , 猫 遵循 更加 线性 的 衰老 曲线 : 在 最初 两年 的 加速 发展 之后 , 每个 猫 年 大约 相当于 四个 人类 年 。 了解 您 宠物 的 生命 阶段 不仅仅 是 好奇 , 它 是 相关 的 医学 信息 : 它 决定 了 何时 安排 老年 检查 、 何时 调整 饮食 以及 您 应该 监测 哪些 健康 迹象 。',
25
+ },
26
+ { type: 'title', text: '精准 营养 : 计算 准确 的 每日 喂食量', level: 2 },
27
+ {
28
+ type: 'paragraph',
29
+ html: '肥胖 影响 着 发达国家 超过 50% 的 宠物 , 并且 是 导致 关节 和 心血管 问题 以及 寿命 缩短 的 直接 原因 。 问题 不在于 主人 缺乏 爱心 , 而在于 <strong>缺乏 精确 的 热量 参考</strong> 。 我们 的 每日 喂食量 计算器 应用 标准 的 兽医 RER 公式 ( 70 × 体重^0.75 ) , 并 乘以 对应 于 您 动物 物种 、 生命 阶段 和 活动 水平 的 DER 系数 。',
30
+ },
31
+ {
32
+ type: 'list',
33
+ items: [
34
+ '<strong>宠物 年龄 计算器 :</strong> 根据 物种 和 体型 将 编年 年份 转换 为 人类 等效 年龄 , 并 提供 生命周期 和 下 一个 兽医 里程碑 。',
35
+ '<strong>每日 喂食量 计算器 :</strong> 基于 RER 和 DER 为 狗 、 猫 和 兔子 计算 干粮 和 湿粮 的 克数 。',
36
+ ],
37
+ },
38
+ {
39
+ type: 'tip',
40
+ title: '兽医 建议',
41
+ html: '这些 工具 提供 了 科学 的 起始 参考 , 但 每 只 动物 都是 独特 的 。 请 始终 咨询 您 的 兽医 , 以 将 饮食 和 健康 计划 调整 为 满足 您 宠物 的 个体 需求 。',
42
+ },
43
+ {
44
+ type: 'paragraph',
45
+ html: '投资 于 了解 您 宠物 的 生物学 知识 是 预防 医学 最 有效 的 形式 。 健康 的 体重 和 及时 的 兽医 护理 可以 为 您 忠实 的 伴侣 增加 优质 的 生活 年限 。 科学 的 护理 方法 确保 您 的 宠物 不仅 能 生存 , 而且 能 在 生命 的 每个 阶段 茁壮 成长 。',
46
+ },
47
+ {
48
+ type: 'paragraph',
49
+ html: '个体 护理 的 重要性 不言而喻 。 在 一个 充满 笼统 建议 的 世界 里 , 我们 的 计算器 为 您 宠物 的 个性化 护理 提供 了 坚实 的 基础 。 利用 这些 资源 , 通过 深入 了解 它们 的 物理 需求 来 加强 与 它们 的 纽带 。',
50
+ },
51
+ ],
52
+ };
@@ -6,8 +6,20 @@ export const petsCategory: PetCategoryEntry = {
6
6
  icon: 'mdi:paw',
7
7
  tools: [petAge, petRation],
8
8
  i18n: {
9
- es: () => import('./i18n/es').then((m) => m.content),
10
9
  en: () => import('./i18n/en').then((m) => m.content),
10
+ es: () => import('./i18n/es').then((m) => m.content),
11
11
  fr: () => import('./i18n/fr').then((m) => m.content),
12
+ de: () => import('./i18n/de').then((m) => m.content),
13
+ id: () => import('./i18n/id').then((m) => m.content),
14
+ it: () => import('./i18n/it').then((m) => m.content),
15
+ ja: () => import('./i18n/ja').then((m) => m.content),
16
+ ko: () => import('./i18n/ko').then((m) => m.content),
17
+ nl: () => import('./i18n/nl').then((m) => m.content),
18
+ pl: () => import('./i18n/pl').then((m) => m.content),
19
+ pt: () => import('./i18n/pt').then((m) => m.content),
20
+ ru: () => import('./i18n/ru').then((m) => m.content),
21
+ sv: () => import('./i18n/sv').then((m) => m.content),
22
+ tr: () => import('./i18n/tr').then((m) => m.content),
23
+ zh: () => import('./i18n/zh').then((m) => m.content),
12
24
  },
13
25
  };
package/src/index.ts CHANGED
@@ -17,4 +17,4 @@ export type {
17
17
  PetCategoryEntry,
18
18
  } from './types';
19
19
 
20
- export { ALL_TOOLS } from './tools';
20
+ export { ALL_ENTRIES, ALL_TOOLS } from './tools';
@@ -0,0 +1,36 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+
4
+ const EXPECTED_LOCALES = [
5
+ 'de', 'en', 'es', 'fr', 'id', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'
6
+ ];
7
+
8
+ describe('I18n Coverage Validation', () => {
9
+ it('all tools should be registered', () => {
10
+ expect(ALL_TOOLS.length).toBeGreaterThan(0);
11
+ });
12
+
13
+ ALL_TOOLS.forEach(({ entry }: { entry: any }) => {
14
+ describe(`Tool: ${entry.id}`, () => {
15
+ it('should have all 15 required locales', () => {
16
+ const registeredLocales = Object.keys(entry.i18n);
17
+ EXPECTED_LOCALES.forEach((locale) => {
18
+ expect(
19
+ registeredLocales,
20
+ `Tool "${entry.id}" is missing locale "${locale}"`,
21
+ ).toContain(locale);
22
+ });
23
+ });
24
+
25
+ it('all locale loaders should be functions', () => {
26
+ EXPECTED_LOCALES.forEach((locale) => {
27
+ const loader = entry.i18n[locale as keyof typeof entry.i18n];
28
+ expect(
29
+ typeof loader,
30
+ `Tool "${entry.id}" locale "${locale}" loader is not a function`,
31
+ ).toBe('function');
32
+ });
33
+ });
34
+ });
35
+ });
36
+ });
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+ import type { ToolLocaleContent } from '../types';
4
+
5
+ describe('Locale Completeness Validation', () => {
6
+ ALL_TOOLS.forEach((tool) => {
7
+ describe(`Tool: ${tool.entry.id}`, () => {
8
+ Object.keys(tool.entry.i18n).forEach((locale) => {
9
+ describe(`Locale: ${locale}`, () => {
10
+ it('faqTitle should be defined when faq items exist', async () => {
11
+ const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
12
+ const content = (await loader?.()) as ToolLocaleContent;
13
+
14
+ if (content.faq.length > 0) {
15
+ expect(
16
+ content.faqTitle,
17
+ `Tool "${tool.entry.id}" locale "${locale}" has ${content.faq.length} FAQ items but is missing faqTitle`,
18
+ ).toBeTruthy();
19
+ }
20
+ });
21
+
22
+ it('bibliographyTitle should be defined when bibliography items exist', async () => {
23
+ const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
24
+ const content = (await loader?.()) as ToolLocaleContent;
25
+
26
+ if (content.bibliography.length > 0) {
27
+ expect(
28
+ content.bibliographyTitle,
29
+ `Tool "${tool.entry.id}" locale "${locale}" has ${content.bibliography.length} bibliography items but is missing bibliographyTitle`,
30
+ ).toBeTruthy();
31
+ }
32
+ });
33
+ });
34
+ });
35
+ });
36
+ });
37
+
38
+ it('all tools registered', () => {
39
+ expect(ALL_TOOLS.length).toBe(2);
40
+ });
41
+ });
42
+
@@ -0,0 +1,48 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { readdirSync, readFileSync } from 'fs';
3
+ import { join } from 'path';
4
+
5
+ const EXCLUDED_DIRS = ['node_modules', 'pages', 'layouts'];
6
+
7
+ function findAstroFiles(dir: string): string[] {
8
+ const files: string[] = [];
9
+ const entries = readdirSync(dir, { withFileTypes: true });
10
+
11
+ for (const entry of entries) {
12
+ const fullPath = join(dir, entry.name);
13
+ if (entry.isDirectory() && !EXCLUDED_DIRS.includes(entry.name)) {
14
+ files.push(...findAstroFiles(fullPath));
15
+ } else if (entry.isFile() && entry.name.endsWith('.astro')) {
16
+ files.push(fullPath);
17
+ }
18
+ }
19
+
20
+ return files;
21
+ }
22
+
23
+ function hasH1(content: string): boolean {
24
+ return /<h1[\s>]/i.test(content);
25
+ }
26
+
27
+ const srcDir = join(process.cwd(), 'src');
28
+ const astroFiles = findAstroFiles(srcDir);
29
+
30
+ describe('No H1 in Components', () => {
31
+ if (astroFiles.length === 0) {
32
+ it('no astro components found', () => {
33
+ expect(true).toBe(true);
34
+ });
35
+ }
36
+
37
+ astroFiles.forEach((file) => {
38
+ const relativePath = file.replace(process.cwd(), '');
39
+ it(`${relativePath} should not contain <h1>`, () => {
40
+ const content = readFileSync(file, 'utf-8');
41
+ expect(
42
+ hasH1(content),
43
+ `File "${relativePath}" contains a <h1> element. Use <h2> or lower inside components — h1 belongs to the page layout.`,
44
+ ).toBe(false);
45
+ });
46
+ });
47
+ });
48
+
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+ import type { ToolLocaleContent } from '../types';
4
+
5
+ describe('Schemas Fulfillment Validation', () => {
6
+ ALL_TOOLS.forEach((tool) => {
7
+ describe(`Tool: ${tool.entry.id}`, () => {
8
+ Object.keys(tool.entry.i18n).forEach((locale) => {
9
+ it(`Locale: ${locale} should have faqSchema, appSchema and howToSchema`, async () => {
10
+ const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
11
+ if (!loader) return;
12
+ const content = (await loader()) as ToolLocaleContent;
13
+
14
+ const schemaTypes = content.schemas.map((s: any) => s['@type']);
15
+
16
+ expect(schemaTypes, `Tool "${tool.entry.id}" locale "${locale}" is missing FAQPage schema`).toContain('FAQPage');
17
+ expect(schemaTypes, `Tool "${tool.entry.id}" locale "${locale}" is missing SoftwareApplication schema`).toContain('SoftwareApplication');
18
+ expect(schemaTypes, `Tool "${tool.entry.id}" locale "${locale}" is missing HowTo schema`).toContain('HowTo');
19
+ });
20
+ });
21
+ });
22
+ });
23
+ });
@@ -0,0 +1,23 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+ import type { ToolLocaleContent } from '../types';
4
+
5
+ describe('Slug Language Code Format Validation', () => {
6
+ ALL_TOOLS.forEach((tool) => {
7
+ describe(`Tool: ${tool.entry.id}`, () => {
8
+ it('slug should not end with 2-letter language codes like -ja, -ru, -ko', async () => {
9
+ const locales = Object.keys(tool.entry.i18n);
10
+
11
+ for (const locale of locales) {
12
+ const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
13
+ const content = (await loader?.()) as ToolLocaleContent;
14
+
15
+ expect(
16
+ content.slug,
17
+ `Tool "${tool.entry.id}" locale "${locale}" slug ("${content.slug}") cannot end with a 2-letter language code (e.g., -ja, -ru, -ko).`,
18
+ ).not.toMatch(/-[a-z]{2}$/);
19
+ }
20
+ });
21
+ });
22
+ });
23
+ });
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+ import type { ToolLocaleContent } from '../types';
4
+
5
+ const sharingLocales = ['ja', 'ko', 'zh'];
6
+
7
+ interface ValidateParams {
8
+ toolId: string;
9
+ locale: string;
10
+ content: ToolLocaleContent;
11
+ enSlug: string;
12
+ slugs: Map<string, string>;
13
+ }
14
+
15
+ const validateLocaleSlug = ({
16
+ toolId,
17
+ locale,
18
+ content,
19
+ enSlug,
20
+ slugs,
21
+ }: ValidateParams) => {
22
+ expect(
23
+ content.slug,
24
+ `Tool "${toolId}" locale "${locale}" has an invalid slug ("${content.slug}"). Slugs must be transliterated (only a-z, 0-9, and -).`,
25
+ ).toMatch(/^[a-z0-9-]+$/);
26
+
27
+ if (locale === 'en') {
28
+ return;
29
+ }
30
+
31
+ if (sharingLocales.includes(locale)) {
32
+ expect(
33
+ content.slug,
34
+ `Tool "${toolId}" locale "${locale}" must use the same slug as "en" ("${enSlug}").`,
35
+ ).toBe(enSlug);
36
+ } else {
37
+ expect(
38
+ content.slug,
39
+ `Tool "${toolId}" locale "${locale}" has the same slug as "en" ("${enSlug}"). Cada slug tiene que estar en su propia idioma`,
40
+ ).not.toBe(enSlug);
41
+
42
+ if (slugs.has(content.slug)) {
43
+ const previousLocale = slugs.get(content.slug);
44
+ expect(
45
+ false,
46
+ `Tool "${toolId}" locales "${locale}" and "${previousLocale}" share the same slug ("${content.slug}"). Cada slug tiene que estar en su propia idioma`,
47
+ ).toBe(true);
48
+ }
49
+ slugs.set(content.slug, locale);
50
+ }
51
+ };
52
+
53
+ describe('Slug Localization and Uniqueness Validation', () => {
54
+ ALL_TOOLS.forEach((tool) => {
55
+ describe(`Tool: ${tool.entry.id}`, () => {
56
+ it('every locale should have a unique, translated slug', async () => {
57
+ const slugs = new Map<string, string>();
58
+ const locales = Object.keys(tool.entry.i18n);
59
+
60
+ let enSlug = '';
61
+ if (locales.includes('en')) {
62
+ const enLoader = tool.entry.i18n['en' as keyof typeof tool.entry.i18n];
63
+ const enContent = (await enLoader?.()) as ToolLocaleContent;
64
+ enSlug = enContent.slug;
65
+ }
66
+
67
+ for (const locale of locales) {
68
+ const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
69
+ const content = (await loader?.()) as ToolLocaleContent;
70
+ validateLocaleSlug({
71
+ toolId: tool.entry.id,
72
+ locale,
73
+ content,
74
+ enSlug,
75
+ slugs,
76
+ });
77
+ }
78
+ });
79
+ });
80
+ });
81
+ });
@@ -0,0 +1,55 @@
1
+ import { describe, it } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ function getFiles(dir: string, ext: string[]): string[] {
6
+ const results: string[] = [];
7
+ if (!fs.existsSync(dir)) return results;
8
+ const list = fs.readdirSync(dir);
9
+ for (const file of list) {
10
+ const fullPath = path.join(dir, file);
11
+ const stat = fs.statSync(fullPath);
12
+ if (stat && stat.isDirectory()) {
13
+ results.push(...getFiles(fullPath, ext));
14
+ } else if (ext.some((e) => file.endsWith(e))) {
15
+ results.push(fullPath);
16
+ }
17
+ }
18
+ return results;
19
+ }
20
+
21
+ const SRC_DIR = path.join(process.cwd(), 'src');
22
+
23
+ describe('Project Titles - Separator Validation', () => {
24
+ const files = [
25
+ ...getFiles(path.join(SRC_DIR, 'tool'), ['.ts']),
26
+ ...getFiles(path.join(SRC_DIR, 'category'), ['.ts']),
27
+ ].filter(f => f.includes('i18n'));
28
+
29
+ it.each(files)('Verify that titles in %s do not contain | or -', (filePath) => {
30
+ const content = fs.readFileSync(filePath, 'utf-8');
31
+ const relativePath = path.relative(process.cwd(), filePath);
32
+
33
+ const titlePatterns = [
34
+ /const\s+title\s*=\s*['"]([^'"]+)['"]/g,
35
+ /title\s*:\s*['"]([^'"]+)['"]/g,
36
+ ];
37
+
38
+ const findings: string[] = [];
39
+
40
+ for (const pattern of titlePatterns) {
41
+ let match;
42
+ while ((match = pattern.exec(content)) !== null) {
43
+ const title = match[1];
44
+ if (title.includes('|') || title.includes('-')) {
45
+ findings.push(title);
46
+ }
47
+ }
48
+ }
49
+
50
+ if (findings.length > 0) {
51
+ const list = findings.map((f) => ` - "${f}"`).join('\n');
52
+ throw new Error(`Forbidden separators (| or -) found in titles in ${relativePath}:\n${list}`);
53
+ }
54
+ });
55
+ });