@jjlmoya/utils-drones 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 (74) hide show
  1. package/package.json +64 -63
  2. package/src/category/i18n/de.ts +106 -0
  3. package/src/category/i18n/id.ts +106 -0
  4. package/src/category/i18n/it.ts +106 -0
  5. package/src/category/i18n/ja.ts +106 -0
  6. package/src/category/i18n/ko.ts +106 -0
  7. package/src/category/i18n/nl.ts +106 -0
  8. package/src/category/i18n/pl.ts +106 -0
  9. package/src/category/i18n/pt.ts +106 -0
  10. package/src/category/i18n/ru.ts +106 -0
  11. package/src/category/i18n/sv.ts +106 -0
  12. package/src/category/i18n/tr.ts +106 -0
  13. package/src/category/i18n/zh.ts +106 -0
  14. package/src/category/index.ts +12 -0
  15. package/src/components/SecurityWarning.astro +140 -0
  16. package/src/tests/faq_count.test.ts +1 -1
  17. package/src/tests/i18n_coverage.test.ts +36 -0
  18. package/src/tests/locale_completeness.test.ts +2 -2
  19. package/src/tests/schemas_fulfillment.test.ts +23 -0
  20. package/src/tests/slug_uniqueness.test.ts +81 -0
  21. package/src/tests/title_quality.test.ts +55 -0
  22. package/src/tool/antenna-length-calculator/component.astro +70 -13
  23. package/src/tool/antenna-length-calculator/i18n/de.ts +276 -0
  24. package/src/tool/antenna-length-calculator/i18n/en.ts +76 -38
  25. package/src/tool/antenna-length-calculator/i18n/es.ts +76 -38
  26. package/src/tool/antenna-length-calculator/i18n/fr.ts +76 -38
  27. package/src/tool/antenna-length-calculator/i18n/id.ts +242 -0
  28. package/src/tool/antenna-length-calculator/i18n/it.ts +276 -0
  29. package/src/tool/antenna-length-calculator/i18n/ja.ts +276 -0
  30. package/src/tool/antenna-length-calculator/i18n/ko.ts +276 -0
  31. package/src/tool/antenna-length-calculator/i18n/nl.ts +276 -0
  32. package/src/tool/antenna-length-calculator/i18n/pl.ts +276 -0
  33. package/src/tool/antenna-length-calculator/i18n/pt.ts +276 -0
  34. package/src/tool/antenna-length-calculator/i18n/ru.ts +276 -0
  35. package/src/tool/antenna-length-calculator/i18n/sv.ts +276 -0
  36. package/src/tool/antenna-length-calculator/i18n/tr.ts +276 -0
  37. package/src/tool/antenna-length-calculator/i18n/zh.ts +276 -0
  38. package/src/tool/antenna-length-calculator/index.ts +12 -0
  39. package/src/tool/drone-flight-time/component.astro +320 -319
  40. package/src/tool/drone-flight-time/components/FlightDashboard.astro +6 -0
  41. package/src/tool/drone-flight-time/i18n/de.ts +238 -0
  42. package/src/tool/drone-flight-time/i18n/en.ts +76 -38
  43. package/src/tool/drone-flight-time/i18n/es.ts +76 -38
  44. package/src/tool/drone-flight-time/i18n/fr.ts +76 -38
  45. package/src/tool/drone-flight-time/i18n/id.ts +238 -0
  46. package/src/tool/drone-flight-time/i18n/it.ts +238 -0
  47. package/src/tool/drone-flight-time/i18n/ja.ts +239 -0
  48. package/src/tool/drone-flight-time/i18n/ko.ts +239 -0
  49. package/src/tool/drone-flight-time/i18n/nl.ts +239 -0
  50. package/src/tool/drone-flight-time/i18n/pl.ts +239 -0
  51. package/src/tool/drone-flight-time/i18n/pt.ts +239 -0
  52. package/src/tool/drone-flight-time/i18n/ru.ts +239 -0
  53. package/src/tool/drone-flight-time/i18n/sv.ts +239 -0
  54. package/src/tool/drone-flight-time/i18n/tr.ts +239 -0
  55. package/src/tool/drone-flight-time/i18n/zh.ts +239 -0
  56. package/src/tool/drone-flight-time/index.ts +12 -0
  57. package/src/tool/gps-coordinates-converter/component.astro +473 -549
  58. package/src/tool/gps-coordinates-converter/components/GpsHistory.astro +6 -1
  59. package/src/tool/gps-coordinates-converter/i18n/de.ts +222 -0
  60. package/src/tool/gps-coordinates-converter/i18n/en.ts +76 -20
  61. package/src/tool/gps-coordinates-converter/i18n/es.ts +76 -20
  62. package/src/tool/gps-coordinates-converter/i18n/fr.ts +75 -2
  63. package/src/tool/gps-coordinates-converter/i18n/id.ts +222 -0
  64. package/src/tool/gps-coordinates-converter/i18n/it.ts +222 -0
  65. package/src/tool/gps-coordinates-converter/i18n/ja.ts +222 -0
  66. package/src/tool/gps-coordinates-converter/i18n/ko.ts +222 -0
  67. package/src/tool/gps-coordinates-converter/i18n/nl.ts +222 -0
  68. package/src/tool/gps-coordinates-converter/i18n/pl.ts +222 -0
  69. package/src/tool/gps-coordinates-converter/i18n/pt.ts +222 -0
  70. package/src/tool/gps-coordinates-converter/i18n/ru.ts +222 -0
  71. package/src/tool/gps-coordinates-converter/i18n/sv.ts +222 -0
  72. package/src/tool/gps-coordinates-converter/i18n/tr.ts +222 -0
  73. package/src/tool/gps-coordinates-converter/i18n/zh.ts +222 -0
  74. package/src/tool/gps-coordinates-converter/index.ts +12 -0
@@ -0,0 +1,106 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'drones',
5
+ title: 'Verktyg och Kalkylatorer för Drönare och Radioamatörer',
6
+ description: 'Optimera dina flygningar och din kommunikation med kostnadsfria onlineverktyg. Flygtidskalkylatorer, batteritidsestimering, GPS-koordinatkonverterare och antennlängder för radiofrekvens.',
7
+ seo: [
8
+ {
9
+ type: 'title',
10
+ text: 'RF-teknik och Precisionsflygning: Verktyg för Piloter och Radiooperatörer',
11
+ level: 2,
12
+ },
13
+ {
14
+ type: 'paragraph',
15
+ html: 'Amatörradio och drönarflygning år 2026 är områden där skicklighet möter radiofrekvensteknik (RF). I den här sektionen erbjuder vi en uppsättning <strong>kostnadsfria onlineverktyg</strong> designade för FPV-entusiaster, kommersiella RPAS-piloter och radiooperatörer. Att förstå begränsningarna i din mobila utrustning och lagarna för vågutbredning är skillnaden mellan ett lyckat uppdrag och ett kostsamt tekniskt fel.',
16
+ },
17
+ {
18
+ type: 'paragraph',
19
+ html: 'Från planering av flygautonomi till konstruktion av anpassade antenner och geoprecis navigering, ger våra verktyg dig den datatrygghet som krävs för att ta dina projekt upp i luften eller ut i etern.',
20
+ },
21
+ {
22
+ type: 'title',
23
+ text: 'Flygplanering: Tid och Batteriautonomi (mAh / Amps)',
24
+ level: 2,
25
+ },
26
+ {
27
+ type: 'paragraph',
28
+ html: 'Att känna till din drönares faktiska operativa fönster är avgörande för säkerheten. Vår <strong>flygtidskalkylator</strong> uppskattar den totala autonomin baserat på batteriets milliamperetimmar (mAh) och motorernas genomsnittliga förbrukning. Låt inte telemetrin överraska dig med kritisk spänning långt hemifrån.',
29
+ },
30
+ {
31
+ type: 'title',
32
+ text: 'Antennteknik: Våglängd och Radiofrekvens',
33
+ level: 2,
34
+ },
35
+ {
36
+ type: 'paragraph',
37
+ html: 'Sändningseffektivitet beror på resonans. <strong>Antennkalkylatorn</strong> fastställer den exakta fysiska längden som krävs för dipoler, 1/2- och 1/4-vågsantenner baserat på önskad driftsfrekvens (VHF, UHF, ISM-band). Maximera räckvidden och reducera SWR (ståendevågförhållande) i dina kommunikationssystem.',
38
+ },
39
+ {
40
+ type: 'title',
41
+ text: 'Geoprecis Navigering: GPS-koordinatkonverterare',
42
+ level: 2,
43
+ },
44
+ {
45
+ type: 'paragraph',
46
+ html: 'Oumbärlig för sökuppdrag, kartläggning eller radiolokaliseing. <strong>Koordinatkonverteraren</strong> översätter format från decimaler till DMS (grader, minuter, sekunder) i båda riktningarna, och erbjuder en kartvy för att bekräfta exaktheten i din intressepunkt före start.',
47
+ },
48
+ {
49
+ type: 'list',
50
+ items: [
51
+ '<strong>RPAS-säkerhet:</strong> Undvik nödlandningar på grund av oförutsedd tömning av battericykler.',
52
+ '<strong>RF-optimering:</strong> Bygg dina egna högpresterande antenner med exakta fysiska mått baserade på ljushastigheten i ledaren.',
53
+ '<strong>Georefererade Uppdrag:</strong> Arbeta sömlöst med olika kartstandarder och internationella koordinater.',
54
+ '<strong>Integritet i Luften:</strong> Dina flygplaner och koordinater processas lokalt; vi laddar inte upp känslig data till tredjepartsmoln.',
55
+ ],
56
+ },
57
+ {
58
+ type: 'title',
59
+ text: 'Aeronautisk Reglering och Operatörscertifiering',
60
+ level: 2,
61
+ },
62
+ {
63
+ type: 'paragraph',
64
+ html: 'Att flyga drönare idag kräver förståelse för reglering. I EU kräver EASA och lokala myndigheter (som Transportstyrelsen i Sverige) certifiering för yrkesmässig flygning. I USA kräver FAA en Part 107-licens. Varje jurisdiktion har höjdgränser, restriktionszoner (NOTAM), försäkringskrav och register.',
65
+ },
66
+ {
67
+ type: 'paragraph',
68
+ html: 'Våra planeringsverktyg hjälper dig att verifiera autonomi, synvinklar för kartläggning och flygtider. Genom att kombinera detta med data om restriktionszoner kan du planera uppdrag som är både tekniskt optimala OCH lagliga. Att flyga utanför regleringen är kostsamt (böter på över 1000 € i Europa), varför noggrann planering är obligatorisk.',
69
+ },
70
+ {
71
+ type: 'title',
72
+ text: 'Kommersiella Applikationer: Fotogrammetri och Inspektion',
73
+ level: 2,
74
+ },
75
+ {
76
+ type: 'paragraph',
77
+ html: 'Drönare har revolutionerat industriell inspektion och topografi. En takinspektör kan använda en drönare för att identifiera skador utan att riskera sitt liv. En jordbruksingenjör använder drönare för att kartlägga vattenstress i grödor via värmekameror. Ett byggföretag använder fotogrammetri för att dokumentera framsteg i 3D.',
78
+ },
79
+ {
80
+ type: 'paragraph',
81
+ html: 'Varje applikation kräver olika autonomi, lastkapacitet och räckvidd. Våra kalkylatorer låter dig modellera: lastvikt, antal batterier, flygcykler per dag. Därifrån beräknar du ROI: om uppdraget motiverar investeringen. Våra verktyg hjälper dig med den kalkylen.',
82
+ },
83
+ {
84
+ type: 'title',
85
+ text: 'Radioamatörcommunity: HF-, VHF-, UHF-band och Satelliter',
86
+ level: 2,
87
+ },
88
+ {
89
+ type: 'paragraph',
90
+ html: 'Radioamatörer (HAM radio) är ett globalt community med över 2 miljoner operatörer som kommunicerar utan internet. De använder frekvensband tilldelade av internationella organ (IARU). VHF (144-146 MHz på 2-metersbandet) är det mest tillgängliga bandet för att börja, med typisk räckvidd på 20-100 km beroende på antenn.',
91
+ },
92
+ {
93
+ type: 'paragraph',
94
+ html: 'Att förstå hur man beräknar antenner för sitt band är väsentligt. Vår generator beräknar detta: du anger frekvensen, specificerar om du vill ha 1/4, 1/2 eller hel våglängd, och får det exakta måttet. Bygg din antenn, trimma in den och anslut till världen med bara en radio och en hembyggd antenn. Det är tillgänglig och enkel teknik som överlever krig, katastrofer och strömavbrott.',
95
+ },
96
+ {
97
+ type: 'title',
98
+ text: 'Framtiden för personlig luftmobilitet 2026',
99
+ level: 2,
100
+ },
101
+ {
102
+ type: 'paragraph',
103
+ html: 'År 2026 är integreringen av drönare i det delade luftrummet en reglerad verklighet. Förmågan hos amatörer att operera under professionella standarder för underhåll och teknisk planering är nyckeln till samexistens. Dessa verktyg är en del av engagemanget för excellens och säkerhet som varje pilot och radioamatör bör upprätthålla.',
104
+ },
105
+ ],
106
+ };
@@ -0,0 +1,106 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'drones',
5
+ title: 'Drone ve Radyo Amatörleri İçin Araçlar ve Hesaplayıcılar',
6
+ description: 'Ücretsiz çevrimiçi araçlarla uçuşlarınızı ve iletişiminizi optimize edin. Uçuş süresi hesaplayıcıları, pil ömrü tahmini, GPS koordinat dönüştürücüleri ve radyo frekansı için anten uzunlukları.',
7
+ seo: [
8
+ {
9
+ type: 'title',
10
+ text: 'RF Mühendisliği ve Hassas Uçuş: Pilotlar ve Radyo Operatörleri İçin Araçlar',
11
+ level: 2,
12
+ },
13
+ {
14
+ type: 'paragraph',
15
+ html: '2026 yılında radyo amatörlüğü ve drone uçuşu, becerinin radyo frekansı (RF) mühendisliği ile birleştiği alanlardır. Bu bölümde FPV tutkunları, ticari RPAS pilotları ve radyo bandı operatörleri için tasarlanmış bir dizi <strong>ücretsiz çevrimiçi araç</strong> sunuyoruz. Mobil ekipmanınızın sınırlarını ve dalga yayılım yasalarını anlamak, başarılı bir görev ile maliyetli bir teknik arıza arasındaki farktır.',
16
+ },
17
+ {
18
+ type: 'paragraph',
19
+ html: 'Uçuş özerkliği planlamasından özel anten yapımına ve hassas coğrafi navigasyona kadar, araçlarımız projelerinizi havaya veya dalgalara taşımanız için size veri güveni sağlar.',
20
+ },
21
+ {
22
+ type: 'title',
23
+ text: 'Uçuş Planlaması: Zaman ve Pil Özerkliği (mAh / Amps)',
24
+ level: 2,
25
+ },
26
+ {
27
+ type: 'paragraph',
28
+ html: 'Drone\'unuzun gerçek çalışma penceresini bilmek güvenlik için hayati önem taşır. <strong>Uçuş süresi hesaplayıcımız</strong>, pilin miliamper-saatine (mAh) ve motorların ortalama tüketimine dayanarak toplam özerkliği tahmin eder. Telemetrinin, evden uzaktayken kritik voltajla sizi şaşırtmasına izin vermeyin.',
29
+ },
30
+ {
31
+ type: 'title',
32
+ text: 'Anten Mühendisliği: Dalga Boyu ve Radyo Frekansı',
33
+ level: 2,
34
+ },
35
+ {
36
+ type: 'paragraph',
37
+ html: 'İletim verimliliği rezonansa bağlıdır. <strong>Anten hesaplayıcı</strong>, istenen çalışma frekansına (VHF, UHF, ISM Bantları) göre dipoller, 1/2 ve 1/4 dalga kamçı antenler için gereken tam fiziksel uzunluğu belirler. İletişim sistemlerinizin menzilini maksimize edin ve SWR\'yi (Duran Dalga Oranı) azaltın.',
38
+ },
39
+ {
40
+ type: 'title',
41
+ text: 'Hassas Navigasyon: GPS Koordinat Dönüştürücü',
42
+ level: 2,
43
+ },
44
+ {
45
+ type: 'paragraph',
46
+ html: 'Arama görevleri, haritalama veya radyo yön bulma için vazgeçilmezdir. <strong>Koordinat dönüştürücü</strong>, ondalık formatları DMS (Derece, Dakika, Saniye) formuna iki yönlü olarak çevirir ve kalkıştan önce ilgi noktanızın doğruluğunu onaylamak için harita görüntüleme sunar.',
47
+ },
48
+ {
49
+ type: 'list',
50
+ items: [
51
+ '<strong>RPAS Güvenliği:</strong> Öngörülemeyen pil döngüsü tükenmesi nedeniyle acil inişlerden kaçının.',
52
+ '<strong>RF Optimizasyonu:</strong> İletkendeki ışık hızına dayalı hassas fiziksel ölçülerle kendi yüksek performanslı antenlerinizi oluşturun.',
53
+ '<strong>Coğrafi Referanslı Görevler:</strong> Farklı harita standartları ve uluslararası koordinatlarla tam akıcılıkla çalışın.',
54
+ '<strong>Havadaki Gizlilik:</strong> Uçuş planlarınız ve koordinatlarınız yerel olarak işlenir; hassas verileri üçüncü taraf bulutlarına yüklemiyoruz.',
55
+ ],
56
+ },
57
+ {
58
+ type: 'title',
59
+ text: 'Havacılık Düzenlemeleri ve Operatör Sertifikasyonu',
60
+ level: 2,
61
+ },
62
+ {
63
+ type: 'paragraph',
64
+ html: 'Drone uçurmak bugün düzenlemelerin anlaşılmasını gerektirir. Türkiye\'de SHGM (Sivil Havacılık Genel Müdürlüğü) profesyonel uçuşlar için sertifikasyon gerektirir. Her yargı bölgesinin irtifa sınırları, kısıtlı bölgeleri (NOTAM), sigorta gereksinimleri ve kayıt sistemleri vardır.',
65
+ },
66
+ {
67
+ type: 'paragraph',
68
+ html: 'Planlama araçlarımız özerklik, haritalama için görüş açıları ve uçuş sürelerini doğrulamanıza yardımcı olur. Bunu kısıtlı bölge verileriyle birleştirdiğinizde, teknik olarak optimize VE yasal görevler planlayabilirsiniz. Düzenlemelere uymadan uçmak maliyetlidir, bu nedenle titiz planlama zorunludur.',
69
+ },
70
+ {
71
+ type: 'title',
72
+ text: 'Ticari Uygulamalar: Fotogrametrik Haritalama ve Denetim',
73
+ level: 2,
74
+ },
75
+ {
76
+ type: 'paragraph',
77
+ html: 'Drone\'lar endüstriyel denetim ve topoğrafyada devrim yarattı. Bir çatı denetçisi, hayatını riske atmadan hasarı belirlemek için drone kullanabilir. Bir tarım mühendisi, termal kameralar aracılığıyla mahsullerdeki su stresini haritalamak için drone kullanır. Bir inşaat şirketi, inşaat ilerleme durumlarını 3D olarak belgelemek için drone fotogrametrisi kullanır.',
78
+ },
79
+ {
80
+ type: 'paragraph',
81
+ html: 'Her uygulama farklı özerklik, yükleme ve çalışma aralıkları gerektirir. Özellik hesaplayıcılarımız; yük ağırlığı, pil sayısı, günlük uçuş döngüsü gibi verileri modellemenize olanak tanır. Buradan ROI hesaplarsınız: yatırımınız görevi haklı çıkarıyor mu? Araçlarımız bu hesabı yapmanıza yardımcı olur.',
82
+ },
83
+ {
84
+ type: 'title',
85
+ text: 'Radyo Amatör Topluluğu: HF, VHF, UHF Bantları ve Uydular',
86
+ level: 2,
87
+ },
88
+ {
89
+ type: 'paragraph',
90
+ html: 'Radyo amatörleri (HAM radio), internet olmadan iletişim kuran 2 milyondan fazla operatörden oluşan küresel bir topluluktur. Uluslararası kuruluşlar (IARU) tarafından atanan frekans bantlarını kullanırlar. VHF (2 metre bandında 144-146 MHz), başlangıç için en erişilebilir banttır.',
91
+ },
92
+ {
93
+ type: 'paragraph',
94
+ html: 'Bandınız için antenlerin nasıl hesaplanacağını anlamak esastır. Jeneratörümüz bunu hesaplar: sadece frekansı girin, 1/4, 1/2 veya tam dalga boyu isteyip istemediğinizi belirtin ve tam boyutu elde edin. Anteninizi oluşturun, ayarlayın ve sadece bir telsiz ve ev yapımı bir antenle dünya ile bağlantı kurun. Savaşlar, afetler ve elektrik kesintilerinden sağ kurtulan mütevazı bir teknolojidir.',
95
+ },
96
+ {
97
+ type: 'title',
98
+ text: 'Kişisel hava hareketliliğinin geleceği 2026',
99
+ level: 2,
100
+ },
101
+ {
102
+ type: 'paragraph',
103
+ html: '2026 yılında, drone\'ların paylaşılan hava sahasına entegrasyonu düzenlenmiş bir gerçektir. Amatörlerin profesyonel bakım ve teknik planlama standartları altında çalışma yeteneği, birlikte yaşamanın anahtarıdır. Bu araçlar, her pilotun ve radyo amatörünün sürdürmesi gereken mükemmellik ve güvenlik taahhüdünün bir parçasıdır.',
104
+ },
105
+ ],
106
+ };
@@ -0,0 +1,106 @@
1
+ import type { CategoryLocaleContent } from '../../types';
2
+
3
+ export const content: CategoryLocaleContent = {
4
+ slug: 'drones',
5
+ title: '无人机与无线电爱好者工具及计算器',
6
+ description: '通过免费在线工具优化您的飞行与通信。提供飞行时间计算、电池续航估算、GPS 坐标转换及射频天线长度计算。',
7
+ seo: [
8
+ {
9
+ type: 'title',
10
+ text: '射频工程与精准飞行:为飞行员与无线电操作员打造的工具',
11
+ level: 2,
12
+ },
13
+ {
14
+ type: 'paragraph',
15
+ html: '2026 年的业余无线电与无人机飞行是技术与射频 (RF) 工程交汇的领域。在此板块,我们为 FPV 爱好者、商用 RPAS 飞行员以及无线电频段操作员提供了一套<strong>免费在线工具</strong>。了解移动设备的限制和电波传播规律,是任务成功与代价昂贵的门技术故障之间的分野。',
16
+ },
17
+ {
18
+ type: 'paragraph',
19
+ html: '从飞行续航规划到定制天线制作,再到高精度地理导航,我们的工具为您提供数据支持,让飞行或电波发射更具信心。',
20
+ },
21
+ {
22
+ type: 'title',
23
+ text: '飞行规划:飞行时间与电池续航 (mAh / 安培)',
24
+ level: 2,
25
+ },
26
+ {
27
+ type: 'paragraph',
28
+ html: '了解无人机的实际运行窗口对安全至关重要。我们的<strong>飞行时间计算器</strong>根据电池毫安时 (mAh) 和电机平均功耗估算总续航时间。不要让遥测数据在远离家园的地方因为临界电压而由于给您带来意外。',
29
+ },
30
+ {
31
+ type: 'title',
32
+ text: '天线工程:波长与无线电频率',
33
+ level: 2,
34
+ },
35
+ {
36
+ type: 'paragraph',
37
+ html: '传输效率取决于谐振。<strong>天线计算器</strong>根据所需的运行频率(VHF、UHF、ISM 频段)确定偶极天线、1/2 波长或 1/4 波长鞭状天线所需的精确物理长度。最大化通信距离并降低驻波比 (SWR)。',
38
+ },
39
+ {
40
+ type: 'title',
41
+ text: '精准导航:GPS 坐标转换器',
42
+ level: 2,
43
+ },
44
+ {
45
+ type: 'paragraph',
46
+ html: '搜索任务、测绘或无线电定位不可或缺。<strong>坐标转换器</strong>支持十进制与 DMS(度分秒)格式的双向相互转换,并提供地图可视化功能,以便在起飞前确认目标点的准确性。',
47
+ },
48
+ {
49
+ type: 'list',
50
+ items: [
51
+ '<strong>RPAS 安全:</strong> 避免因电池循环意外耗尽而导致紧急迫降。',
52
+ '<strong>射频优化:</strong> 基于导体中的光速,利用精确的物理尺寸制作自己的高性能天线。',
53
+ '<strong>地理测绘任务:</strong> 流畅处理各种地图标准和国际坐标。',
54
+ '<strong>空中隐私:</strong> 您的飞行计划和坐标均在本地处理;我们不会将敏感数据上传到第三方云端。',
55
+ ],
56
+ },
57
+ {
58
+ type: 'title',
59
+ text: '航空法规与操作员认证',
60
+ level: 2,
61
+ },
62
+ {
63
+ type: 'paragraph',
64
+ html: '今日飞行无人机需要理解相关法规。各国的民航局(如中国 CAAC、美国 FAA、欧洲 EASA)对专业飞行均有认证要求。每个司法管辖区都有高度限制、限飞区 (NOTAM)、保险要求及登记制度。',
65
+ },
66
+ {
67
+ type: 'paragraph',
68
+ html: '我们的规划工具帮助您验证续航能力、测绘视角及飞行时间。将这些数据与限飞区信息相结合,您可以规划技术最优且合规的任务。违规飞行代价高昂,因此严谨的规划是必须的。',
69
+ },
70
+ {
71
+ type: 'title',
72
+ text: '商业应用:摄影测量测绘与巡检',
73
+ level: 2,
74
+ },
75
+ {
76
+ type: 'paragraph',
77
+ html: '无人机彻底改变了工业巡检和地形测量。屋顶检查员可以使用无人机识别损坏而无需冒生命危险。农业工程师使用热成像相机测绘农作物的缺水压力。建筑公司使用无人机摄影测量技术以 3D 方式记录工程进度。',
78
+ },
79
+ {
80
+ type: 'paragraph',
81
+ html: '每种应用都需要不同的续航、载荷及作业范围。我们的计算器允许您模拟负载重量、电池数量、每日飞行循环,从而计算投资回报率 (ROI)。这些工具能帮您做出更明智的决策。',
82
+ },
83
+ {
84
+ type: 'title',
85
+ text: '业余无线电社区:HF、VHF、UHF 频段与卫星',
86
+ level: 2,
87
+ },
88
+ {
89
+ type: 'paragraph',
90
+ html: '业余无线电 (HAM radio) 是一个由全球 200 多万名在没有互联网的情况下进行通信的操作员组成的社区。他们使用国际组织 (IARU) 分配的频段。VHF(2 米频段)是入门最简单的频段。',
91
+ },
92
+ {
93
+ type: 'paragraph',
94
+ html: '理解如何为您的频段计算天线至关重要。我们的工具只需输入频率并指定波长类型(1/4、1/2 或全波长),即可获得准确尺寸。自制天线、调试,仅需一台电台即可与世界连接。',
95
+ },
96
+ {
97
+ type: 'title',
98
+ text: '2026 年个人空中出行的未来',
99
+ level: 2,
100
+ },
101
+ {
102
+ type: 'paragraph',
103
+ html: '2026 年,无人机融入共享空域已成为受监管的现实。业余爱好者按照专业维护标准和技术规划进行操作的能力是共和的关键。这些工具体现了每位飞行员和操作员应保持的卓越与安全承诺。',
104
+ },
105
+ ],
106
+ };
@@ -10,6 +10,18 @@ export const dronesCategory: DronesCategoryEntry = {
10
10
  es: () => import('./i18n/es').then((m) => m.content),
11
11
  en: () => import('./i18n/en').then((m) => m.content),
12
12
  fr: () => import('./i18n/fr').then((m) => m.content),
13
+ de: () => import('./i18n/de').then((m) => m.content),
14
+ it: () => import('./i18n/it').then((m) => m.content),
15
+ pt: () => import('./i18n/pt').then((m) => m.content),
16
+ nl: () => import('./i18n/nl').then((m) => m.content),
17
+ pl: () => import('./i18n/pl').then((m) => m.content),
18
+ ru: () => import('./i18n/ru').then((m) => m.content),
19
+ ja: () => import('./i18n/ja').then((m) => m.content),
20
+ ko: () => import('./i18n/ko').then((m) => m.content),
21
+ zh: () => import('./i18n/zh').then((m) => m.content),
22
+ tr: () => import('./i18n/tr').then((m) => m.content),
23
+ sv: () => import('./i18n/sv').then((m) => m.content),
24
+ id: () => import('./i18n/id').then((m) => m.content),
13
25
  },
14
26
  };
15
27
 
@@ -0,0 +1,140 @@
1
+ ---
2
+ import { Icon } from "astro-icon/components";
3
+
4
+ interface Props {
5
+ title: string;
6
+ message: string;
7
+ }
8
+
9
+ const { title, message } = Astro.props;
10
+ ---
11
+
12
+ <div class="security-warning">
13
+ <div class="warning-icon-wrapper">
14
+ <Icon name="mdi:alert-decagram" class="warning-icon" />
15
+ </div>
16
+ <div class="warning-content">
17
+ <p>
18
+ <strong class="warning-title">{title}:</strong>
19
+ <span class="warning-message">{message}</span>
20
+ </p>
21
+ </div>
22
+ <div class="warning-accent"></div>
23
+ </div>
24
+
25
+ <style>
26
+ .security-warning {
27
+ position: relative;
28
+ display: flex;
29
+ align-items: center;
30
+ gap: 1.5rem;
31
+ padding: 1.5rem 2rem;
32
+ background: rgba(239, 68, 68, 0.05);
33
+ backdrop-filter: blur(10px);
34
+ border: 1px solid rgba(239, 68, 68, 0.2);
35
+ border-radius: 24px;
36
+ overflow: hidden;
37
+ transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
38
+ margin: 2rem 0;
39
+ }
40
+
41
+ :global(.theme-dark) .security-warning {
42
+ background: rgba(239, 68, 68, 0.1);
43
+ border-color: rgba(239, 68, 68, 0.3);
44
+ }
45
+
46
+ .security-warning:hover {
47
+ transform: translateY(-4px) scale(1.01);
48
+ box-shadow: 0 20px 40px rgba(239, 68, 68, 0.15);
49
+ border-color: rgba(239, 68, 68, 0.4);
50
+ }
51
+
52
+ .warning-icon-wrapper {
53
+ flex-shrink: 0;
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ width: 48px;
58
+ height: 48px;
59
+ background: linear-gradient(135deg, #ef4444, #b91c1c);
60
+ border-radius: 16px;
61
+ box-shadow: 0 8px 16px rgba(239, 68, 68, 0.3);
62
+ animation: pulse-ring 3s cubic-bezier(0.25, 0.8, 0.25, 1) infinite;
63
+ }
64
+
65
+ .warning-icon {
66
+ --icon-color: #fff;
67
+
68
+ width: 28px;
69
+ height: 28px;
70
+ color: var(--icon-color);
71
+ }
72
+
73
+ .warning-content {
74
+ flex: 1;
75
+ }
76
+
77
+ .warning-content p {
78
+ margin: 0;
79
+ color: var(--antenna-text);
80
+ font-size: 0.95rem;
81
+ line-height: 1.6;
82
+ }
83
+
84
+ .warning-title {
85
+ --warning-color: #ef4444;
86
+
87
+ color: var(--warning-color);
88
+ font-weight: 800;
89
+ text-transform: uppercase;
90
+ letter-spacing: 0.05em;
91
+ font-size: 0.85rem;
92
+ }
93
+
94
+ .warning-message {
95
+ font-weight: 500;
96
+ opacity: 0.9;
97
+ }
98
+
99
+ .warning-accent {
100
+ position: absolute;
101
+ top: 0;
102
+ left: 0;
103
+ height: 100%;
104
+ width: 4px;
105
+ background: #ef4444;
106
+ }
107
+
108
+ @keyframes pulse-ring {
109
+ 0% {
110
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4);
111
+ }
112
+
113
+ 70% {
114
+ box-shadow: 0 0 0 10px rgba(239, 68, 68, 0);
115
+ }
116
+
117
+ 100% {
118
+ box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
119
+ }
120
+ }
121
+
122
+ @media (max-width: 600px) {
123
+ .security-warning {
124
+ flex-direction: column;
125
+ align-items: flex-start;
126
+ gap: 1rem;
127
+ padding: 1.5rem;
128
+ }
129
+
130
+ .warning-icon-wrapper {
131
+ width: 40px;
132
+ height: 40px;
133
+ }
134
+
135
+ .warning-icon {
136
+ width: 24px;
137
+ height: 24px;
138
+ }
139
+ }
140
+ </style>
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import type * as DATA from '../data';
3
3
 
4
- const TOOLS: typeof DATA.dronesCategory[] = [];
4
+ const TOOLS: typeof DATA.audiovisualCategory[] = [];
5
5
 
6
6
  describe('FAQ Content Validation', () => {
7
7
  TOOLS.forEach((entry) => {
@@ -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
+ });
@@ -35,8 +35,8 @@ describe('Locale Completeness Validation', () => {
35
35
  });
36
36
  });
37
37
 
38
- it('all tools are registered', () => {
39
- expect(ALL_TOOLS.length).toBeGreaterThan(0);
38
+ it('all 3 tools registered', () => {
39
+ expect(ALL_TOOLS.length).toBe(3);
40
40
  });
41
41
  });
42
42
 
@@ -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,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
+ });