@jjlmoya/utils-textiles 1.20.0 → 1.22.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 (209) hide show
  1. package/package.json +1 -1
  2. package/src/category/i18n/de.ts +76 -76
  3. package/src/category/i18n/en.ts +76 -76
  4. package/src/category/i18n/es.ts +76 -76
  5. package/src/category/i18n/fr.ts +76 -76
  6. package/src/category/i18n/id.ts +76 -76
  7. package/src/category/i18n/it.ts +76 -76
  8. package/src/category/i18n/ja.ts +76 -76
  9. package/src/category/i18n/ko.ts +76 -76
  10. package/src/category/i18n/nl.ts +76 -76
  11. package/src/category/i18n/pl.ts +76 -76
  12. package/src/category/i18n/pt.ts +76 -76
  13. package/src/category/i18n/ru.ts +76 -76
  14. package/src/category/i18n/sv.ts +76 -76
  15. package/src/category/i18n/zh.ts +76 -76
  16. package/src/category/index.ts +2 -0
  17. package/src/category/seo.astro +1 -1
  18. package/src/components/PreviewNavSidebar.astro +1 -1
  19. package/src/components/PreviewToolbar.astro +1 -1
  20. package/src/data.ts +1 -1
  21. package/src/entries.ts +4 -1
  22. package/src/env.d.ts +1 -1
  23. package/src/index.ts +1 -1
  24. package/src/layouts/PreviewLayout.astro +113 -113
  25. package/src/pages/index.astro +1 -1
  26. package/src/tests/faq_count.test.ts +19 -19
  27. package/src/tests/i18n_coverage.test.ts +4 -2
  28. package/src/tests/locale_spelling.test.ts +44 -0
  29. package/src/tests/mocks/astro_mock.js +1 -1
  30. package/src/tests/no_en_dash.test.ts +70 -70
  31. package/src/tests/no_h1_in_components.test.ts +1 -1
  32. package/src/tests/seo_length.test.ts +22 -22
  33. package/src/tests/seo_translation_completeness.test.ts +69 -0
  34. package/src/tests/spanish_leakage.test.ts +175 -153
  35. package/src/tests/tool_validation.test.ts +17 -17
  36. package/src/tool/burnTest/i18n/es.ts +211 -211
  37. package/src/tool/burnTest/i18n/fr.ts +211 -211
  38. package/src/tool/burnTest/i18n/ja.ts +211 -211
  39. package/src/tool/burnTest/i18n/ko.ts +211 -211
  40. package/src/tool/burnTest/i18n/pl.ts +211 -211
  41. package/src/tool/burnTest/i18n/ru.ts +211 -211
  42. package/src/tool/burnTest/i18n/sv.ts +211 -211
  43. package/src/tool/burnTest/i18n/zh.ts +211 -211
  44. package/src/tool/burnTest/seo.astro +15 -15
  45. package/src/tool/clothingSizeConverter/i18n/de.ts +348 -348
  46. package/src/tool/clothingSizeConverter/i18n/en.ts +348 -348
  47. package/src/tool/clothingSizeConverter/i18n/fr.ts +348 -348
  48. package/src/tool/clothingSizeConverter/i18n/ja.ts +348 -348
  49. package/src/tool/clothingSizeConverter/i18n/ko.ts +348 -348
  50. package/src/tool/clothingSizeConverter/i18n/nl.ts +348 -348
  51. package/src/tool/clothingSizeConverter/i18n/pl.ts +348 -348
  52. package/src/tool/clothingSizeConverter/i18n/ru.ts +348 -348
  53. package/src/tool/clothingSizeConverter/i18n/sv.ts +348 -348
  54. package/src/tool/clothingSizeConverter/i18n/tr.ts +348 -348
  55. package/src/tool/clothingSizeConverter/i18n/zh.ts +348 -348
  56. package/src/tool/clothingSizeConverter/seo.astro +15 -15
  57. package/src/tool/embroideryStitchPricingEstimator/bibliography.astro +6 -0
  58. package/src/tool/embroideryStitchPricingEstimator/bibliography.ts +12 -0
  59. package/src/tool/embroideryStitchPricingEstimator/component.astro +111 -0
  60. package/src/tool/embroideryStitchPricingEstimator/controller.ts +111 -0
  61. package/src/tool/embroideryStitchPricingEstimator/dom-views.ts +94 -0
  62. package/src/tool/embroideryStitchPricingEstimator/embroidery-stitch-count-pricing-estimator.css +438 -0
  63. package/src/tool/embroideryStitchPricingEstimator/entry.ts +29 -0
  64. package/src/tool/embroideryStitchPricingEstimator/evaluator.ts +22 -0
  65. package/src/tool/embroideryStitchPricingEstimator/i18n/de.ts +38 -0
  66. package/src/tool/embroideryStitchPricingEstimator/i18n/en.ts +194 -0
  67. package/src/tool/embroideryStitchPricingEstimator/i18n/es.ts +38 -0
  68. package/src/tool/embroideryStitchPricingEstimator/i18n/fr.ts +38 -0
  69. package/src/tool/embroideryStitchPricingEstimator/i18n/id.ts +38 -0
  70. package/src/tool/embroideryStitchPricingEstimator/i18n/it.ts +38 -0
  71. package/src/tool/embroideryStitchPricingEstimator/i18n/ja.ts +38 -0
  72. package/src/tool/embroideryStitchPricingEstimator/i18n/ko.ts +38 -0
  73. package/src/tool/embroideryStitchPricingEstimator/i18n/nl.ts +38 -0
  74. package/src/tool/embroideryStitchPricingEstimator/i18n/pl.ts +38 -0
  75. package/src/tool/embroideryStitchPricingEstimator/i18n/pt.ts +38 -0
  76. package/src/tool/embroideryStitchPricingEstimator/i18n/ru.ts +38 -0
  77. package/src/tool/embroideryStitchPricingEstimator/i18n/sv.ts +38 -0
  78. package/src/tool/embroideryStitchPricingEstimator/i18n/tr.ts +38 -0
  79. package/src/tool/embroideryStitchPricingEstimator/i18n/zh.ts +38 -0
  80. package/src/tool/embroideryStitchPricingEstimator/index.ts +11 -0
  81. package/src/tool/embroideryStitchPricingEstimator/logic.test.ts +40 -0
  82. package/src/tool/embroideryStitchPricingEstimator/logic.ts +86 -0
  83. package/src/tool/embroideryStitchPricingEstimator/seo.astro +15 -0
  84. package/src/tool/embroideryStitchPricingEstimator/storage.ts +20 -0
  85. package/src/tool/embroideryStitchPricingEstimator/ui.ts +53 -0
  86. package/src/tool/fabricProjectCalculator/i18n/de.ts +236 -236
  87. package/src/tool/fabricProjectCalculator/i18n/ja.ts +236 -236
  88. package/src/tool/fabricProjectCalculator/i18n/ko.ts +236 -236
  89. package/src/tool/fabricProjectCalculator/i18n/nl.ts +236 -236
  90. package/src/tool/fabricProjectCalculator/i18n/pl.ts +236 -236
  91. package/src/tool/fabricProjectCalculator/i18n/ru.ts +236 -236
  92. package/src/tool/fabricProjectCalculator/i18n/sv.ts +236 -236
  93. package/src/tool/fabricProjectCalculator/i18n/tr.ts +236 -236
  94. package/src/tool/fabricProjectCalculator/i18n/zh.ts +236 -236
  95. package/src/tool/fabricProjectCalculator/seo.astro +15 -15
  96. package/src/tool/fabricTruth/i18n/de.ts +268 -268
  97. package/src/tool/fabricTruth/i18n/en.ts +268 -268
  98. package/src/tool/fabricTruth/i18n/es.ts +302 -302
  99. package/src/tool/fabricTruth/i18n/fr.ts +268 -268
  100. package/src/tool/fabricTruth/i18n/id.ts +268 -268
  101. package/src/tool/fabricTruth/i18n/it.ts +268 -268
  102. package/src/tool/fabricTruth/i18n/ja.ts +268 -268
  103. package/src/tool/fabricTruth/i18n/ko.ts +268 -268
  104. package/src/tool/fabricTruth/i18n/nl.ts +268 -268
  105. package/src/tool/fabricTruth/i18n/pl.ts +268 -268
  106. package/src/tool/fabricTruth/i18n/pt.ts +268 -268
  107. package/src/tool/fabricTruth/i18n/ru.ts +268 -268
  108. package/src/tool/fabricTruth/i18n/sv.ts +268 -268
  109. package/src/tool/fabricTruth/i18n/tr.ts +268 -268
  110. package/src/tool/fabricTruth/i18n/zh.ts +272 -272
  111. package/src/tool/fabricTruth/seo.astro +15 -15
  112. package/src/tool/fiberPrep/i18n/de.ts +215 -215
  113. package/src/tool/fiberPrep/i18n/fr.ts +215 -215
  114. package/src/tool/fiberPrep/i18n/ja.ts +215 -215
  115. package/src/tool/fiberPrep/i18n/ko.ts +215 -215
  116. package/src/tool/fiberPrep/i18n/nl.ts +215 -215
  117. package/src/tool/fiberPrep/i18n/pl.ts +215 -215
  118. package/src/tool/fiberPrep/i18n/ru.ts +215 -215
  119. package/src/tool/fiberPrep/i18n/sv.ts +215 -215
  120. package/src/tool/fiberPrep/i18n/tr.ts +215 -215
  121. package/src/tool/fiberPrep/i18n/zh.ts +215 -215
  122. package/src/tool/fiberPrep/seo.astro +15 -15
  123. package/src/tool/knittingGauge/i18n/de.ts +224 -224
  124. package/src/tool/knittingGauge/i18n/ja.ts +224 -224
  125. package/src/tool/knittingGauge/i18n/ko.ts +224 -224
  126. package/src/tool/knittingGauge/i18n/pl.ts +224 -224
  127. package/src/tool/knittingGauge/i18n/ru.ts +224 -224
  128. package/src/tool/knittingGauge/i18n/tr.ts +224 -224
  129. package/src/tool/knittingGauge/i18n/zh.ts +224 -224
  130. package/src/tool/knittingGauge/seo.astro +15 -15
  131. package/src/tool/laundryGuide/i18n/de.ts +271 -271
  132. package/src/tool/laundryGuide/i18n/en.ts +271 -271
  133. package/src/tool/laundryGuide/i18n/es.ts +271 -271
  134. package/src/tool/laundryGuide/i18n/fr.ts +255 -255
  135. package/src/tool/laundryGuide/i18n/id.ts +271 -271
  136. package/src/tool/laundryGuide/i18n/it.ts +271 -271
  137. package/src/tool/laundryGuide/i18n/ja.ts +271 -271
  138. package/src/tool/laundryGuide/i18n/ko.ts +271 -271
  139. package/src/tool/laundryGuide/i18n/nl.ts +271 -271
  140. package/src/tool/laundryGuide/i18n/pl.ts +271 -271
  141. package/src/tool/laundryGuide/i18n/pt.ts +271 -271
  142. package/src/tool/laundryGuide/i18n/ru.ts +271 -271
  143. package/src/tool/laundryGuide/i18n/sv.ts +271 -271
  144. package/src/tool/laundryGuide/i18n/tr.ts +271 -271
  145. package/src/tool/laundryGuide/i18n/zh.ts +271 -271
  146. package/src/tool/laundryGuide/seo.astro +15 -15
  147. package/src/tool/needleConverter/i18n/de.ts +202 -202
  148. package/src/tool/needleConverter/i18n/es.ts +207 -207
  149. package/src/tool/needleConverter/i18n/fr.ts +207 -207
  150. package/src/tool/needleConverter/i18n/ja.ts +202 -202
  151. package/src/tool/needleConverter/i18n/ko.ts +202 -202
  152. package/src/tool/needleConverter/i18n/nl.ts +202 -202
  153. package/src/tool/needleConverter/i18n/pl.ts +202 -202
  154. package/src/tool/needleConverter/i18n/ru.ts +202 -202
  155. package/src/tool/needleConverter/i18n/sv.ts +202 -202
  156. package/src/tool/needleConverter/i18n/tr.ts +202 -202
  157. package/src/tool/needleConverter/i18n/zh.ts +202 -202
  158. package/src/tool/needleConverter/seo.astro +15 -15
  159. package/src/tool/sewingPatternScaler/component.astro +236 -236
  160. package/src/tool/sewingPatternScaler/i18n/fr.ts +170 -170
  161. package/src/tool/sewingPatternScaler/i18n/ja.ts +114 -114
  162. package/src/tool/sewingPatternScaler/i18n/ko.ts +114 -114
  163. package/src/tool/sewingPatternScaler/i18n/nl.ts +114 -114
  164. package/src/tool/sewingPatternScaler/i18n/pl.ts +114 -114
  165. package/src/tool/sewingPatternScaler/i18n/ru.ts +114 -114
  166. package/src/tool/sewingPatternScaler/i18n/sv.ts +114 -114
  167. package/src/tool/sewingPatternScaler/i18n/tr.ts +114 -114
  168. package/src/tool/sewingPatternScaler/i18n/zh.ts +114 -114
  169. package/src/tool/sewingPatternScaler/seo.astro +15 -15
  170. package/src/tool/shoeSizeConverter/i18n/fr.ts +183 -183
  171. package/src/tool/shoeSizeConverter/i18n/ja.ts +179 -179
  172. package/src/tool/shoeSizeConverter/i18n/ko.ts +179 -179
  173. package/src/tool/shoeSizeConverter/i18n/nl.ts +179 -179
  174. package/src/tool/shoeSizeConverter/i18n/pl.ts +179 -179
  175. package/src/tool/shoeSizeConverter/i18n/ru.ts +179 -179
  176. package/src/tool/shoeSizeConverter/i18n/sv.ts +179 -179
  177. package/src/tool/shoeSizeConverter/i18n/tr.ts +179 -179
  178. package/src/tool/shoeSizeConverter/i18n/zh.ts +179 -179
  179. package/src/tool/shoeSizeConverter/seo.astro +15 -15
  180. package/src/tool/stainChemistry/bibliography.ts +16 -16
  181. package/src/tool/stainChemistry/i18n/de.ts +286 -286
  182. package/src/tool/stainChemistry/i18n/en.ts +289 -289
  183. package/src/tool/stainChemistry/i18n/es.ts +286 -286
  184. package/src/tool/stainChemistry/i18n/fr.ts +296 -296
  185. package/src/tool/stainChemistry/i18n/id.ts +286 -286
  186. package/src/tool/stainChemistry/i18n/it.ts +286 -286
  187. package/src/tool/stainChemistry/i18n/ja.ts +286 -286
  188. package/src/tool/stainChemistry/i18n/ko.ts +283 -283
  189. package/src/tool/stainChemistry/i18n/nl.ts +286 -286
  190. package/src/tool/stainChemistry/i18n/pl.ts +286 -286
  191. package/src/tool/stainChemistry/i18n/pt.ts +286 -286
  192. package/src/tool/stainChemistry/i18n/ru.ts +286 -286
  193. package/src/tool/stainChemistry/i18n/sv.ts +286 -286
  194. package/src/tool/stainChemistry/i18n/tr.ts +286 -286
  195. package/src/tool/stainChemistry/i18n/zh.ts +281 -281
  196. package/src/tool/stainChemistry/seo.astro +15 -15
  197. package/src/tool/yarnCalculator/i18n/de.ts +296 -296
  198. package/src/tool/yarnCalculator/i18n/es.ts +296 -296
  199. package/src/tool/yarnCalculator/i18n/fr.ts +296 -296
  200. package/src/tool/yarnCalculator/i18n/ja.ts +296 -296
  201. package/src/tool/yarnCalculator/i18n/ko.ts +296 -296
  202. package/src/tool/yarnCalculator/i18n/nl.ts +296 -296
  203. package/src/tool/yarnCalculator/i18n/pl.ts +296 -296
  204. package/src/tool/yarnCalculator/i18n/ru.ts +296 -296
  205. package/src/tool/yarnCalculator/i18n/sv.ts +296 -296
  206. package/src/tool/yarnCalculator/i18n/tr.ts +296 -296
  207. package/src/tool/yarnCalculator/i18n/zh.ts +295 -295
  208. package/src/tool/yarnCalculator/seo.astro +15 -15
  209. package/src/tools.ts +2 -2
@@ -0,0 +1,38 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import { bibliography } from '../bibliography';
4
+ import type { EmbroideryStitchPricingEstimatorUI } from '../ui';
5
+
6
+ const slug = 'kalkulator-liczby-sciegow-ceny-haftu';
7
+ const title = 'Kalkulator liczby ściegów i ceny haftu';
8
+ const description = 'Oszacuj liczbę ściegów, czas produkcji i podstawowy koszt pracy na podstawie rozmiaru wzoru, gęstości, kolorów, prędkości i stawki godzinowej.';
9
+ const faq = [
10
+ { question: 'Jak działa szacowanie liczby ściegów?', answer: 'Kalkulator mnoży wyhaftowaną powierzchnię przez kwadrat gęstości ściegów, a następnie stosuje współczynnik rodzaju ściegu. Powstaje porównywalne obciążenie pracy, a nie prognoza pliku maszyny.' },
11
+ { question: 'Dlaczego rodzaj ściegu zmienia wynik?', answer: 'Ścieg bieżący, satynowy, wypełniający i tatami układają nić w różny sposób. Przejrzysty współczynnik pokazuje, że gęstsze pokrycie wymaga większego nakładu produkcyjnego.' },
12
+ { question: 'Czy liczba kolorów zmienia liczbę ściegów?', answer: 'Nie. Liczba kolorów dodaje tylko czas przezbrojenia. Model przyjmuje 1,75 minuty za każdą zmianę po pierwszym kolorze jako założenie planistyczne.' },
13
+ { question: 'Czy obliczona cena jest wyceną?', answer: 'Nie. To szacunek pracy oparty na wpisanej stawce godzinowej. Nici, stabilizator, digitalizacja, przygotowanie, wykończenie, koszty stałe, podatki i marża nie są uwzględnione.' },
14
+ ];
15
+ const howTo = [
16
+ { name: 'Zmierz wzór', text: 'Wpisz wyhaftowaną szerokość i wysokość w centymetrach. Użyj obszaru z haftem, a nie całego rozmiaru tamborka.' },
17
+ { name: 'Opisz plan ściegów', text: 'Wybierz dominujący rodzaj ściegu, wpisz gęstość w ściegach na centymetr i policz kolory nici.' },
18
+ { name: 'Dodaj założenia produkcyjne', text: 'Wpisz prędkość maszyny w ściegach na minutę i stawkę pracy za godzinę. Zachowaj jedną walutę.' },
19
+ { name: 'Odczytaj szacunek', text: 'Użyj ściegów równoważnych, czasu produkcji, czasu przygotowania i ceny bazowej do porównania wzorów.' },
20
+ ];
21
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
22
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
23
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'BusinessApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'pl' };
24
+ const ui: EmbroideryStitchPricingEstimatorUI = {
25
+ intro: 'Zbuduj plan ściegów w tamborku i zobacz nakład produkcyjny oraz wartość pracy.', stageLabel: 'Tamborek haftu', controlsLabel: 'Zbuduj plan ściegów', resultLabel: 'Podstawowy koszt pracy', widthLabel: 'Szerokość wzoru', heightLabel: 'Wysokość wzoru', densityLabel: 'Gęstość ściegów', stitchLabel: 'Dominujący rodzaj ściegu', coloursLabel: 'Kolory nici', speedLabel: 'Prędkość maszyny', rateLabel: 'Stawka godzinowa', widthHint: 'obszar haftu w cm', heightHint: 'obszar haftu w cm', densityHint: 'ściegi na cm', coloursHint: 'pierwszy kolor nie wymaga zmiany', speedHint: 'ściegi na minutę', rateHint: 'twoja waluta za godzinę', stitchRunning: 'Ścieg bieżący', stitchSatin: 'Ścieg satynowy', stitchFill: 'Ścieg wypełniający', stitchTatami: 'Wypełnienie tatami', stitchRunningNote: 'Lekkie kontury i napisy z małym pokryciem.', stitchSatinNote: 'Gładkie kolumny i krawędzie ze skupioną ścieżką nici.', stitchFillNote: 'Pełne pokrycie, które zwiększa zużycie nici i nakład pracy.', stitchTatamiNote: 'Warstwowe wypełnienie z najwyższym współczynnikiem planowania.', selectStitch: 'Wybierz rodzaj ściegu', emptyResult: 'Wpisz plan ściegów, aby uruchomić tamborek.', stitchesUnit: 'ściegi równoważne', minutesUnit: 'minuty', hoursUnit: 'godziny', priceUnit: 'jednostki waluty', areaLabel: 'Wyhaftowana powierzchnia', effectiveLabel: 'Ściegi równoważne', stitchTimeLabel: 'Czas haftowania', productionLabel: 'Czas produkcji', setupLabel: 'Przygotowanie kolorów', changesLabel: 'Zmiany kolorów', basePriceLabel: 'Podstawowy koszt pracy', estimateBadge: 'Szacunek gotowy', attentionBadge: 'Sprawdź skalę', warningHighVolume: 'Duża liczba ściegów. Wykonaj próbę na maszynie przed wyceną.', warningLongRun: 'Długi cykl produkcyjny. Osobno sprawdź tamborek, zerwania nici i wykończenie.', invalidMessage: 'Użyj dodatnich wartości rozmiaru, gęstości, prędkości i stawki godzinowej.', formulaTitle: 'Co jest liczone', formulaText: 'Powierzchnia razy gęstość do kwadratu daje podstawę pokrycia. Współczynnik ściegu zamienia ją na ściegi równoważne, a każda zmiana po pierwszym kolorze dodaje 1,75 minuty.', noteTitle: 'Granica planowania', noteText: 'To przejrzysty kalkulator, a nie zdigitalizowany plik maszyny ani końcowa wycena. Samodzielnie dodaj materiały, przygotowanie, wykończenie, koszty, podatki i marżę.', reset: 'Zresetuj przykład', copied: 'Skopiowano podsumowanie', copySummary: 'Kopiuj podsumowanie', canvasAlt: 'Tamborek z tkaniną i liniami nici pokazującymi obecną gęstość',
26
+ };
27
+ export const content: ToolLocaleContent<EmbroideryStitchPricingEstimatorUI> = { slug, title, description, ui, faq, bibliography, howTo, schemas: [faqSchema, howToSchema, appSchema], seo: [
28
+ { type: 'title', text: 'Jak oszacować liczbę ściegów i koszt pracy przy hafcie', level: 2 },
29
+ { type: 'paragraph', html: 'Wzór haftu stawia dwa pytania planistyczne: ile pracy nici zawiera motyw i jak długo maszyna oraz operator będą zajęci? Ten kalkulator pokazuje oba aspekty i zamienia rozmiar, gęstość, rodzaj ściegu, kolory, prędkość oraz stawkę w porównywalną wartość planistyczną.' },
30
+ { type: 'title', text: 'Model liczby ściegów', level: 3 },
31
+ { type: 'paragraph', html: 'Podstawa to wyhaftowana powierzchnia pomnożona przez kwadrat gęstości w ściegach na centymetr. Współczynnik względny uwzględnia rodzinę ściegów: ścieg bieżący jest lżejszy, satyna stanowi punkt odniesienia, a wypełnienie i tatami są coraz gęstszymi przypadkami planistycznymi. To szacunek pracy równoważnej, a nie dokładna liczba eksportowana przez program do digitalizacji.' },
32
+ { type: 'table', headers: ['Dane', 'Co zmieniają', 'Czego nie obiecują'], rows: [['Szerokość i wysokość', 'Powierzchnię pokrycia i liczbę bazową', 'Dokładnego obrysu zdigitalizowanego pliku'], ['Gęstość i rodzaj ściegu', 'Równoważną pracę nici i czas maszyny', 'Uniwersalnego ustawienia jakości dla każdej tkaniny'], ['Kolory', 'Czas zmiany po pierwszym kolorze', 'Liczby obcięć ani zerwań nici'], ['Prędkość i stawka', 'Czas produkcji i wartość pracy', 'Końcowej wyceny dla klienta']] },
33
+ { type: 'title', text: 'Zamień szacunek w odpowiedzialną cenę', level: 3 },
34
+ { type: 'paragraph', html: 'Pokazana cena to tylko czas pracy pomnożony przez stawkę godzinową. Prawdziwa wycena powinna dodać digitalizację, nici, stabilizator, mocowanie, ustawienie maszyny, próbki, obcinanie, poprawki, wykończenie, koszty stałe, podatki i marżę. Rozdziel te elementy, aby mała liczba ściegów nie ukryła pracochłonnego przygotowania.' },
35
+ { type: 'tip', title: 'Przetestuj plan przed wyceną', html: 'Wykonaj reprezentatywną próbę na docelowej tkaninie i stabilizatorze. Porównaj czas oraz zachowanie nici z szacunkiem, a potem dopasuj lokalne założenia prędkości i przygotowania.' },
36
+ { type: 'title', text: 'Jak czytać podział produkcji', level: 3 },
37
+ { type: 'list', items: ['Ściegi równoważne pokazują obciążenie pokrycia po zastosowaniu współczynnika.', 'Czas produkcji to ściegi równoważne podzielone przez prędkość maszyny.', 'Przygotowanie kolorów dodaje 1,75 minuty za każdą zmianę po pierwszej.', 'Podstawowy koszt pracy to łączny czas podzielony przez 60 i pomnożony przez stawkę godzinową.'] },
38
+ ] };
@@ -0,0 +1,38 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import { bibliography } from '../bibliography';
4
+ import type { EmbroideryStitchPricingEstimatorUI } from '../ui';
5
+
6
+ const slug = 'calculadora-pontos-preco-bordado';
7
+ const title = 'Calculadora de pontos e preço de bordado';
8
+ const description = 'Estime pontos, tempo de produção e custo base de mão de obra com base no tamanho, densidade, cores, velocidade e valor por hora.';
9
+ const faq = [
10
+ { question: 'Como funciona a estimativa de pontos?', answer: 'A calculadora multiplica a área bordada pelo quadrado da densidade de pontos e aplica um fator conforme o tipo de ponto. O resultado é uma carga de trabalho equivalente para comparar produções, não uma previsão do arquivo da máquina.' },
11
+ { question: 'Por que o tipo de ponto muda a estimativa?', answer: 'Ponto corrido, cetim, preenchimento e tatami depositam a linha de maneiras diferentes. O fator relativo mostra com transparência que uma cobertura mais densa exige mais trabalho de produção.' },
12
+ { question: 'A quantidade de cores muda o número de pontos?', answer: 'Não. As cores acrescentam apenas tempo de troca. O modelo considera 1,75 minuto para cada troca depois da primeira cor como uma hipótese de planejamento.' },
13
+ { question: 'O preço calculado é um orçamento?', answer: 'Não. É uma estimativa de mão de obra usando o valor por hora informado. Linha, estabilizador, digitalização, preparação, acabamento, despesas, impostos e margem de lucro não estão incluídos.' },
14
+ ];
15
+ const howTo = [
16
+ { name: 'Meça o desenho', text: 'Informe a largura e a altura bordadas em centímetros. Use a área que receberá pontos, não o tamanho total do bastidor.' },
17
+ { name: 'Descreva o plano de pontos', text: 'Escolha o tipo dominante, informe a densidade em pontos por centímetro e conte as cores da linha.' },
18
+ { name: 'Adicione as hipóteses', text: 'Informe a velocidade em pontos por minuto e o valor da mão de obra por hora. Mantenha uma única moeda.' },
19
+ { name: 'Leia a estimativa', text: 'Use pontos equivalentes, tempo de produção, preparação e custo base para comparar desenhos antes da digitalização ou do orçamento.' },
20
+ ];
21
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
22
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
23
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'BusinessApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'pt' };
24
+ const ui: EmbroideryStitchPricingEstimatorUI = {
25
+ intro: 'Monte seu plano de pontos no bastidor e veja o esforço de produção e o valor da mão de obra.', stageLabel: 'O bastidor de pontos', controlsLabel: 'Monte o plano de pontos', resultLabel: 'Estimativa base de mão de obra', widthLabel: 'Largura do desenho', heightLabel: 'Altura do desenho', densityLabel: 'Densidade de pontos', stitchLabel: 'Ponto dominante', coloursLabel: 'Cores da linha', speedLabel: 'Velocidade da máquina', rateLabel: 'Valor por hora', widthHint: 'área bordada em cm', heightHint: 'área bordada em cm', densityHint: 'pontos por cm', coloursHint: 'a primeira cor não exige troca', speedHint: 'pontos por minuto', rateHint: 'sua moeda por hora', stitchRunning: 'Ponto corrido', stitchSatin: 'Ponto cetim', stitchFill: 'Ponto de preenchimento', stitchTatami: 'Preenchimento tatami', stitchRunningNote: 'Contornos leves e letras com pouca cobertura.', stitchSatinNote: 'Colunas e bordas suaves com um caminho de linha concentrado.', stitchFillNote: 'Cobertura sólida que acrescenta linha e esforço de produção.', stitchTatamiNote: 'Preenchimento em camadas com o maior fator de planejamento.', selectStitch: 'Escolha um tipo de ponto', emptyResult: 'Informe um plano para ativar o bastidor.', stitchesUnit: 'pontos equivalentes', minutesUnit: 'minutos', hoursUnit: 'horas', priceUnit: 'unidades monetárias', areaLabel: 'Área bordada', effectiveLabel: 'Pontos equivalentes', stitchTimeLabel: 'Tempo de bordado', productionLabel: 'Tempo de produção', setupLabel: 'Preparação das cores', changesLabel: 'Trocas de cor', basePriceLabel: 'Estimativa base de mão de obra', estimateBadge: 'Estimativa pronta', attentionBadge: 'Revise a escala', warningHighVolume: 'Grande volume de pontos. Faça uma amostra real na máquina antes de orçar.', warningLongRun: 'Produção longa. Verifique separadamente bastidor, quebras de linha e acabamento.', invalidMessage: 'Use valores positivos para tamanho, densidade, velocidade e valor por hora.', formulaTitle: 'O que é contado', formulaText: 'Área vezes densidade ao quadrado cria a base de cobertura. O fator do ponto transforma esse valor em pontos equivalentes, e cada troca depois da primeira cor acrescenta 1,75 minuto.', noteTitle: 'Limite do planejamento', noteText: 'Este é um estimador transparente, não um arquivo de máquina digitalizado nem um orçamento final. Acrescente materiais, preparação, acabamento, despesas, impostos e margem por conta própria.', reset: 'Redefinir exemplo', copied: 'Resumo copiado', copySummary: 'Copiar resumo', canvasAlt: 'Bastidor de tecido com linhas de linha mostrando a densidade atual',
26
+ };
27
+ export const content: ToolLocaleContent<EmbroideryStitchPricingEstimatorUI> = { slug, title, description, ui, faq, bibliography, howTo, schemas: [faqSchema, howToSchema, appSchema], seo: [
28
+ { type: 'title', text: 'Como estimar pontos e preço de mão de obra de bordado', level: 2 },
29
+ { type: 'paragraph', html: 'Um desenho de bordado traz duas perguntas de planejamento: quanto trabalho de linha existe no motivo e por quanto tempo máquina e operador ficarão ocupados? Este estimador mantém as duas perguntas visíveis e transforma tamanho, densidade, ponto, cores, velocidade e valor por hora em um número comparável.' },
30
+ { type: 'title', text: 'O modelo de pontos', level: 3 },
31
+ { type: 'paragraph', html: 'A base é a área bordada multiplicada pelo quadrado da densidade em pontos por centímetro. Um fator relativo considera a família do ponto: o ponto corrido é mais leve, o cetim é a referência e os preenchimentos são casos de planejamento cada vez mais densos. É uma estimativa do trabalho equivalente, não a contagem exata exportada por um programa de digitalização.' },
32
+ { type: 'table', headers: ['Entrada', 'O que muda', 'O que não promete'], rows: [['Largura e altura', 'Área de cobertura e pontos básicos', 'O contorno exato de um arquivo digitalizado'], ['Densidade e tipo de ponto', 'Trabalho de linha equivalente e tempo de máquina', 'Uma configuração de qualidade universal para todo tecido'], ['Cores', 'Tempo de troca depois da primeira cor', 'A quantidade de cortes ou quebras de linha'], ['Velocidade e valor', 'Duração da produção e valor da mão de obra', 'Um orçamento final para o cliente']] },
33
+ { type: 'title', text: 'Transforme uma estimativa em um preço responsável', level: 3 },
34
+ { type: 'paragraph', html: 'O preço exibido multiplica apenas o tempo de trabalho pelo valor por hora. Um orçamento real deve acrescentar digitalização, linha, estabilizador, bastidor, preparação da máquina, amostras, cortes, retrabalho, acabamento, despesas, impostos e margem. Mantenha esses itens explícitos para que uma contagem baixa não esconda uma preparação demorada.' },
35
+ { type: 'tip', title: 'Teste o plano antes de orçar', html: 'Faça uma amostra representativa no tecido e no estabilizador previstos. Compare o tempo e o comportamento da linha com a estimativa e ajuste suas hipóteses locais de velocidade e preparação.' },
36
+ { type: 'title', text: 'Leia o detalhamento da produção', level: 3 },
37
+ { type: 'list', items: ['Pontos equivalentes mostram a carga de cobertura depois da aplicação do fator.', 'Tempo de produção é igual aos pontos equivalentes divididos pela velocidade da máquina.', 'A preparação das cores acrescenta 1,75 minuto por troca depois da primeira.', 'A estimativa base é o tempo total dividido por 60 e multiplicado pelo valor por hora.'] },
38
+ ] };
@@ -0,0 +1,38 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import { bibliography } from '../bibliography';
4
+ import type { EmbroideryStitchPricingEstimatorUI } from '../ui';
5
+
6
+ const slug = 'kalkulyator-kolichestva-stitchkov-stoimosti-vyshivki';
7
+ const title = 'Калькулятор количества стежков и стоимости вышивки';
8
+ const description = 'Рассчитайте примерное количество стежков, время производства и базовую стоимость работы по размеру дизайна, плотности, цветам, скорости и ставке.';
9
+ const faq = [
10
+ { question: 'Как рассчитывается примерное количество стежков?', answer: 'Калькулятор умножает площадь вышивки на квадрат плотности стежков, а затем применяет коэффициент типа стежка. Это сопоставимая производственная нагрузка, а не прогноз файла для машины.' },
11
+ { question: 'Почему тип стежка меняет результат?', answer: 'Ходовой, гладьевый, заполняющий и татами стежки укладывают нить по-разному. Относительный коэффициент прозрачно показывает, что плотное покрытие требует больше производственной работы.' },
12
+ { question: 'Меняет ли число цветов количество стежков?', answer: 'Нет. Число цветов добавляет только время смены. Модель закладывает 1,75 минуты на каждую смену после первого цвета как плановое допущение.' },
13
+ { question: 'Является ли рассчитанная цена коммерческим предложением?', answer: 'Нет. Это оценка труда по введенной почасовой ставке. Нить, стабилизатор, оцифровка, настройка, отделка, накладные расходы, налоги и прибыль не включены.' },
14
+ ];
15
+ const howTo = [
16
+ { name: 'Измерьте дизайн', text: 'Введите ширину и высоту вышивки в сантиметрах. Используйте площадь со стежками, а не полный размер пялец.' },
17
+ { name: 'Опишите план стежков', text: 'Выберите основной тип стежка, укажите плотность в стежках на сантиметр и посчитайте цвета нити.' },
18
+ { name: 'Добавьте условия производства', text: 'Введите скорость машины в стежках в минуту и почасовую ставку труда. Используйте одну валюту.' },
19
+ { name: 'Прочитайте оценку', text: 'Сравнивайте дизайны по эквивалентным стежкам, времени производства, настройке и базовой стоимости до оцифровки или расчета цены.' },
20
+ ];
21
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
22
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
23
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'BusinessApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'ru' };
24
+ const ui: EmbroideryStitchPricingEstimatorUI = {
25
+ intro: 'Соберите план стежков в пяльцах и увидьте производственную нагрузку и стоимость труда.', stageLabel: 'Пяльцы для стежков', controlsLabel: 'Собрать план стежков', resultLabel: 'Базовая стоимость работы', widthLabel: 'Ширина дизайна', heightLabel: 'Высота дизайна', densityLabel: 'Плотность стежков', stitchLabel: 'Основной стежок', coloursLabel: 'Цвета нити', speedLabel: 'Скорость машины', rateLabel: 'Почасовая ставка', widthHint: 'площадь вышивки в см', heightHint: 'площадь вышивки в см', densityHint: 'стежков на см', coloursHint: 'первый цвет не требует смены', speedHint: 'стежков в минуту', rateHint: 'ваша валюта в час', stitchRunning: 'Ходовой стежок', stitchSatin: 'Гладьевой стежок', stitchFill: 'Заполняющий стежок', stitchTatami: 'Заполнение татами', stitchRunningNote: 'Легкие контуры и надписи с небольшим покрытием.', stitchSatinNote: 'Гладкие столбики и границы с направленным путем нити.', stitchFillNote: 'Сплошное покрытие, увеличивающее расход нити и работу.', stitchTatamiNote: 'Многослойное заполнение с самым высоким коэффициентом модели.', selectStitch: 'Выберите тип стежка', emptyResult: 'Введите план стежков, чтобы активировать пяльцы.', stitchesUnit: 'эквивалентных стежков', minutesUnit: 'минут', hoursUnit: 'часов', priceUnit: 'денежных единиц', areaLabel: 'Площадь вышивки', effectiveLabel: 'Эквивалентные стежки', stitchTimeLabel: 'Время вышивки', productionLabel: 'Время производства', setupLabel: 'Настройка цветов', changesLabel: 'Смены цвета', basePriceLabel: 'Базовая стоимость работы', estimateBadge: 'Оценка готова', attentionBadge: 'Проверьте масштаб', warningHighVolume: 'Большой объем стежков. Перед расчетом цены сделайте пробный образец на машине.', warningLongRun: 'Долгий производственный цикл. Отдельно проверьте запяливание, обрывы нити и отделку.', invalidMessage: 'Введите положительные значения размера, плотности, скорости и почасовой ставки.', formulaTitle: 'Что подсчитывается', formulaText: 'Площадь, умноженная на плотность в квадрате, дает основу покрытия. Коэффициент стежка превращает ее в эквивалентные стежки, а каждая смена после первого цвета добавляет 1,75 минуты.', noteTitle: 'Граница планирования', noteText: 'Это прозрачный оценочный калькулятор, а не оцифрованный файл машины и не готовое коммерческое предложение. Отдельно добавьте материалы, настройку, отделку, расходы, налоги и маржу.', reset: 'Сбросить пример', copied: 'Сводка скопирована', copySummary: 'Копировать сводку', canvasAlt: 'Тканевые пяльцы с линиями нити, показывающими текущую плотность',
26
+ };
27
+ export const content: ToolLocaleContent<EmbroideryStitchPricingEstimatorUI> = { slug, title, description, ui, faq, bibliography, howTo, schemas: [faqSchema, howToSchema, appSchema], seo: [
28
+ { type: 'title', text: 'Как оценить количество стежков и стоимость труда при вышивке', level: 2 },
29
+ { type: 'paragraph', html: 'У дизайна вышивки есть два вопроса планирования: сколько работы с нитью содержит мотив и как долго будут заняты машина и оператор? Этот калькулятор показывает оба параметра и превращает размер, плотность, стежок, цвета, скорость и ставку в сопоставимое плановое значение.' },
30
+ { type: 'title', text: 'Модель количества стежков', level: 3 },
31
+ { type: 'paragraph', html: 'В основе лежит площадь вышивки, умноженная на квадрат плотности в стежках на сантиметр. Относительный коэффициент учитывает семейство стежка: ходовой легче, гладьевой служит ориентиром, а заполнение и татами представляют все более плотные плановые случаи. Это оценка эквивалентной работы, а не точное число из программы оцифровки.' },
32
+ { type: 'table', headers: ['Ввод', 'Что меняется', 'Чего это не обещает'], rows: [['Ширина и высота', 'Площадь покрытия и базовое число стежков', 'Точный контур оцифрованного файла'], ['Плотность и тип стежка', 'Эквивалентная работа с нитью и время машины', 'Универсальную настройку качества для любой ткани'], ['Цвета', 'Время смены после первого цвета', 'Число обрезок или обрывов нити'], ['Скорость и ставка', 'Длительность производства и стоимость труда', 'Итоговую цену для клиента']] },
33
+ { type: 'title', text: 'Превратите оценку в ответственную цену', level: 3 },
34
+ { type: 'paragraph', html: 'Показанная цена учитывает только рабочее время и почасовую ставку. В реальное предложение нужно добавить оцифровку, нить, стабилизатор, запяливание, настройку машины, образцы, обрезку, переделки, отделку, накладные расходы, налоги и маржу. Разделяйте эти статьи, чтобы небольшое число стежков не скрывало долгую подготовку.' },
35
+ { type: 'tip', title: 'Проверьте план до расчета цены', html: 'Сделайте показательный образец на нужной ткани и стабилизаторе. Сравните время и поведение нити с оценкой, затем настройте местные допущения по скорости и подготовке.' },
36
+ { type: 'title', text: 'Как читать производственную разбивку', level: 3 },
37
+ { type: 'list', items: ['Эквивалентные стежки показывают нагрузку покрытия после применения коэффициента.', 'Время производства равно эквивалентным стежкам, деленным на скорость машины.', 'Настройка цветов добавляет 1,75 минуты за каждую смену после первой.', 'Базовая стоимость работы равна общему времени, деленному на 60 и умноженному на почасовую ставку.'] },
38
+ ] };
@@ -0,0 +1,38 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import { bibliography } from '../bibliography';
4
+ import type { EmbroideryStitchPricingEstimatorUI } from '../ui';
5
+
6
+ const slug = 'kalkylator-stygnadsantal-pris-broderi';
7
+ const title = 'Kalkylator för stygnantal och broderipris';
8
+ const description = 'Uppskatta stygnantal, produktionstid och ett grundpris för arbete utifrån motivets storlek, stygntäthet, färger, hastighet och timpris.';
9
+ const faq = [
10
+ { question: 'Hur fungerar uppskattningen av stygnantal?', answer: 'Kalkylatorn multiplicerar den broderade ytan med kvadraten av stygntätheten och använder sedan en faktor för stygnsorten. Resultatet är en jämförbar arbetsbelastning, inte en förutsägelse av en maskinfil.' },
11
+ { question: 'Varför ändrar stygnsorten uppskattningen?', answer: 'Löpstygn, satäng, fyllnad och tatami placerar tråden på olika sätt. Den relativa faktorn visar tydligt att tätare täckning kräver mer produktionsarbete.' },
12
+ { question: 'Ändrar antalet färger antalet stygn?', answer: 'Nej. Antalet färger lägger bara till bytestid. Modellen räknar 1,75 minuter för varje byte efter den första färgen som ett planeringsantagande.' },
13
+ { question: 'Är det beräknade priset en offert?', answer: 'Nej. Det är en uppskattning av arbetet utifrån det angivna timpriset. Tråd, stabilisering, digitalisering, förberedelse, efterarbete, omkostnader, skatt och marginal ingår inte.' },
14
+ ];
15
+ const howTo = [
16
+ { name: 'Mät motivet', text: 'Ange den broderade bredden och höjden i centimeter. Använd den sydda ytan och inte hela bågens storlek.' },
17
+ { name: 'Beskriv stygnplanen', text: 'Välj den dominerande stygnsorten, ange tätheten i stygn per centimeter och räkna trådfärgerna.' },
18
+ { name: 'Lägg till produktionsantaganden', text: 'Ange maskinhastigheten i stygn per minut och ditt timpris för arbete. Håll dig till en valuta.' },
19
+ { name: 'Läs planeringsvärdet', text: 'Använd motsvarande stygn, produktionstid, förberedelsetid och grundpris för att jämföra motiv före digitalisering eller offert.' },
20
+ ];
21
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
22
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
23
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'BusinessApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'sv' };
24
+ const ui: EmbroideryStitchPricingEstimatorUI = {
25
+ intro: 'Bygg en stygnplan i broderibågen och se produktionsarbetet och arbetsvärdet.', stageLabel: 'Broderibågen', controlsLabel: 'Bygg stygnplanen', resultLabel: 'Grundpris för arbete', widthLabel: 'Motivets bredd', heightLabel: 'Motivets höjd', densityLabel: 'Stygntäthet', stitchLabel: 'Dominerande stygn', coloursLabel: 'Trådfärger', speedLabel: 'Maskinhastighet', rateLabel: 'Timpris', widthHint: 'sydd yta i cm', heightHint: 'sydd yta i cm', densityHint: 'stygn per cm', coloursHint: 'första färgen kräver inget byte', speedHint: 'stygn per minut', rateHint: 'din valuta per timme', stitchRunning: 'Löpstygn', stitchSatin: 'Satängstygn', stitchFill: 'Fyllnadssöm', stitchTatami: 'Tatamifyllning', stitchRunningNote: 'Lätta konturer och bokstäver med låg täckning.', stitchSatinNote: 'Släta kolumner och kanter med en fokuserad trådbana.', stitchFillNote: 'Solid täckning som ökar trådåtgång och produktionsarbete.', stitchTatamiNote: 'Lagerfyllning med modellens högsta planeringsfaktor.', selectStitch: 'Välj en stygnsort', emptyResult: 'Ange en stygnplan för att aktivera bågen.', stitchesUnit: 'motsvarande stygn', minutesUnit: 'minuter', hoursUnit: 'timmar', priceUnit: 'valutaenheter', areaLabel: 'Broderad yta', effectiveLabel: 'Motsvarande stygn', stitchTimeLabel: 'Stygnstid', productionLabel: 'Produktionstid', setupLabel: 'Färgförberedelse', changesLabel: 'Färgbyten', basePriceLabel: 'Grundpris för arbete', estimateBadge: 'Uppskattning klar', attentionBadge: 'Kontrollera skalan', warningHighVolume: 'Stort stygnantal. Gör ett riktigt maskinprov före offert.', warningLongRun: 'Lång produktion. Kontrollera bågning, trådbrott och efterarbete separat.', invalidMessage: 'Använd positiva värden för storlek, täthet, hastighet och timpris.', formulaTitle: 'Det som räknas', formulaText: 'Yta gånger täthet i kvadrat ger en täckningsbas. Stygnfaktorn omvandlar den till motsvarande stygn och varje byte efter den första färgen lägger till 1,75 minuter.', noteTitle: 'Planeringsgräns', noteText: 'Detta är en transparent uppskattning, inte en digitaliserad maskinfil eller en färdig offert. Lägg själv till material, förberedelse, efterarbete, omkostnader, skatt och marginal.', reset: 'Återställ exempel', copied: 'Sammanfattning kopierad', copySummary: 'Kopiera sammanfattning', canvasAlt: 'Broderibåge av tyg med trådlinjer som visar aktuell täthet',
26
+ };
27
+ export const content: ToolLocaleContent<EmbroideryStitchPricingEstimatorUI> = { slug, title, description, ui, faq, bibliography, howTo, schemas: [faqSchema, howToSchema, appSchema], seo: [
28
+ { type: 'title', text: 'Så uppskattar du stygnantal och arbetspris för broderi', level: 2 },
29
+ { type: 'paragraph', html: 'Ett broderimotiv har två planeringsfrågor: hur mycket trådarbete motivet innehåller och hur länge maskin och operatör blir upptagna. Den här kalkylatorn håller båda synliga och omvandlar storlek, täthet, stygn, färger, hastighet och timpris till ett jämförbart planeringsvärde.' },
30
+ { type: 'title', text: 'Modellen för stygnantal', level: 3 },
31
+ { type: 'paragraph', html: 'Basen är den broderade ytan multiplicerad med kvadraten av tätheten i stygn per centimeter. En relativ faktor tar hänsyn till stygnfamiljen: löpstygn är lättare, satäng är referensen och fyllnad och tatami är allt tätare planeringsfall. Det är en uppskattning av motsvarande arbete, inte det exakta antal som ett digitaliseringsprogram exporterar.' },
32
+ { type: 'table', headers: ['Inmatning', 'Detta ändras', 'Detta utlovas inte'], rows: [['Bredd och höjd', 'Täckningsyta och basstygn', 'Den exakta konturen i en digitaliserad fil'], ['Täthet och stygnsort', 'Motsvarande trådarbete och maskintid', 'En universell kvalitetsinställning för alla tyger'], ['Färger', 'Bytestid efter första färgen', 'Antalet trådklipp eller trådbrott'], ['Hastighet och timpris', 'Produktionslängd och arbetsvärde', 'En slutlig kundoffert']] },
33
+ { type: 'title', text: 'Gör uppskattningen till ett ansvarsfullt pris', level: 3 },
34
+ { type: 'paragraph', html: 'Det visade priset är bara arbetstid multiplicerad med timpriset. En riktig offert bör lägga till digitalisering, tråd, stabilisering, uppspänning, maskininställning, provbitar, klippning, omarbete, efterarbete, omkostnader, skatt och marginal. Håll posterna synliga så att ett lågt stygnantal inte döljer en lång förberedelse.' },
35
+ { type: 'tip', title: 'Testa planen före offert', html: 'Kör ett representativt prov på avsett tyg och stabilisering. Jämför tid och trådbeteende med uppskattningen och justera dina lokala antaganden om hastighet och förberedelse.' },
36
+ { type: 'title', text: 'Läs produktionsuppdelningen', level: 3 },
37
+ { type: 'list', items: ['Motsvarande stygn visar täckningsarbetet efter att stygnfaktorn har använts.', 'Produktionstid är motsvarande stygn delat med maskinhastigheten.', 'Färgförberedelse lägger till 1,75 minuter för varje byte efter det första.', 'Grundpriset är den totala tiden delad med 60 och multiplicerad med timpriset.'] },
38
+ ] };
@@ -0,0 +1,38 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import { bibliography } from '../bibliography';
4
+ import type { EmbroideryStitchPricingEstimatorUI } from '../ui';
5
+
6
+ const slug = 'nakis-dikis-sayisi-fiyat-tahminleyici';
7
+ const title = 'Nakış dikiş sayısı ve fiyat tahminleyici';
8
+ const description = 'Tasarım boyutu, dikiş yoğunluğu, renk sayısı, hız ve saatlik ücrete göre nakış dikiş sayısını, üretim süresini ve temel işçilik bedelini tahmin edin.';
9
+ const faq = [
10
+ { question: 'Nakış dikiş sayısı tahmini nasıl çalışır?', answer: 'Tahminleyici, işlenen alanı dikiş yoğunluğunun karesiyle çarpar ve ardından dikiş türü katsayısını uygular. Sonuç, üretim yükünü karşılaştırmak içindir; makine dosyasının tahmini değildir.' },
11
+ { question: 'Dikiş türü tahmini neden değiştirir?', answer: 'Kontur, saten, dolgu ve tatami dikişleri ipliği farklı biçimlerde yerleştirir. Göreli katsayı, daha yoğun kaplamanın daha fazla üretim emeği istediğini açıkça gösterir.' },
12
+ { question: 'Renk sayısı dikiş sayısını değiştirir mi?', answer: 'Hayır. Renk sayısı yalnızca renk değişim süresi ekler. Model, ilk renkten sonraki her değişim için planlama varsayımı olarak 1,75 dakika ekler.' },
13
+ { question: 'Hesaplanan fiyat teklif midir?', answer: 'Hayır. Girilen saatlik ücrete göre temel işçilik tahminidir. İplik, tela, dijitalleştirme, hazırlık, bitirme, genel gider, vergi ve kâr dahil değildir.' },
14
+ ];
15
+ const howTo = [
16
+ { name: 'Tasarımı ölçün', text: 'İşlenecek genişlik ve yüksekliği santimetre olarak girin. Kasnağın tamamı yerine dikiş uygulanacak alanı kullanın.' },
17
+ { name: 'Dikiş planını tanımlayın', text: 'Baskın dikiş türünü seçin, santimetre başına yoğunluğu girin ve iplik renklerini sayın.' },
18
+ { name: 'Üretim varsayımlarını ekleyin', text: 'Dakikadaki dikiş sayısını ve saatlik işçilik ücretinizi girin. Tek bir para birimi kullanın.' },
19
+ { name: 'Planlama tahminini okuyun', text: 'Dijitalleştirme veya teklif öncesinde tasarımları eşdeğer dikiş, üretim süresi, hazırlık süresi ve temel fiyatla karşılaştırın.' },
20
+ ];
21
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
22
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
23
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'BusinessApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'tr' };
24
+ const ui: EmbroideryStitchPricingEstimatorUI = {
25
+ intro: 'Kasnak içindeki dikiş planınızı oluşturun, üretim yükünü ve işçilik değerini görün.', stageLabel: 'Dikiş kasnağı', controlsLabel: 'Dikiş planını oluşturun', resultLabel: 'Temel işçilik tahmini', widthLabel: 'Tasarım genişliği', heightLabel: 'Tasarım yüksekliği', densityLabel: 'Dikiş yoğunluğu', stitchLabel: 'Baskın dikiş', coloursLabel: 'İplik renkleri', speedLabel: 'Makine hızı', rateLabel: 'Saatlik ücret', widthHint: 'işlenen alan cm', heightHint: 'işlenen alan cm', densityHint: 'cm başına dikiş', coloursHint: 'ilk renk değişim gerektirmez', speedHint: 'dakikadaki dikiş', rateHint: 'saat başına para biriminiz', stitchRunning: 'Kontur dikişi', stitchSatin: 'Saten dikiş', stitchFill: 'Dolgu dikişi', stitchTatami: 'Tatami dolgusu', stitchRunningNote: 'Düşük kaplamalı hafif konturlar ve yazılar.', stitchSatinNote: 'Odaklanmış iplik yolu olan düzgün sütunlar ve kenarlar.', stitchFillNote: 'İplik ve üretim emeğini artıran yoğun kaplama.', stitchTatamiNote: 'Bu modeldeki en yüksek planlama katsayısına sahip katmanlı dolgu.', selectStitch: 'Dikiş türü seçin', emptyResult: 'Kasnağı etkinleştirmek için bir dikiş planı girin.', stitchesUnit: 'eşdeğer dikiş', minutesUnit: 'dakika', hoursUnit: 'saat', priceUnit: 'para birimi', areaLabel: 'İşlenen alan', effectiveLabel: 'Eşdeğer dikişler', stitchTimeLabel: 'Dikiş süresi', productionLabel: 'Üretim süresi', setupLabel: 'Renk hazırlığı', changesLabel: 'Renk değişimleri', basePriceLabel: 'Temel işçilik tahmini', estimateBadge: 'Tahmin hazır', attentionBadge: 'Ölçeği inceleyin', warningHighVolume: 'Dikiş hacmi yüksek. Teklif vermeden önce gerçek makine numunesi alın.', warningLongRun: 'Üretim süresi uzun. Kasnaklama, iplik kopmaları ve bitirme süresini ayrıca kontrol edin.', invalidMessage: 'Boyut, yoğunluk, hız ve saatlik ücret için pozitif değerler kullanın.', formulaTitle: 'Neler hesaplanır', formulaText: 'Alan ile yoğunluğun karesi kaplama tabanını oluşturur. Dikiş katsayısı bunu eşdeğer dikişlere çevirir ve ilk renkten sonraki her değişim 1,75 dakika ekler.', noteTitle: 'Planlama sınırı', noteText: 'Bu şeffaf bir tahmin aracıdır; dijitalleştirilmiş makine dosyası veya son teklif değildir. Malzeme, hazırlık, bitirme, genel gider, vergi ve kârı ayrıca ekleyin.', reset: 'Örneği sıfırla', copied: 'Özet kopyalandı', copySummary: 'Özeti kopyala', canvasAlt: 'Güncel yoğunluğu iplik çizgileriyle gösteren kumaş nakış kasnağı',
26
+ };
27
+ export const content: ToolLocaleContent<EmbroideryStitchPricingEstimatorUI> = { slug, title, description, ui, faq, bibliography, howTo, schemas: [faqSchema, howToSchema, appSchema], seo: [
28
+ { type: 'title', text: 'Nakış dikiş sayısı ve işçilik fiyatı nasıl tahmin edilir', level: 2 },
29
+ { type: 'paragraph', html: 'Bir nakış tasarımında iki planlama sorusu vardır: motifte ne kadar iplik işi bulunur ve makine ile operatör ne kadar süre meşgul olur? Bu tahminleyici iki konuyu görünür tutar ve boyut, yoğunluk, dikiş, renk, hız ve ücreti karşılaştırılabilir bir planlama değerine dönüştürür.' },
30
+ { type: 'title', text: 'Dikiş sayısı modeli', level: 3 },
31
+ { type: 'paragraph', html: 'Temel değer, işlenen alanın santimetre başına dikiş yoğunluğunun karesiyle çarpılmasıdır. Göreli katsayı dikiş ailesini hesaba katar: kontur daha hafif, saten referans, dolgu ve tatami ise giderek yoğunlaşan planlama durumlarıdır. Bu, eşdeğer iş yükü tahminidir; dijitalleştirme yazılımının verdiği kesin sayı değildir.' },
32
+ { type: 'table', headers: ['Girdi', 'Neyi değiştirir', 'Neyi garanti etmez'], rows: [['Genişlik ve yükseklik', 'Kaplama alanı ve temel dikiş sayısı', 'Dijital dosyanın kesin dış çizgisi'], ['Yoğunluk ve dikiş türü', 'Eşdeğer iplik işi ve makine süresi', 'Her kumaş için evrensel kalite ayarı'], ['Renkler', 'İlk renkten sonraki değişim süresi', 'Kesim veya iplik kopması sayısı'], ['Hız ve ücret', 'Üretim süresi ve işçilik değeri', 'Müşteriye verilecek son teklif']] },
33
+ { type: 'title', text: 'Tahmini sorumlu bir fiyata dönüştürün', level: 3 },
34
+ { type: 'paragraph', html: 'Gösterilen fiyat yalnızca çalışma süresinin saatlik ücretle çarpımıdır. Gerçek bir teklif dijitalleştirme, iplik, tela, kasnaklama, makine hazırlığı, numune, kesim, yeniden işleme, bitirme, genel gider, vergi ve kârı eklemelidir. Düşük dikiş sayısının uzun hazırlığı gizlememesi için kalemleri ayrı tutun.' },
35
+ { type: 'tip', title: 'Tekliften önce planı test edin', html: 'Kullanılacak kumaş ve tela üzerinde temsilî bir numune çalıştırın. Süreyi ve iplik davranışını tahminle karşılaştırıp yerel hız ve hazırlık varsayımlarınızı ayarlayın.' },
36
+ { type: 'title', text: 'Üretim dökümünü okuma', level: 3 },
37
+ { type: 'list', items: ['Eşdeğer dikişler, katsayı uygulandıktan sonraki kaplama işini gösterir.', 'Üretim süresi, eşdeğer dikişlerin makine hızına bölünmesidir.', 'Renk hazırlığı ilk renkten sonraki her değişim için 1,75 dakika ekler.', 'Temel işçilik tahmini, toplam sürenin 60a bölünüp saatlik ücretle çarpılmasıdır.'] },
38
+ ] };
@@ -0,0 +1,38 @@
1
+ import type { FAQPage, HowTo, SoftwareApplication, WithContext } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import { bibliography } from '../bibliography';
4
+ import type { EmbroideryStitchPricingEstimatorUI } from '../ui';
5
+
6
+ const slug = 'embroidery-stitch-count-pricing-estimator';
7
+ const title = '刺绣针数与价格估算器';
8
+ const description = '根据设计尺寸、针迹密度、颜色数量、速度和小时费率,估算刺绣针数、生产时间和基础人工成本。';
9
+ const faq = [
10
+ { question: '刺绣针数估算是如何工作的?', answer: '计算器将刺绣面积乘以针迹密度的平方,再应用针迹类型系数。结果是用于比较生产工作量的等效针数,不是机器文件的预测。' },
11
+ { question: '为什么针迹类型会改变估算结果?', answer: '走针、缎面针、填充针和榻榻米针的走线方式不同。相对系数透明地表示覆盖越密,生产工作量越大。' },
12
+ { question: '颜色数量会改变针数吗?', answer: '不会。颜色数量只会增加换色时间。模型将第一种颜色之后的每次换色按计划假设增加1.75分钟。' },
13
+ { question: '计算出的价格是正式报价吗?', answer: '不是。这是根据输入小时费率计算的基础人工成本估算,不包含线材、衬纸、打版、准备、收尾、间接费用、税费和利润。' },
14
+ ];
15
+ const howTo = [
16
+ { name: '测量设计', text: '输入刺绣宽度和高度,单位为厘米。使用实际走针区域,而不是整个绷框尺寸。' },
17
+ { name: '描述针迹计划', text: '选择主要针迹类型,输入每厘米针迹密度,并统计线材颜色数量。' },
18
+ { name: '加入生产假设', text: '输入每分钟针数和每小时人工费率。请保持货币单位一致。' },
19
+ { name: '读取计划估算', text: '在打版或报价前,使用等效针数、生产时间、准备时间和基础价格比较不同设计。' },
20
+ ];
21
+ const faqSchema: WithContext<FAQPage> = { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) };
22
+ const howToSchema: WithContext<HowTo> = { '@context': 'https://schema.org', '@type': 'HowTo', name: title, description, step: howTo.map((step) => ({ '@type': 'HowToStep', name: step.name, text: step.text })) };
23
+ const appSchema: WithContext<SoftwareApplication> = { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: title, description, applicationCategory: 'BusinessApplication', operatingSystem: 'All', offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' }, inLanguage: 'zh' };
24
+ const ui: EmbroideryStitchPricingEstimatorUI = {
25
+ intro: '在刺绣绷框中制定针迹计划,查看生产工作量和人工价值。', stageLabel: '针迹绷框', controlsLabel: '制定针迹计划', resultLabel: '基础人工成本估算', widthLabel: '设计宽度', heightLabel: '设计高度', densityLabel: '针迹密度', stitchLabel: '主要针迹', coloursLabel: '线材颜色', speedLabel: '机器速度', rateLabel: '小时费率', widthHint: '刺绣区域,单位厘米', heightHint: '刺绣区域,单位厘米', densityHint: '每厘米针数', coloursHint: '第一种颜色无需换色', speedHint: '每分钟针数', rateHint: '每小时货币金额', stitchRunning: '走针', stitchSatin: '缎面针', stitchFill: '填充针', stitchTatami: '榻榻米填充', stitchRunningNote: '覆盖较少的轻量轮廓和文字。', stitchSatinNote: '线迹集中且平滑的柱状区域和边缘。', stitchFillNote: '增加线材用量和生产工作的完整覆盖。', stitchTatamiNote: '本模型中计划系数最高的分层填充。', selectStitch: '选择针迹类型', emptyResult: '输入针迹计划以激活绷框。', stitchesUnit: '等效针数', minutesUnit: '分钟', hoursUnit: '小时', priceUnit: '货币单位', areaLabel: '刺绣面积', effectiveLabel: '等效针数', stitchTimeLabel: '走针时间', productionLabel: '生产时间', setupLabel: '颜色准备', changesLabel: '换色次数', basePriceLabel: '基础人工成本估算', estimateBadge: '估算完成', attentionBadge: '检查规模', warningHighVolume: '针数较多。报价前请进行真实机器打样。', warningLongRun: '生产时间较长。请单独检查绷框、断线和收尾时间。', invalidMessage: '尺寸、密度、速度和小时费率必须使用正数。', formulaTitle: '计算内容', formulaText: '面积乘以密度平方得到覆盖基准。针迹系数将其转换为等效针数,第一种颜色之后的每次换色增加1.75分钟。', noteTitle: '计划估算范围', noteText: '这是透明的估算工具,不是已打版的机器文件,也不是最终报价。请自行加入材料、准备、收尾、间接费用、税费和利润。', reset: '重置示例', copied: '摘要已复制', copySummary: '复制摘要', canvasAlt: '用线条显示当前密度的布质刺绣绷框',
26
+ };
27
+ export const content: ToolLocaleContent<EmbroideryStitchPricingEstimatorUI> = { slug, title, description, ui, faq, bibliography, howTo, schemas: [faqSchema, howToSchema, appSchema], seo: [
28
+ { type: 'title', text: '如何估算刺绣针数和人工价格', level: 2 },
29
+ { type: 'paragraph', html: '刺绣设计有两个计划问题:图案包含多少线材工作,以及机器和操作员将被占用多久?这个估算器让两者保持可见,并将尺寸、密度、针迹、颜色、速度和费率转换为可比较的计划数值。' },
30
+ { type: 'title', text: '针数模型', level: 3 },
31
+ { type: 'paragraph', html: '基础值是刺绣面积乘以每厘米密度的平方。相对系数用于考虑针迹系列:走针较轻,缎面针作为参考,填充针和榻榻米针代表越来越密的计划情况。这是等效工作量估算,不是打版软件导出的精确针数。' },
32
+ { type: 'table', headers: ['输入', '会改变什么', '不会承诺什么'], rows: [['宽度和高度', '覆盖面积与基础针数', '打版文件的精确轮廓'], ['密度和针迹类型', '等效线材工作与机器时间', '适合所有面料的通用质量设置'], ['颜色', '第一种颜色之后的换色时间', '剪线或断线次数'], ['速度和费率', '生产时长与人工价值', '给客户的最终报价']] },
33
+ { type: 'title', text: '将估算变成负责任的价格', level: 3 },
34
+ { type: 'paragraph', html: '显示价格只包含工作时间乘以小时费率。实际报价还应加入打版、线材、衬纸、绷框、机器准备、打样、剪线、返工、收尾、间接费用、税费和利润。将这些项目分开,避免低针数掩盖漫长的准备工作。' },
35
+ { type: 'tip', title: '报价前测试计划', html: '在计划使用的面料和衬纸上进行有代表性的打样。将样品时间和线材表现与估算比较,再调整本地速度和准备假设。' },
36
+ { type: 'title', text: '读取生产明细', level: 3 },
37
+ { type: 'list', items: ['等效针数表示应用针迹系数后的覆盖工作量。', '生产时间等于等效针数除以机器速度。', '颜色准备在第一种颜色之后的每次换色增加1.75分钟。', '基础人工成本等于总时间除以60,再乘以小时费率。'] },
38
+ ] };
@@ -0,0 +1,11 @@
1
+ import type { ToolDefinition } from '../../types';
2
+ import { embroideryStitchPricingEstimator } from './entry';
3
+
4
+ export * from './entry';
5
+
6
+ export const EMBROIDERY_STITCH_PRICING_ESTIMATOR_TOOL: ToolDefinition = {
7
+ entry: embroideryStitchPricingEstimator,
8
+ Component: () => import('./component.astro'),
9
+ SEOComponent: () => import('./seo.astro'),
10
+ BibliographyComponent: () => import('./bibliography.astro'),
11
+ };
@@ -0,0 +1,40 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ calculateEmbroidery,
4
+ DEFAULT_INPUT,
5
+ formatMinutes,
6
+ formatNumber,
7
+ } from './logic';
8
+
9
+ describe('calculateEmbroidery', () => {
10
+ it('calculates equivalent stitches, setup time, and price', () => {
11
+ const result = calculateEmbroidery(DEFAULT_INPUT);
12
+ expect(result).not.toBeNull();
13
+ expect(result?.areaCm2).toBe(80);
14
+ expect(result?.baseStitches).toBe(2000);
15
+ expect(result?.effectiveStitches).toBe(2000);
16
+ expect(result?.colourChanges).toBe(1);
17
+ expect(result?.setupMinutes).toBe(1.75);
18
+ expect(result?.basePrice).toBeCloseTo(1.8429, 3);
19
+ });
20
+
21
+ it('uses the selected stitch multiplier and clamps colour count', () => {
22
+ const result = calculateEmbroidery({ ...DEFAULT_INPUT, stitchType: 'tatami', colours: 0 });
23
+ expect(result?.effectiveStitches).toBe(2900);
24
+ expect(result?.colourChanges).toBe(0);
25
+ });
26
+
27
+ it('returns null for non-positive production inputs', () => {
28
+ expect(calculateEmbroidery({ ...DEFAULT_INPUT, widthCm: 0 })).toBeNull();
29
+ expect(calculateEmbroidery({ ...DEFAULT_INPUT, speed: 0 })).toBeNull();
30
+ expect(calculateEmbroidery({ ...DEFAULT_INPUT, hourlyRate: -1 })).toBeNull();
31
+ });
32
+ });
33
+
34
+ describe('formatters', () => {
35
+ it('formats whole numbers and short durations', () => {
36
+ expect(formatNumber(1200)).toBe('1,200');
37
+ expect(formatMinutes(12.4)).toBe('12.4 min');
38
+ expect(formatMinutes(74)).toBe('1h 14m');
39
+ });
40
+ });
@@ -0,0 +1,86 @@
1
+ export type StitchType = 'running' | 'satin' | 'fill' | 'tatami';
2
+
3
+ export interface EmbroideryInput {
4
+ widthCm: number;
5
+ heightCm: number;
6
+ density: number;
7
+ stitchType: StitchType;
8
+ colours: number;
9
+ speed: number;
10
+ hourlyRate: number;
11
+ }
12
+
13
+ export interface EmbroideryCalculation {
14
+ areaCm2: number;
15
+ baseStitches: number;
16
+ effectiveStitches: number;
17
+ stitchMinutes: number;
18
+ colourChanges: number;
19
+ setupMinutes: number;
20
+ totalMinutes: number;
21
+ hours: number;
22
+ basePrice: number;
23
+ multiplier: number;
24
+ }
25
+
26
+ export const STITCH_MULTIPLIERS: Record<StitchType, number> = {
27
+ running: 0.55,
28
+ satin: 1,
29
+ fill: 1.25,
30
+ tatami: 1.45,
31
+ };
32
+
33
+ export const DEFAULT_INPUT: EmbroideryInput = {
34
+ widthCm: 10,
35
+ heightCm: 8,
36
+ density: 5,
37
+ stitchType: 'satin',
38
+ colours: 2,
39
+ speed: 700,
40
+ hourlyRate: 24,
41
+ };
42
+
43
+ function isFinitePositive(value: number): boolean {
44
+ return Number.isFinite(value) && value > 0;
45
+ }
46
+
47
+ function normaliseColours(value: number): number {
48
+ return Math.max(1, Math.round(value));
49
+ }
50
+
51
+ export function calculateEmbroidery(input: EmbroideryInput): EmbroideryCalculation | null {
52
+ if (!isFinitePositive(input.widthCm) || !isFinitePositive(input.heightCm)) return null;
53
+ if (!isFinitePositive(input.density) || !isFinitePositive(input.speed)) return null;
54
+ if (!isFinitePositive(input.hourlyRate)) return null;
55
+ const multiplier = STITCH_MULTIPLIERS[input.stitchType] ?? STITCH_MULTIPLIERS.satin;
56
+ const areaCm2 = input.widthCm * input.heightCm;
57
+ const baseStitches = Math.round(areaCm2 * input.density * input.density);
58
+ const effectiveStitches = Math.round(baseStitches * multiplier);
59
+ const colourChanges = Math.max(0, normaliseColours(input.colours) - 1);
60
+ const setupMinutes = colourChanges * 1.75;
61
+ const stitchMinutes = effectiveStitches / input.speed;
62
+ const totalMinutes = stitchMinutes + setupMinutes;
63
+ return {
64
+ areaCm2,
65
+ baseStitches,
66
+ effectiveStitches,
67
+ stitchMinutes,
68
+ colourChanges,
69
+ setupMinutes,
70
+ totalMinutes,
71
+ hours: totalMinutes / 60,
72
+ basePrice: (totalMinutes / 60) * input.hourlyRate,
73
+ multiplier,
74
+ };
75
+ }
76
+
77
+ export function formatNumber(value: number, maximumFractionDigits = 0): string {
78
+ return new Intl.NumberFormat('en-US', { maximumFractionDigits }).format(value);
79
+ }
80
+
81
+ export function formatMinutes(value: number): string {
82
+ if (value < 60) return `${formatNumber(value, 1)} min`;
83
+ const hours = Math.floor(value / 60);
84
+ const minutes = Math.round(value % 60);
85
+ return `${hours}h ${minutes}m`;
86
+ }
@@ -0,0 +1,15 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { embroideryStitchPricingEstimator } 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 embroideryStitchPricingEstimator.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
@@ -0,0 +1,20 @@
1
+ import type { EmbroideryInput } from './logic';
2
+
3
+ const STORAGE_KEY = 'jjlmoya-embroidery-stitch-pricing:v1';
4
+
5
+ export function loadEmbroideryInput(fallback: EmbroideryInput): EmbroideryInput {
6
+ try {
7
+ const raw = window.localStorage.getItem(STORAGE_KEY);
8
+ if (!raw) return fallback;
9
+ const saved = JSON.parse(raw) as Partial<EmbroideryInput>;
10
+ return { ...fallback, ...saved };
11
+ } catch {
12
+ return fallback;
13
+ }
14
+ }
15
+
16
+ export function saveEmbroideryInput(input: EmbroideryInput): void {
17
+ try {
18
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(input));
19
+ } catch {}
20
+ }
@@ -0,0 +1,53 @@
1
+ export interface EmbroideryStitchPricingEstimatorUI {
2
+ intro: string;
3
+ stageLabel: string;
4
+ controlsLabel: string;
5
+ resultLabel: string;
6
+ widthLabel: string;
7
+ heightLabel: string;
8
+ densityLabel: string;
9
+ stitchLabel: string;
10
+ coloursLabel: string;
11
+ speedLabel: string;
12
+ rateLabel: string;
13
+ widthHint: string;
14
+ heightHint: string;
15
+ densityHint: string;
16
+ coloursHint: string;
17
+ speedHint: string;
18
+ rateHint: string;
19
+ stitchRunning: string;
20
+ stitchSatin: string;
21
+ stitchFill: string;
22
+ stitchTatami: string;
23
+ stitchRunningNote: string;
24
+ stitchSatinNote: string;
25
+ stitchFillNote: string;
26
+ stitchTatamiNote: string;
27
+ selectStitch: string;
28
+ emptyResult: string;
29
+ stitchesUnit: string;
30
+ minutesUnit: string;
31
+ hoursUnit: string;
32
+ priceUnit: string;
33
+ areaLabel: string;
34
+ effectiveLabel: string;
35
+ stitchTimeLabel: string;
36
+ productionLabel: string;
37
+ setupLabel: string;
38
+ changesLabel: string;
39
+ basePriceLabel: string;
40
+ estimateBadge: string;
41
+ attentionBadge: string;
42
+ warningHighVolume: string;
43
+ warningLongRun: string;
44
+ invalidMessage: string;
45
+ formulaTitle: string;
46
+ formulaText: string;
47
+ noteTitle: string;
48
+ noteText: string;
49
+ reset: string;
50
+ copied: string;
51
+ copySummary: string;
52
+ canvasAlt: string;
53
+ }