@jjlmoya/utils-home 1.30.0 → 1.32.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 (56) hide show
  1. package/package.json +2 -1
  2. package/src/entries.ts +7 -1
  3. package/src/index.ts +1 -0
  4. package/src/tests/locale_completeness.test.ts +2 -2
  5. package/src/tests/tool_validation.test.ts +2 -2
  6. package/src/tool/lightingCalculator/bibliography.astro +36 -0
  7. package/src/tool/lightingCalculator/bibliography.ts +10 -0
  8. package/src/tool/lightingCalculator/component.astro +308 -0
  9. package/src/tool/lightingCalculator/draw.ts +247 -0
  10. package/src/tool/lightingCalculator/entry.ts +29 -0
  11. package/src/tool/lightingCalculator/how-many-lights-per-room.css +493 -0
  12. package/src/tool/lightingCalculator/i18n/de.ts +213 -0
  13. package/src/tool/lightingCalculator/i18n/en.ts +213 -0
  14. package/src/tool/lightingCalculator/i18n/es.ts +213 -0
  15. package/src/tool/lightingCalculator/i18n/fr.ts +213 -0
  16. package/src/tool/lightingCalculator/i18n/id.ts +213 -0
  17. package/src/tool/lightingCalculator/i18n/it.ts +213 -0
  18. package/src/tool/lightingCalculator/i18n/ja.ts +213 -0
  19. package/src/tool/lightingCalculator/i18n/ko.ts +213 -0
  20. package/src/tool/lightingCalculator/i18n/nl.ts +213 -0
  21. package/src/tool/lightingCalculator/i18n/pl.ts +213 -0
  22. package/src/tool/lightingCalculator/i18n/pt.ts +213 -0
  23. package/src/tool/lightingCalculator/i18n/ru.ts +213 -0
  24. package/src/tool/lightingCalculator/i18n/sv.ts +213 -0
  25. package/src/tool/lightingCalculator/i18n/tr.ts +213 -0
  26. package/src/tool/lightingCalculator/i18n/zh.ts +213 -0
  27. package/src/tool/lightingCalculator/index.ts +9 -0
  28. package/src/tool/lightingCalculator/logic.ts +119 -0
  29. package/src/tool/lightingCalculator/seo.astro +15 -0
  30. package/src/tool/lightingCalculator/state.ts +113 -0
  31. package/src/tool/lightingCalculator/ui.ts +48 -0
  32. package/src/tool/tileLayoutCalculator/bibliography.astro +14 -0
  33. package/src/tool/tileLayoutCalculator/bibliography.ts +10 -0
  34. package/src/tool/tileLayoutCalculator/component.astro +415 -0
  35. package/src/tool/tileLayoutCalculator/entry.ts +29 -0
  36. package/src/tool/tileLayoutCalculator/i18n/de.ts +208 -0
  37. package/src/tool/tileLayoutCalculator/i18n/en.ts +208 -0
  38. package/src/tool/tileLayoutCalculator/i18n/es.ts +208 -0
  39. package/src/tool/tileLayoutCalculator/i18n/fr.ts +208 -0
  40. package/src/tool/tileLayoutCalculator/i18n/id.ts +208 -0
  41. package/src/tool/tileLayoutCalculator/i18n/it.ts +208 -0
  42. package/src/tool/tileLayoutCalculator/i18n/ja.ts +208 -0
  43. package/src/tool/tileLayoutCalculator/i18n/ko.ts +208 -0
  44. package/src/tool/tileLayoutCalculator/i18n/nl.ts +208 -0
  45. package/src/tool/tileLayoutCalculator/i18n/pl.ts +208 -0
  46. package/src/tool/tileLayoutCalculator/i18n/pt.ts +208 -0
  47. package/src/tool/tileLayoutCalculator/i18n/ru.ts +208 -0
  48. package/src/tool/tileLayoutCalculator/i18n/sv.ts +208 -0
  49. package/src/tool/tileLayoutCalculator/i18n/tr.ts +208 -0
  50. package/src/tool/tileLayoutCalculator/i18n/zh.ts +208 -0
  51. package/src/tool/tileLayoutCalculator/index.ts +9 -0
  52. package/src/tool/tileLayoutCalculator/logic.ts +55 -0
  53. package/src/tool/tileLayoutCalculator/seo.astro +15 -0
  54. package/src/tool/tileLayoutCalculator/tile-layout-calculator.css +404 -0
  55. package/src/tool/tileLayoutCalculator/ui.ts +37 -0
  56. package/src/tools.ts +4 -0
@@ -0,0 +1,213 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { LightingCalculatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
5
+
6
+ const slug = 'ev-aydinlatma-ve-lumen-hesaplayici';
7
+ const title = 'Ev Aydınlatma ve Lümen Hesaplayıcı';
8
+ const description =
9
+ 'Herhangi bir oda için tam olarak kaç lümen ve ampule ihtiyacınız olduğunu hesaplayın. Oda tipi, boyutlar ve ampul tipini seçerek gerçek zamanlı lüks tahminleriyle profesyonel bir aydınlatma planı elde edin.';
10
+
11
+ const faqData = [
12
+ {
13
+ question: 'Oturma odası için kaç lümen gerekiyor?',
14
+ answer:
15
+ 'Tipik bir oturma odası için zeminde yaklaşık 150 lüks hedefleyin. Oda alanını metrekare cinsinden 150 ile çarparak gerekli lümeni bulun. 20 metrekarelik bir oda için yaklaşık 3.000 lümen gereklidir.',
16
+ },
17
+ {
18
+ question: 'Lüks ile lümen arasındaki fark nedir?',
19
+ answer:
20
+ 'Lümen, bir ampulün toplam ışık çıktısını ölçer. Lüks, gerçekten bir yüzeye ulaşan ışığı ölçer. Yüksek tavanlı veya koyu duvarlı bir oda, mesafe ve emilim nedeniyle ışık kaybı yaşadığından aynı lüks seviyesine ulaşmak için daha fazla lümen gerektirir.',
21
+ },
22
+ {
23
+ question: 'LED ampuller gerçekten enerji tasarrufu sağlar mı?',
24
+ answer:
25
+ 'Evet. LED ampuller yaklaşık 100 lümen/watt üretirken, akkor ampuller sadece yaklaşık 15 lümen/watt üretir. 10W\'lık bir LED ampul, 60W\'lık bir akkor ampulle aynı ışığı verir ve elektriğin altıda birini kullanır.',
26
+ },
27
+ {
28
+ question: 'Oda rengi aydınlatma ihtiyacını nasıl etkiler?',
29
+ answer:
30
+ 'Koyu duvarlar ve tavanlar ışığı emer. Koyu yüzeylere sahip bir odada, standart öneriye göre yüzde 50 daha fazla lümen gerekebilir.',
31
+ },
32
+ {
33
+ question: 'Okumak için iyi bir lüks seviyesi nedir?',
34
+ answer:
35
+ 'Rahat okumak için sayfada 300 ila 500 lüks gerekir. Genel ortam aydınlatması genellikle 150 ila 200 lüks sağlar, bu nedenle ek parlaklık için okuma yerinizin yakınına bir çalışma lambası ekleyin.',
36
+ },
37
+ {
38
+ question: 'LED ampulleri kısabilir miyim?',
39
+ answer:
40
+ 'Yalnızca kısılabilir olarak işaretlenmişse. Standart LED\'ler bir kısıcıya bağlandığında titreyebilir veya ömrü kısalabilir. Kurulumdan önce her zaman ambalajdaki kısılabilir sembolünü kontrol edin.',
41
+ },
42
+ ];
43
+
44
+ const howToData = [
45
+ {
46
+ name: 'Oda tipinizi seçin',
47
+ text: 'Oda işlevini seçin. Mutfaklar ve ofisler, yatak odaları veya koridorlardan daha parlak ışığa ihtiyaç duyar.',
48
+ },
49
+ {
50
+ name: 'Boyutları girin',
51
+ text: 'Oda genişliği, uzunluğu ve tavan yüksekliğini girin. Hesaplayıcı, bunları oda indeksini ve ışık kullanım faktörünü belirlemek için kullanır.',
52
+ },
53
+ {
54
+ name: 'Ampul tipi ve sayısını seçin',
55
+ text: 'Tercih ettiğiniz ampul teknolojisini ve sahip olduğunuz armatür sayısını seçin. Hesaplayıcı, daha fazla ampule ihtiyacınız olup olmadığını söyler.',
56
+ },
57
+ ];
58
+
59
+ const faqSchema: WithContext<FAQPage> = {
60
+ '@context': 'https://schema.org',
61
+ '@type': 'FAQPage',
62
+ mainEntity: faqData.map((item) => ({
63
+ '@type': 'Question',
64
+ name: item.question,
65
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
66
+ })),
67
+ };
68
+
69
+ const howToSchema: WithContext<HowTo> = {
70
+ '@context': 'https://schema.org',
71
+ '@type': 'HowTo',
72
+ name: title,
73
+ description,
74
+ step: howToData.map((step) => ({
75
+ '@type': 'HowToStep',
76
+ name: step.name,
77
+ text: step.text,
78
+ })),
79
+ };
80
+
81
+ const appSchema: WithContext<SoftwareApplication> = {
82
+ '@context': 'https://schema.org',
83
+ '@type': 'SoftwareApplication',
84
+ name: title,
85
+ description,
86
+ applicationCategory: 'UtilityApplication',
87
+ operatingSystem: 'All',
88
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
89
+ inLanguage: 'tr',
90
+ };
91
+
92
+ export const content: ToolLocaleContent<LightingCalculatorUI> = {
93
+ slug,
94
+ title,
95
+ description,
96
+ faq: faqData,
97
+ bibliography,
98
+ howTo: howToData,
99
+ schemas: [faqSchema, howToSchema, appSchema],
100
+ seo: [
101
+ {
102
+ type: 'title',
103
+ text: 'Ev Aydınlatma Tasarımı ve Lümen Planlaması için Tam Kılavuz',
104
+ level: 2,
105
+ },
106
+ {
107
+ type: 'paragraph',
108
+ html: 'Doğru aydınlatma, bir evi yuva haline getirir. Estetiğin ötesinde, doğru ışık miktarı ruh halini, verimliliği ve hatta göz sağlığını etkiler. <strong>Ev aydınlatma hesaplayıcımız</strong>, oda büyüklüğü, tavan yüksekliği, yüzey renkleri ve ampul tipini göz önünde bulundurarak herhangi bir oda için tam olarak kaç lümen gerektiğini belirlemenize yardımcı olur.',
109
+ },
110
+ {
111
+ type: 'stats',
112
+ items: [
113
+ { value: '150-500', label: 'Önerilen Lüks Aralığı', icon: 'mdi:brightness-6' },
114
+ { value: '100 lm/W', label: 'LED Verimliliği', icon: 'mdi:lightbulb' },
115
+ { value: '80%', label: 'Bakım Faktörü', icon: 'mdi:tools' },
116
+ ],
117
+ columns: 3,
118
+ },
119
+ {
120
+ type: 'comparative',
121
+ items: [
122
+ {
123
+ title: 'Sıcak Ortam Aydınlatması',
124
+ description: 'Oturma odaları ve yatak odaları için ideal. Rahat bir atmosfer yaratır ve akşamları göz yorgunluğunu azaltır.',
125
+ icon: 'mdi:weather-night',
126
+ points: ['150-200 lux', 'Sıcak renk sıcaklığı (2700K)', 'Birden fazla kısılabilir kaynak'],
127
+ },
128
+ {
129
+ title: 'Parlak Görev Aydınlatması',
130
+ description: 'Mutfaklar, ofisler ve banyolar için zorunlu. Detaylı işler için net görünürlük sağlar.',
131
+ icon: 'mdi:white-balance-sunny',
132
+ points: ['300-500 lux', 'Serin beyaz (4000K)', 'Çalışma yüzeylerine odaklanmış'],
133
+ },
134
+ ],
135
+ columns: 2,
136
+ },
137
+ {
138
+ type: 'title',
139
+ text: 'Neden Oda Boyutları Önemli',
140
+ level: 3,
141
+ },
142
+ {
143
+ type: 'paragraph',
144
+ html: 'Işık yayılır ve mesafe ile zayıflar. 2,5 metrelik bir tavan için yeterince parlak olan bir ampul, 4 metrelik bir tavan için çok loş olabilir. Hesaplayıcı, bu durumu oda indeksi formülü kullanarak hesaba katar; formül hem zemin alanını hem de montaj yüksekliğini dikkate alarak gerçekten çalışma düzleminize ulaşan ışık miktarını tahmin eder.',
145
+ },
146
+ {
147
+ type: 'diagnostic',
148
+ variant: 'info',
149
+ title: 'Hızlı Referans Lüks Tablosu',
150
+ icon: 'mdi:table',
151
+ badge: 'Referans',
152
+ html: '<ul style="margin:0;padding-left:1.2em"><li><strong>Oturma odası</strong> → 150 lux</li><li><strong>Mutfak</strong> → 300 lux</li><li><strong>Yatak odası</strong> → 100 lux</li><li><strong>Banyo</strong> → 200 lux</li><li><strong>Ofis</strong> → 500 lux</li><li><strong>Koridor</strong> → 100 lux</li></ul>',
153
+ },
154
+ {
155
+ type: 'summary',
156
+ title: 'Daha İyi Aydınlatma için Uzman İpuçları',
157
+ items: [
158
+ 'Ortam, görev ve vurgu olmak üzere üç aydınlatma türünü katmanlayın.',
159
+ 'Günün farklı saatlerinde parlaklığı ayarlamak için kısıcılar kullanın.',
160
+ 'Uzun ömür ve düşük enerji tüketimi için LED ampuller seçin.',
161
+ 'Gölge oluşumunu önlemek için çalışma lambalarını baskın elinizin karşı tarafına yerleştirin.',
162
+ 'Otomatik planlama ve renk sıcaklığı değişimi için akıllı ampulleri düşünün.',
163
+ ],
164
+ },
165
+ ],
166
+ ui: {
167
+ sectionTitle: 'Oda Aydınlatma Hesaplayıcı',
168
+ labelRoomType: 'Oda tipi',
169
+ labelRoomWidth: 'Genişlik',
170
+ labelRoomLength: 'Uzunluk',
171
+ labelHeight: 'Tavan',
172
+ labelBulbType: 'Ampul',
173
+ labelBulbWatt: 'Güç',
174
+ labelFixtures: 'Armatür',
175
+ labelAmbient: 'Ortam',
176
+ btnAmbientCozy: 'Rahat',
177
+ btnAmbientNormal: 'Normal',
178
+ btnAmbientBright: 'Parlak',
179
+ unitMetricRoom: 'm',
180
+ unitImperialRoom: 'ft',
181
+ unitHeight: 'm',
182
+ unitBulbs: 'ad',
183
+ unitWatt: 'W',
184
+ unitLux: 'lux',
185
+ labelTargetLux: 'Hedef lüks',
186
+ labelCurrentLux: 'Mevcut lüks',
187
+ labelBulbsNeeded: 'Gerekli ampul',
188
+ labelRoomArea: 'Alan',
189
+ statusOptimal: 'Mükemmel',
190
+ statusInsufficient: 'Çok Loş',
191
+ statusExcess: 'Çok Parlak',
192
+ btnLiving: 'Oturma',
193
+ btnKitchen: 'Mutfak',
194
+ btnBedroom: 'Yatak',
195
+ btnBathroom: 'Banyo',
196
+ btnOffice: 'Ofis',
197
+ btnHallway: 'Koridor',
198
+ btnBulbLED: 'LED',
199
+ btnBulbCFL: 'CFL',
200
+ btnBulbHalogen: 'Halojen',
201
+ btnBulbIncandescent: 'Akkor',
202
+ btnMetric: 'M',
203
+ btnImperial: 'ft',
204
+ tipOptimal: 'Aydınlatmanız bu oda için mükemmel dengelenmiş.',
205
+ tipInsufficient: 'Daha fazla armatür veya daha yüksek güçte ampul ekleyin.',
206
+ tipExcess: 'Enerji tasarrufu için armatür azaltın veya kısın.',
207
+ labelManualAdjust: 'Manuel ayar',
208
+ labelSummary: 'Aydınlatma planınız',
209
+ labelTotalLumens: 'Toplam gerekli lümen',
210
+ labelSuggestedSetup: 'Önerilen kurulum',
211
+ btnExport: 'PDF İndir',
212
+ },
213
+ };
@@ -0,0 +1,213 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { LightingCalculatorUI } from '../ui';
4
+ import { bibliography } from '../bibliography';
5
+
6
+ const slug = 'how-many-lights-per-room';
7
+ const title = '住宅照明与流明计算器';
8
+ const description =
9
+ '精确计算每个房间所需的流明数和灯泡数量。选择房间类型、尺寸和灯泡类型,即可获得包含实时照度估算的专业照明方案。';
10
+
11
+ const faqData = [
12
+ {
13
+ question: '客厅需要多少流明?',
14
+ answer:
15
+ '对于一般客厅,地面照度目标约为150勒克斯。将房间面积(平方米)乘以150,即可得出所需流明数。20平方米的房间大约需要3000流明。',
16
+ },
17
+ {
18
+ question: '勒克斯和流明有什么区别?',
19
+ answer:
20
+ '流明衡量灯泡发出的总光量。勒克斯衡量实际到达某一表面的光量。天花板较高或墙壁较暗的房间需要更多流明才能达到相同勒克斯水平,因为光线会因距离和吸收而损失。',
21
+ },
22
+ {
23
+ question: 'LED灯泡真的省电吗?',
24
+ answer:
25
+ '是的。LED灯泡每瓦约产生100流明,而白炽灯泡仅约15流明。10瓦LED灯泡的亮度与60瓦白炽灯泡相当,但耗电量仅为其六分之一。',
26
+ },
27
+ {
28
+ question: '房间颜色如何影响照明需求?',
29
+ answer:
30
+ '深色墙壁和天花板会吸收光线。在深色表面的房间中,您可能需要比标准建议多50%的流明。',
31
+ },
32
+ {
33
+ question: '阅读需要多少勒克斯?',
34
+ answer:
35
+ '舒适阅读需要页面照度达到300至500勒克斯。一般环境照明通常提供150至200勒克斯,因此请在阅读位置附近添加一盏台灯以补充亮度。',
36
+ },
37
+ {
38
+ question: 'LED灯泡可以调光吗?',
39
+ answer:
40
+ '只有标有可调光字样的LED灯泡才能调光。普通LED连接调光器时可能会闪烁或缩短寿命。安装前务必检查包装上的可调光标志。',
41
+ },
42
+ ];
43
+
44
+ const howToData = [
45
+ {
46
+ name: '选择房间类型',
47
+ text: '选择房间功能。厨房和办公室比卧室或走廊需要更明亮的光线。',
48
+ },
49
+ {
50
+ name: '输入尺寸',
51
+ text: '输入房间的宽度、长度和天花板高度。计算器将据此计算室形指数和照明利用系数。',
52
+ },
53
+ {
54
+ name: '选择灯泡类型和数量',
55
+ text: '选择您偏好的灯泡技术和现有灯具数量。计算器会告诉您是否需要更多灯泡。',
56
+ },
57
+ ];
58
+
59
+ const faqSchema: WithContext<FAQPage> = {
60
+ '@context': 'https://schema.org',
61
+ '@type': 'FAQPage',
62
+ mainEntity: faqData.map((item) => ({
63
+ '@type': 'Question',
64
+ name: item.question,
65
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
66
+ })),
67
+ };
68
+
69
+ const howToSchema: WithContext<HowTo> = {
70
+ '@context': 'https://schema.org',
71
+ '@type': 'HowTo',
72
+ name: title,
73
+ description,
74
+ step: howToData.map((step) => ({
75
+ '@type': 'HowToStep',
76
+ name: step.name,
77
+ text: step.text,
78
+ })),
79
+ };
80
+
81
+ const appSchema: WithContext<SoftwareApplication> = {
82
+ '@context': 'https://schema.org',
83
+ '@type': 'SoftwareApplication',
84
+ name: title,
85
+ description,
86
+ applicationCategory: 'UtilityApplication',
87
+ operatingSystem: 'All',
88
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
89
+ inLanguage: 'zh',
90
+ };
91
+
92
+ export const content: ToolLocaleContent<LightingCalculatorUI> = {
93
+ slug,
94
+ title,
95
+ description,
96
+ faq: faqData,
97
+ bibliography,
98
+ howTo: howToData,
99
+ schemas: [faqSchema, howToSchema, appSchema],
100
+ seo: [
101
+ {
102
+ type: 'title',
103
+ text: '住宅照明设计与流明规划完整指南',
104
+ level: 2,
105
+ },
106
+ {
107
+ type: 'paragraph',
108
+ html: '恰当的照明能把房子变成家。除了美观之外,合适的光量还会影响情绪、生产力,甚至眼睛健康。<strong>我们的住宅照明计算器</strong>会综合考虑房间大小、天花板高度、表面颜色和灯泡类型,帮助您精确计算每个房间所需的流明数。',
109
+ },
110
+ {
111
+ type: 'stats',
112
+ items: [
113
+ { value: '150-500', label: '推荐照度范围', icon: 'mdi:brightness-6' },
114
+ { value: '100 lm/W', label: 'LED效率', icon: 'mdi:lightbulb' },
115
+ { value: '80%', label: '维护系数', icon: 'mdi:tools' },
116
+ ],
117
+ columns: 3,
118
+ },
119
+ {
120
+ type: 'comparative',
121
+ items: [
122
+ {
123
+ title: '温馨环境照明',
124
+ description: '适用于客厅和卧室。营造舒适氛围,并在夜间减轻眼睛疲劳。',
125
+ icon: 'mdi:weather-night',
126
+ points: ['150-200 lux', '暖色温(2700K)', '多个可调光光源'],
127
+ },
128
+ {
129
+ title: '明亮任务照明',
130
+ description: '厨房、办公室和浴室必备。为精细工作提供清晰视野。',
131
+ icon: 'mdi:white-balance-sunny',
132
+ points: ['300-500 lux', '冷白色(4000K)', '聚焦工作台面'],
133
+ },
134
+ ],
135
+ columns: 2,
136
+ },
137
+ {
138
+ type: 'title',
139
+ text: '为什么房间尺寸很重要',
140
+ level: 3,
141
+ },
142
+ {
143
+ type: 'paragraph',
144
+ html: '光线会随着距离扩散而减弱。一盏在2.5米天花板下足够明亮的灯泡,在4米天花板下可能显得太暗。计算器通过室形指数公式来考虑这一点,该公式同时参考地板面积和安装高度,以估算实际到达工作面的光量。',
145
+ },
146
+ {
147
+ type: 'diagnostic',
148
+ variant: 'info',
149
+ title: '快速参考照度表',
150
+ icon: 'mdi:table',
151
+ badge: '参考',
152
+ html: '<ul style="margin:0;padding-left:1.2em"><li><strong>客厅</strong> → 150 lux</li><li><strong>厨房</strong> → 300 lux</li><li><strong>卧室</strong> → 100 lux</li><li><strong>浴室</strong> → 200 lux</li><li><strong>办公室</strong> → 500 lux</li><li><strong>走廊</strong> → 100 lux</li></ul>',
153
+ },
154
+ {
155
+ type: 'summary',
156
+ title: '优化照明的专业建议',
157
+ items: [
158
+ '将三种照明叠加使用:环境照明、任务照明和重点照明。',
159
+ '使用调光器根据不同时间段调整亮度。',
160
+ '选择LED灯泡,寿命长且耗电低。',
161
+ '将台灯放在惯用手的另一侧,可避免阴影。',
162
+ '考虑使用智能灯泡,实现自动定时和色温变化。',
163
+ ],
164
+ },
165
+ ],
166
+ ui: {
167
+ sectionTitle: '房间照明计算器',
168
+ labelRoomType: '房间类型',
169
+ labelRoomWidth: '宽度',
170
+ labelRoomLength: '长度',
171
+ labelHeight: '天花板',
172
+ labelBulbType: '灯泡',
173
+ labelBulbWatt: '功率',
174
+ labelFixtures: '灯具',
175
+ labelAmbient: '氛围',
176
+ btnAmbientCozy: '温馨',
177
+ btnAmbientNormal: '普通',
178
+ btnAmbientBright: '明亮',
179
+ unitMetricRoom: 'm',
180
+ unitImperialRoom: 'ft',
181
+ unitHeight: 'm',
182
+ unitBulbs: '个',
183
+ unitWatt: 'W',
184
+ unitLux: 'lux',
185
+ labelTargetLux: '目标照度',
186
+ labelCurrentLux: '当前照度',
187
+ labelBulbsNeeded: '所需灯泡',
188
+ labelRoomArea: '面积',
189
+ statusOptimal: '完美',
190
+ statusInsufficient: '太暗',
191
+ statusExcess: '太亮',
192
+ btnLiving: '客厅',
193
+ btnKitchen: '厨房',
194
+ btnBedroom: '卧室',
195
+ btnBathroom: '浴室',
196
+ btnOffice: '办公室',
197
+ btnHallway: '走廊',
198
+ btnBulbLED: 'LED',
199
+ btnBulbCFL: 'CFL',
200
+ btnBulbHalogen: '卤素',
201
+ btnBulbIncandescent: '白炽',
202
+ btnMetric: 'M',
203
+ btnImperial: 'ft',
204
+ tipOptimal: '该房间的照明平衡完美。',
205
+ tipInsufficient: '请增加灯具或更换更高瓦数的灯泡。',
206
+ tipExcess: '减少灯具或调光以节省能源。',
207
+ labelManualAdjust: '手动调整',
208
+ labelSummary: '您的照明方案',
209
+ labelTotalLumens: '所需总流明',
210
+ labelSuggestedSetup: '推荐配置',
211
+ btnExport: '导出PDF',
212
+ },
213
+ };
@@ -0,0 +1,9 @@
1
+ import type { ToolDefinition } from '../../types';
2
+ import { lightingCalculator } from './entry';
3
+ export * from './entry';
4
+ export const LIGHTING_CALCULATOR_TOOL: ToolDefinition = {
5
+ entry: lightingCalculator,
6
+ Component: () => import('./component.astro'),
7
+ SEOComponent: () => import('./seo.astro'),
8
+ BibliographyComponent: () => import('./bibliography.astro'),
9
+ };
@@ -0,0 +1,119 @@
1
+ export interface LightingInput {
2
+ roomWidthM: number;
3
+ roomLengthM: number;
4
+ roomHeightM: number;
5
+ roomType: string;
6
+ bulbType: string;
7
+ bulbWatt: number;
8
+ fixtures: number;
9
+ luxMultiplier: number;
10
+ }
11
+
12
+ export interface LightingResult {
13
+ roomArea: number;
14
+ targetLux: number;
15
+ requiredLumens: number;
16
+ bulbLumens: number;
17
+ currentLux: number;
18
+ bulbsNeeded: number;
19
+ optimalBulbs: number;
20
+ optimalWatt: number;
21
+ utilizationFactor: number;
22
+ status: 'optimal' | 'insufficient' | 'excess';
23
+ luxRatio: number;
24
+ sensoryContext: string;
25
+ suggestedProducts: string;
26
+ }
27
+
28
+ const LUX_TARGETS: Record<string, number> = {
29
+ living: 150,
30
+ kitchen: 300,
31
+ bedroom: 100,
32
+ bathroom: 200,
33
+ office: 500,
34
+ hallway: 100,
35
+ };
36
+
37
+ const LUMENS_PER_WATT: Record<string, number> = {
38
+ led: 100,
39
+ cfl: 60,
40
+ halogen: 20,
41
+ incandescent: 15,
42
+ };
43
+
44
+ const UF_MAP: Record<string, number> = {
45
+ low: 0.55,
46
+ medium: 0.50,
47
+ high: 0.45,
48
+ };
49
+
50
+ function getUF(roomHeightM: number): number {
51
+ if (roomHeightM <= 2.5) return UF_MAP.low;
52
+ if (roomHeightM <= 3.5) return UF_MAP.medium;
53
+ return UF_MAP.high;
54
+ }
55
+
56
+ function getSensoryContext(currentLux: number, targetLux: number): string {
57
+ const ratio = targetLux > 0 ? currentLux / targetLux : 0;
58
+ if (ratio < 0.3) return 'Like a dark corridor. You need significantly more light.';
59
+ if (ratio < 0.6) return 'Like a dim restaurant. Good for atmosphere, but not for daily tasks.';
60
+ if (ratio < 0.9) return 'Getting there. Like a cozy evening, but slightly too dark for comfort.';
61
+ if (ratio <= 1.3) return 'Perfectly balanced. Like a comfortable living room during the day.';
62
+ if (ratio <= 1.8) return 'Bright and energetic. Like a well-lit office or kitchen.';
63
+ return 'Very intense. Like a hospital or studio. Consider dimming for comfort.';
64
+ }
65
+
66
+ function getSuggestedProducts(
67
+ bulbsNeeded: number,
68
+ bulbType: string,
69
+ optimalWatt: number,
70
+ ): string {
71
+ const typeName = bulbType.toUpperCase();
72
+ const watt = Math.round(optimalWatt);
73
+ return `${bulbsNeeded} x ${typeName} ${watt}W bulbs ≈ ${bulbsNeeded * watt * (LUMENS_PER_WATT[bulbType] ?? 100)} lumens total`;
74
+ }
75
+
76
+ function getStatus(currentLux: number, targetLux: number): 'optimal' | 'insufficient' | 'excess' {
77
+ if (currentLux < targetLux * 0.9) return 'insufficient';
78
+ if (currentLux > targetLux * 1.3) return 'excess';
79
+ return 'optimal';
80
+ }
81
+
82
+ function getLuxRatio(currentLux: number, targetLux: number): number {
83
+ return targetLux > 0 ? currentLux / targetLux : 0;
84
+ }
85
+
86
+ function computeLighting(i: LightingInput) {
87
+ const roomArea = i.roomWidthM * i.roomLengthM;
88
+ const baseLux = LUX_TARGETS[i.roomType] ?? 150;
89
+ const targetLux = Math.round(baseLux * i.luxMultiplier);
90
+ const utilizationFactor = getUF(i.roomHeightM);
91
+ const maintenanceFactor = 0.8;
92
+ const requiredLumens = Math.ceil((targetLux * roomArea) / (utilizationFactor * maintenanceFactor));
93
+ const bulbLumens = i.bulbWatt * (LUMENS_PER_WATT[i.bulbType] ?? 100);
94
+ const currentLumens = bulbLumens * i.fixtures;
95
+ const currentLux = Math.round(currentLumens * utilizationFactor * maintenanceFactor / roomArea);
96
+ const bulbsNeeded = Math.ceil(requiredLumens / (bulbLumens || 1));
97
+ const optimalBulbs = bulbsNeeded;
98
+ const optimalWatt = Math.ceil(requiredLumens / (optimalBulbs * (LUMENS_PER_WATT[i.bulbType] ?? 100)));
99
+ return {
100
+ roomArea, targetLux, requiredLumens, bulbLumens, currentLux,
101
+ bulbsNeeded, optimalBulbs, optimalWatt, utilizationFactor,
102
+ };
103
+ }
104
+
105
+ export function calculateLighting(i: LightingInput): LightingResult {
106
+ const c = computeLighting(i);
107
+ const luxRatio = getLuxRatio(c.currentLux, c.targetLux);
108
+ const status = getStatus(c.currentLux, c.targetLux);
109
+ return {
110
+ ...c,
111
+ status,
112
+ luxRatio,
113
+ sensoryContext: getSensoryContext(c.currentLux, c.targetLux),
114
+ suggestedProducts: getSuggestedProducts(c.optimalBulbs, i.bulbType, c.optimalWatt),
115
+ };
116
+ }
117
+
118
+ export function fmt0(n: number): string { return String(Math.round(n)); }
119
+ export function fmt1(n: number): string { return n.toFixed(1); }
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { lightingCalculator } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const content = await lightingCalculator.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,113 @@
1
+ import { calculateLighting } from './logic';
2
+ import type { LightingInput, LightingResult } from './logic';
3
+
4
+ const LS_KEY = 'lighting-calc';
5
+
6
+ export interface State {
7
+ unitSys: 'metric' | 'imperial';
8
+ roomType: string;
9
+ bulbType: string;
10
+ luxMultiplier: number;
11
+ }
12
+
13
+ export const defaultState: State = {
14
+ unitSys: 'metric',
15
+ roomType: 'living',
16
+ bulbType: 'led',
17
+ luxMultiplier: 1,
18
+ };
19
+
20
+ export function loadState(): State {
21
+ const s = localStorage.getItem(LS_KEY);
22
+ if (!s) return { ...defaultState };
23
+ try {
24
+ const v = JSON.parse(s);
25
+ return {
26
+ unitSys: v.unit || defaultState.unitSys,
27
+ roomType: v.room || defaultState.roomType,
28
+ bulbType: v.bulb || defaultState.bulbType,
29
+ luxMultiplier: typeof v.ambient === 'number' ? v.ambient : defaultState.luxMultiplier,
30
+ };
31
+ } catch {
32
+ return { ...defaultState };
33
+ }
34
+ }
35
+
36
+ export function saveState(
37
+ st: State,
38
+ getNum: (id: string) => number,
39
+ ) {
40
+ const v = {
41
+ unit: st.unitSys,
42
+ room: st.roomType,
43
+ bulb: st.bulbType,
44
+ ambient: st.luxMultiplier,
45
+ w: getNum('lc-w'),
46
+ l: getNum('lc-l'),
47
+ h: getNum('lc-h'),
48
+ bw: getNum('lc-bw'),
49
+ f: getNum('lc-f'),
50
+ };
51
+ localStorage.setItem(LS_KEY, JSON.stringify(v));
52
+ }
53
+
54
+ export function restoreInputs(
55
+ getNum: (id: string) => number,
56
+ setVal: (id: string, v: string) => void,
57
+ ) {
58
+ const s = localStorage.getItem(LS_KEY);
59
+ if (!s) return;
60
+ try {
61
+ const v = JSON.parse(s);
62
+ const ids = ['lc-w', 'lc-l', 'lc-h', 'lc-bw', 'lc-f'];
63
+ const keys = ['w', 'l', 'h', 'bw', 'f'];
64
+ ids.forEach((id, i) => {
65
+ if (v[keys[i]] !== undefined) setVal(id, String(v[keys[i]]));
66
+ });
67
+ } catch {}
68
+ }
69
+
70
+ export function factors(unitSys: 'metric' | 'imperial') {
71
+ return unitSys === 'metric'
72
+ ? { rm: 1, hm: 1 }
73
+ : { rm: 0.3048, hm: 0.3048 };
74
+ }
75
+
76
+ export function runCalc(
77
+ st: State,
78
+ getNum: (id: string) => number,
79
+ ): LightingResult {
80
+ const f = factors(st.unitSys);
81
+ const input: LightingInput = {
82
+ roomWidthM: getNum('lc-w') * f.rm,
83
+ roomLengthM: getNum('lc-l') * f.rm,
84
+ roomHeightM: getNum('lc-h') * f.hm,
85
+ roomType: st.roomType,
86
+ bulbType: st.bulbType,
87
+ bulbWatt: getNum('lc-bw'),
88
+ fixtures: getNum('lc-f'),
89
+ luxMultiplier: st.luxMultiplier,
90
+ };
91
+ return calculateLighting(input);
92
+ }
93
+
94
+ export function updateUnitLabels(
95
+ unitSys: 'metric' | 'imperial',
96
+ setTxt: (id: string, v: string) => void,
97
+ ) {
98
+ const isM = unitSys === 'metric';
99
+ setTxt('lc-w-u', isM ? 'm' : 'ft');
100
+ setTxt('lc-l-u', isM ? 'm' : 'ft');
101
+ setTxt('lc-h-u', isM ? 'm' : 'ft');
102
+ }
103
+
104
+ export function convertValue(
105
+ id: string,
106
+ getNum: (id: string) => number,
107
+ setVal: (id: string, v: string) => void,
108
+ toMetric: boolean,
109
+ ) {
110
+ const factor = toMetric ? 0.3048 : 1 / 0.3048;
111
+ const v = getNum(id);
112
+ setVal(id, String(Math.round(v * factor * 10) / 10));
113
+ }