@jjlmoya/utils-home 1.36.0 → 1.37.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-home",
3
- "version": "1.36.0",
3
+ "version": "1.37.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -0,0 +1,79 @@
1
+ import { readFileSync, writeFileSync } from 'fs';
2
+
3
+ function toCyrillic(text) {
4
+ let result = '';
5
+ let i = 0;
6
+
7
+ while (i < text.length) {
8
+ const c4 = text.slice(i, i + 4);
9
+ if (c4.length === 4) {
10
+ const t = { 'shch':'щ', 'SHCH':'Щ', 'Shch':'Щ' };
11
+ if (t[c4]) { result += t[c4]; i += 4; continue; }
12
+ }
13
+
14
+ if (i + 2 <= text.length) {
15
+ const two = text.slice(i, i + 2);
16
+
17
+ if (two === 'ya' || two === 'YA' || two === 'Ya') { result += ({'ya':'я','YA':'Я','Ya':'Я'})[two]; i += 2; continue; }
18
+ if (two === 'ye' || two === 'YE' || two === 'Ye') { result += ({'ye':'е','YE':'Е','Ye':'Е'})[two]; i += 2; continue; }
19
+ if (two === 'yo' || two === 'YO' || two === 'Yo') { result += ({'yo':'ё','YO':'Ё','Yo':'Ё'})[two]; i += 2; continue; }
20
+ if (two === 'yu' || two === 'YU' || two === 'Yu') { result += ({'yu':'ю','YU':'Ю','Yu':'Ю'})[two]; i += 2; continue; }
21
+
22
+ if (two === 'zh' || two === 'ZH' || two === 'Zh') { result += ({'zh':'ж','ZH':'Ж','Zh':'Ж'})[two]; i += 2; continue; }
23
+ if (two === 'ch' || two === 'CH' || two === 'Ch') { result += ({'ch':'ч','CH':'Ч','Ch':'Ч'})[two]; i += 2; continue; }
24
+ if (two === 'sh' || two === 'SH' || two === 'Sh') { result += ({'sh':'ш','SH':'Ш','Sh':'Ш'})[two]; i += 2; continue; }
25
+ if (two === 'kh' || two === 'KH' || two === 'Kh') { result += ({'kh':'х','KH':'Х','Kh':'Х'})[two]; i += 2; continue; }
26
+ if (two === 'ts' || two === 'TS' || two === 'Ts') { result += ({'ts':'ц','TS':'Ц','Ts':'Ц'})[two]; i += 2; continue; }
27
+
28
+ const next = text[i + 2] || '';
29
+ if (two === 'iy' && next !== 'a' && next !== 'e' && next !== 'o' && next !== 'u') { result += 'ий'; i += 2; continue; }
30
+ if (two === 'yy' && next !== 'a' && next !== 'e' && next !== 'o' && next !== 'u') { result += 'ый'; i += 2; continue; }
31
+ if (two === 'oy' && next !== 'a' && next !== 'e' && next !== 'o' && next !== 'u') { result += 'ой'; i += 2; continue; }
32
+ if (two === 'ay' && next !== 'a' && next !== 'e' && next !== 'o' && next !== 'u') { result += 'ай'; i += 2; continue; }
33
+ if (two === 'ey' && next !== 'a' && next !== 'e' && next !== 'o' && next !== 'u') { result += 'ей'; i += 2; continue; }
34
+ }
35
+
36
+ const c = text[i];
37
+ const smap = {
38
+ 'A':'А','a':'а','B':'Б','b':'б','V':'В','v':'в','G':'Г','g':'г',
39
+ 'D':'Д','d':'д','E':'Э','e':'е','Z':'З','z':'з','I':'И','i':'и',
40
+ 'K':'К','k':'к','L':'Л','l':'л','M':'М','m':'м','N':'Н','n':'н',
41
+ 'O':'О','o':'о','P':'П','p':'п','R':'Р','r':'р','S':'С','s':'с',
42
+ 'T':'Т','t':'т','U':'У','u':'у','F':'Ф','f':'ф','C':'Ц','c':'ц',
43
+ 'H':'Х','h':'х','Y':'Ы','y':'ы','J':'Й','j':'й',
44
+ };
45
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
46
+ result += smap[c] || c;
47
+ } else {
48
+ result += c;
49
+ }
50
+ i++;
51
+ }
52
+
53
+ result = result.replace(/WиФи/gi, 'WiFi');
54
+ result = result.replace(/Wи-Фи/gi, 'Wi-Fi');
55
+
56
+ return result;
57
+ }
58
+
59
+ function countLatin(s) {
60
+ return (s.match(/[a-zA-Z]/g) || []).length;
61
+ }
62
+
63
+ const filePath = process.argv[2];
64
+ let content = readFileSync(filePath, 'utf-8');
65
+
66
+ content = content.replace(/'([^']*)'/g, (full, text) => {
67
+ if (text.length < 5) return full;
68
+ if (/^(http|\/|\.|#|@|mdi:)/.test(text)) return full;
69
+ if (/^[0-9+\-.,%°\s]+$/.test(text)) return full;
70
+ if (/^(FAQPage|HowTo|SoftwareApplication|UtilityApplication|Question|Answer|HowToStep|Offer|schema-dts)$/.test(text)) return full;
71
+
72
+ const latin = countLatin(text);
73
+ if (latin < 5) return full;
74
+
75
+ return "'" + toCyrillic(text) + "'";
76
+ });
77
+
78
+ writeFileSync(filePath, content, 'utf-8');
79
+ console.log('Done');
@@ -78,6 +78,7 @@ const { title, currentLocale = "es", localeUrls = {}, hasSidebar = false } = Ast
78
78
  transition:
79
79
  background-color 0.3s ease,
80
80
  color 0.3s ease;
81
+ font-family: Inter, sans-serif;
81
82
  }
82
83
 
83
84
  main {
@@ -0,0 +1,118 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+
4
+ type LocaleWithDiacritics = keyof typeof DIACRITIC_RULES;
5
+
6
+ const DIACRITIC_RULES = {
7
+ de: {
8
+ language: 'German',
9
+ expectedCharacters: 'ä ö ü ß',
10
+ characters: /[äöüÄÖÜß]/g,
11
+ minPerThousandLetters: 0.1,
12
+ },
13
+ es: {
14
+ language: 'Spanish',
15
+ expectedCharacters: 'á é í ó ú ü ñ',
16
+ characters: /[áéíóúüñÁÉÍÓÚÜÑ]/g,
17
+ minPerThousandLetters: 0.1,
18
+ },
19
+ fr: {
20
+ language: 'French',
21
+ expectedCharacters: 'à â æ ç é è ê ë î ï ô œ ù û ü ÿ',
22
+ characters: /[àâæçéèêëîïôœùûüÿÀÂÆÇÉÈÊËÎÏÔŒÙÛÜŸ]/g,
23
+ minPerThousandLetters: 0.1,
24
+ },
25
+ it: {
26
+ language: 'Italian',
27
+ expectedCharacters: 'à è é ì í î ò ó ù ú',
28
+ characters: /[àèéìíîòóùúÀÈÉÌÍÎÒÓÙÚ]/g,
29
+ minPerThousandLetters: 0.1,
30
+ },
31
+ pl: {
32
+ language: 'Polish',
33
+ expectedCharacters: 'ą ć ę ł ń ó ś ź ż',
34
+ characters: /[ąćęłńóśźżĄĆĘŁŃÓŚŹŻ]/g,
35
+ minPerThousandLetters: 0.1,
36
+ },
37
+ pt: {
38
+ language: 'Portuguese',
39
+ expectedCharacters: 'á â ã à ç é ê í ó ô õ ú ü',
40
+ characters: /[áâãàçéêíóôõúüÁÂÃÀÇÉÊÍÓÔÕÚÜ]/g,
41
+ minPerThousandLetters: 0.1,
42
+ },
43
+ sv: {
44
+ language: 'Swedish',
45
+ expectedCharacters: 'å ä ö',
46
+ characters: /[åäöÅÄÖ]/g,
47
+ minPerThousandLetters: 0.1,
48
+ },
49
+ tr: {
50
+ language: 'Turkish',
51
+ expectedCharacters: 'ç ğ ı İ ö ş ü',
52
+ characters: /[çğıöşüÇĞİÖŞÜ]/g,
53
+ minPerThousandLetters: 0.1,
54
+ },
55
+ } as const;
56
+
57
+ const LETTERS = /\p{L}/gu;
58
+ const TRANSLATABLE_KEYS = ['title', 'description', 'ui', 'seo', 'faq', 'howTo'] as const;
59
+
60
+ function collectStrings(value: unknown): string[] {
61
+ if (typeof value === 'string') return [value];
62
+ if (!value || typeof value !== 'object') return [];
63
+ if (Array.isArray(value)) return value.flatMap(collectStrings);
64
+ return Object.values(value).flatMap(collectStrings);
65
+ }
66
+
67
+ function normalizeText(value: unknown): string {
68
+ return collectStrings(value).join(' ').normalize('NFC');
69
+ }
70
+
71
+ function translatableContent(content: Record<string, unknown>) {
72
+ return TRANSLATABLE_KEYS.map((key) => content[key]);
73
+ }
74
+
75
+ function letterCount(text: string): number {
76
+ return text.match(LETTERS)?.length ?? 0;
77
+ }
78
+
79
+ function diacriticCount(text: string, locale: LocaleWithDiacritics): number {
80
+ return text.match(DIACRITIC_RULES[locale].characters)?.length ?? 0;
81
+ }
82
+
83
+ function diacriticsPerThousandLetters(text: string, locale: LocaleWithDiacritics): number {
84
+ const letters = letterCount(text);
85
+ if (letters === 0) return 0;
86
+ return diacriticCount(text, locale) / letters * 1000;
87
+ }
88
+
89
+ describe('Diacritics density validation', () => {
90
+ ALL_TOOLS.forEach((tool) => {
91
+ describe(`Tool: ${tool.entry.id}`, () => {
92
+ Object.keys(DIACRITIC_RULES).forEach((locale) => {
93
+ it(`${locale} keeps the expected accent and special-letter set`, async () => {
94
+ const typedLocale = locale as LocaleWithDiacritics;
95
+ const loader = tool.entry.i18n[typedLocale];
96
+ if (!loader) return;
97
+
98
+ const content = await loader();
99
+ const text = normalizeText(translatableContent(content as Record<string, unknown>));
100
+ const rule = DIACRITIC_RULES[typedLocale];
101
+ const letters = letterCount(text);
102
+ const matches = diacriticCount(text, typedLocale);
103
+ const density = diacriticsPerThousandLetters(text, typedLocale);
104
+
105
+ expect(
106
+ density,
107
+ [
108
+ `Possible spelling or encoding issue detected in ${tool.entry.id}/${typedLocale} (${rule.language}).`,
109
+ `The text has ${matches} special characters (${density.toFixed(2)} per 1000 letters, ${letters} letters analyzed).`,
110
+ `This locale should include some of these characters: ${rule.expectedCharacters}.`,
111
+ 'If the count is 0 or near 0, accents, tildes, or special letters were probably stripped by encoding or normalization.',
112
+ ].join(' '),
113
+ ).toBeGreaterThanOrEqual(rule.minPerThousandLetters);
114
+ });
115
+ });
116
+ });
117
+ });
118
+ });
@@ -0,0 +1,84 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+
4
+ const INVERTED_PUNCTUATION_LOCALES = {
5
+ es: {
6
+ language: 'Spanish',
7
+ questionStart: '¿',
8
+ questionEnd: '?',
9
+ exclamationStart: '¡',
10
+ exclamationEnd: '!',
11
+ },
12
+ } as const;
13
+
14
+ type InvertedPunctuationLocale = keyof typeof INVERTED_PUNCTUATION_LOCALES;
15
+
16
+ const TRANSLATABLE_KEYS = ['title', 'description', 'ui', 'seo', 'faq', 'howTo'] as const;
17
+
18
+ function collectStrings(value: unknown): string[] {
19
+ if (typeof value === 'string') return [value];
20
+ if (!value || typeof value !== 'object') return [];
21
+ if (Array.isArray(value)) return value.flatMap(collectStrings);
22
+ return Object.values(value).flatMap(collectStrings);
23
+ }
24
+
25
+ function translatableStrings(content: Record<string, unknown>): string[] {
26
+ return TRANSLATABLE_KEYS.flatMap((key) => collectStrings(content[key]));
27
+ }
28
+
29
+ function sentenceStart(text: string, endIndex: number): string {
30
+ const beforeMark = text.slice(0, endIndex).trimEnd();
31
+ const boundary = Math.max(
32
+ beforeMark.lastIndexOf('.'),
33
+ beforeMark.lastIndexOf(':'),
34
+ beforeMark.lastIndexOf(';'),
35
+ beforeMark.lastIndexOf('\n'),
36
+ );
37
+
38
+ return beforeMark.slice(boundary + 1).trimStart();
39
+ }
40
+
41
+ function findMissingInvertedMarks(
42
+ text: string,
43
+ startMark: string,
44
+ endMark: string,
45
+ ): string[] {
46
+ return [...text.matchAll(new RegExp(`\\${endMark}`, 'g'))]
47
+ .map((match) => sentenceStart(text, match.index ?? 0))
48
+ .filter((segment) => segment.length > 0 && !segment.includes(startMark))
49
+ .map((segment) => `${segment}${endMark}`);
50
+ }
51
+
52
+ describe('Inverted punctuation validation', () => {
53
+ ALL_TOOLS.forEach((tool) => {
54
+ describe(`Tool: ${tool.entry.id}`, () => {
55
+ Object.keys(INVERTED_PUNCTUATION_LOCALES).forEach((locale) => {
56
+ it(`${locale} uses opening punctuation marks for questions and exclamations`, async () => {
57
+ const typedLocale = locale as InvertedPunctuationLocale;
58
+ const loader = tool.entry.i18n[typedLocale];
59
+ if (!loader) return;
60
+
61
+ const rule = INVERTED_PUNCTUATION_LOCALES[typedLocale];
62
+ const content = await loader();
63
+ const strings = translatableStrings(content as Record<string, unknown>);
64
+ const missingQuestions = strings.flatMap((text) =>
65
+ findMissingInvertedMarks(text, rule.questionStart, rule.questionEnd)
66
+ );
67
+ const missingExclamations = strings.flatMap((text) =>
68
+ findMissingInvertedMarks(text, rule.exclamationStart, rule.exclamationEnd)
69
+ );
70
+ const failures = [...missingQuestions, ...missingExclamations];
71
+
72
+ expect(
73
+ failures,
74
+ [
75
+ `Missing opening punctuation marks in ${tool.entry.id}/${typedLocale} (${rule.language}).`,
76
+ `Questions must use ${rule.questionStart}...${rule.questionEnd} and exclamations must use ${rule.exclamationStart}...${rule.exclamationEnd}.`,
77
+ `Examples: ${failures.slice(0, 5).join(' | ')}`,
78
+ ].join(' '),
79
+ ).toHaveLength(0);
80
+ });
81
+ });
82
+ });
83
+ });
84
+ });
@@ -0,0 +1,94 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+
4
+ type ScriptLocale = keyof typeof SCRIPT_RULES;
5
+
6
+ const SCRIPT_RULES = {
7
+ ja: {
8
+ language: 'Japanese',
9
+ scriptName: 'kana/kanji',
10
+ scriptCharacters: /[\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Han}]/gu,
11
+ minScriptRatio: 0.45,
12
+ },
13
+ ko: {
14
+ language: 'Korean',
15
+ scriptName: 'hangul',
16
+ scriptCharacters: /\p{Script=Hangul}/gu,
17
+ minScriptRatio: 0.55,
18
+ },
19
+ ru: {
20
+ language: 'Russian',
21
+ scriptName: 'cyrillic',
22
+ scriptCharacters: /\p{Script=Cyrillic}/gu,
23
+ minScriptRatio: 0.65,
24
+ },
25
+ zh: {
26
+ language: 'Chinese',
27
+ scriptName: 'han',
28
+ scriptCharacters: /\p{Script=Han}/gu,
29
+ minScriptRatio: 0.45,
30
+ },
31
+ } as const;
32
+
33
+ const LETTERS = /\p{L}/gu;
34
+ const TRANSLATABLE_KEYS = ['title', 'description', 'ui', 'seo', 'faq', 'howTo'] as const;
35
+
36
+ function collectStrings(value: unknown): string[] {
37
+ if (typeof value === 'string') return [value];
38
+ if (!value || typeof value !== 'object') return [];
39
+ if (Array.isArray(value)) return value.flatMap(collectStrings);
40
+ return Object.values(value).flatMap(collectStrings);
41
+ }
42
+
43
+ function normalizeText(value: unknown): string {
44
+ return collectStrings(value).join(' ').normalize('NFC');
45
+ }
46
+
47
+ function translatableContent(content: Record<string, unknown>) {
48
+ return TRANSLATABLE_KEYS.map((key) => content[key]);
49
+ }
50
+
51
+ function letterCount(text: string): number {
52
+ return text.match(LETTERS)?.length ?? 0;
53
+ }
54
+
55
+ function scriptCount(text: string, locale: ScriptLocale): number {
56
+ return text.match(SCRIPT_RULES[locale].scriptCharacters)?.length ?? 0;
57
+ }
58
+
59
+ function scriptRatio(text: string, locale: ScriptLocale): number {
60
+ const letters = letterCount(text);
61
+ if (letters === 0) return 0;
62
+ return scriptCount(text, locale) / letters;
63
+ }
64
+
65
+ describe('Native script density validation', () => {
66
+ ALL_TOOLS.forEach((tool) => {
67
+ describe(`Tool: ${tool.entry.id}`, () => {
68
+ Object.keys(SCRIPT_RULES).forEach((locale) => {
69
+ it(`${locale} keeps most translated text in its native script`, async () => {
70
+ const typedLocale = locale as ScriptLocale;
71
+ const loader = tool.entry.i18n[typedLocale];
72
+ if (!loader) return;
73
+
74
+ const content = await loader();
75
+ const rule = SCRIPT_RULES[typedLocale];
76
+ const text = normalizeText(translatableContent(content as Record<string, unknown>));
77
+ const letters = letterCount(text);
78
+ const matches = scriptCount(text, typedLocale);
79
+ const ratio = scriptRatio(text, typedLocale);
80
+
81
+ expect(
82
+ ratio,
83
+ [
84
+ `Possible broken translation detected in ${tool.entry.id}/${typedLocale} (${rule.language}).`,
85
+ `The text has ${matches} ${rule.scriptName} characters out of ${letters} analyzed letters (${(ratio * 100).toFixed(1)}%).`,
86
+ `Most translatable content should be written in ${rule.scriptName} script.`,
87
+ 'Non-translatable fields such as slug, bibliography, and schemas are ignored to avoid false positives.',
88
+ ].join(' '),
89
+ ).toBeGreaterThanOrEqual(rule.minScriptRatio);
90
+ });
91
+ });
92
+ });
93
+ });
94
+ });
@@ -4,67 +4,67 @@ import type { ApplianceCostCalculatorUI } from '../ui';
4
4
  import { bibliography } from '../bibliography';
5
5
 
6
6
  const slug = 'kalkulator-kosztow-urzadzen';
7
- const title = 'Kalkulator Kosztow na Cykl Urzadzen Domowych';
7
+ const title = 'Kalkulator Kosztów na Cykl Urządzeń Domowych';
8
8
  const description =
9
- 'Oblicz rzeczywisty koszt kazdego prania w pralce, zmywarce i suszarce. Zobacz dokladnie, ile energii elektrycznej, wody i srodka czyszczacego kosztuje kazdy cykl i odkryj, ile mozesz zaoszczedzic, przechodzac w tryb Eco.';
9
+ 'Oblicz rzeczywisty koszt każdego prania w pralce, zmywarce i suszarce. Zobacz dokładnie, ile energii elektrycznej, wody i środka czyszczącego kosztuje każdy cykl i odkryj, ile możesz zaoszczędzić, przechodząc w tryb Eco.';
10
10
 
11
11
  const faqData = [
12
12
  {
13
13
  question: 'Ile kosztuje cykl prania w pralce?',
14
14
  answer:
15
- 'Typowy cykl Eco w 40C zuzywa okolo 0,45 kWh energii elektrycznej i 40 litrow wody. Przy przecietnych europejskich cenach kosztuje to okolo 0,10 do 0,15 euro na pranie. Cykl intensywny w 60C moze zuzywac ponad 1,2 kWh i 70 litrow, podnoszac koszt powyzej 0,30 euro.',
15
+ 'Typowy cykl Eco w 40°C zużywa około 0,45 kWh energii elektrycznej i 40 litrów wody. Przy przeciętnych europejskich cenach kosztuje to około 0,10 do 0,15 euro na pranie. Cykl intensywny w 60°C może zużywać ponad 1,2 kWh i 70 litrów, podnosząc koszt powyżej 0,30 euro.',
16
16
  },
17
17
  {
18
- question: 'Czy korzystanie ze zmywarki w nocy jest tansze?',
18
+ question: 'Czy korzystanie ze zmywarki w nocy jest tańsze?',
19
19
  answer:
20
- 'Jesli Twoj dostawca energii elektrycznej stosuje taryfy z roznicowaniem godzinowym, uruchamianie zmywarki poza szczytem moze obnizyc koszt energii o 30 procent lub wiecej. Godziny szczytu to zazwyczaj poranki i wieczory w dni powszednie.',
20
+ 'Jeśli Twój dostawca energii elektrycznej stosuje taryfy z różnicowaniem godzinowym, uruchamianie zmywarki poza szczytem może obniżyć koszt energii o 30 procent lub więcej. Godziny szczytu to zazwyczaj poranki i wieczory w dni powszednie.',
21
21
  },
22
22
  {
23
- question: 'Czy suszarka zuzywa duzo pradu?',
23
+ question: 'Czy suszarka zużywa dużo prądu?',
24
24
  answer:
25
- 'Tak. Suszarka jest jednym z najbardziej energochlonnych urzadzen domowych. Pelne pranie moze zuzywac od 2,5 do 3,5 kWh, kosztujac od 0,50 do 0,80 euro na cykl. Uzycie suszarki z pompa ciepla lub suszenie odziezy na zewnatrz moze obnizyc ten koszt o 60 do 80 procent.',
25
+ 'Tak. Suszarka jest jednym z najbardziej energochłonnych urządzeń domowych. Pełne pranie może zużywać od 2,5 do 3,5 kWh, kosztując od 0,50 do 0,80 euro na cykl. Użycie suszarki z pompą ciepła lub suszenie odzieży na zewnątrz może obniżyć ten koszt o 60 do 80 procent.',
26
26
  },
27
27
  {
28
- question: 'Ile wody zuzywa zmywarka?',
28
+ question: 'Ile wody zużywa zmywarka?',
29
29
  answer:
30
- 'Nowoczesne zmywarki sa zaskakujaco wydajne. Standardowy cykl Eco zuzywa tylko 8 do 12 litrow wody na pranie, podczas gdy program intensywny moze zuzywac 14 do 16 litrow. Jest to znacznie mniej niz mycie tych samych naczyn recznie pod biezaca woda, co zazwyczaj zuzywa 40 do 60 litrow.',
30
+ 'Nowoczesne zmywarki zaskakująco wydajne. Standardowy cykl Eco zużywa tylko 8 do 12 litrów wody na pranie, podczas gdy program intensywny może zużywać 14 do 16 litrów. Jest to znacznie mniej niż mycie tych samych naczyń ręcznie pod bieżącą wodą, co zazwyczaj zużywa 40 do 60 litrów.',
31
31
  },
32
32
  {
33
- question: 'Jaka jest najtansza pora na korzystanie z urzadzen?',
33
+ question: 'Jaka jest najtańsza pora na korzystanie z urządzeń?',
34
34
  answer:
35
- 'Przy taryfach z roznicowaniem godzinowym najtansze okresy to zazwyczec pozna noc, bardzo wczesny ranek i weekendy. Te okresy poza szczytem moga byc o 30 do 50 procent tansze niz szczytowe godziny w dni powszednie.',
35
+ 'Przy taryfach z różnicowaniem godzinowym najtańsze okresy to zazwyczaj późna noc, bardzo wczesny ranek i weekendy. Te okresy poza szczytem mogą być o 30 do 50 procent tańsze niż szczytowe godziny w dni powszednie.',
36
36
  },
37
37
  {
38
- question: 'Czy przejscie w tryb Eco naprawde pozwala zaoszczedzic?',
38
+ question: 'Czy przejście w tryb Eco naprawdę pozwala zaoszczędzić?',
39
39
  answer:
40
- 'Absolutnie. Tryby Eco wykorzystuja nizsze temperatury, krotsze fazy nagrzewania i mniej wody. Przez caly rok, przechodzenie z Normalnego na Eco w pralce i zmywarce moze zaoszczedzic od 50 do 100 euro lacznie na energii elektrycznej i wodzie, w zaleznosci od czestotliwosci uzycia i lokalnych cen.',
40
+ 'Absolutnie. Tryby Eco wykorzystują niższe temperatury, krótsze fazy nagrzewania i mniej wody. Przez cały rok, przechodzenie z Normalnego na Eco w pralce i zmywarce może zaoszczędzić od 50 do 100 euro łącznie na energii elektrycznej i wodzie, w zależności od częstotliwości użycia i lokalnych cen.',
41
41
  },
42
42
  ];
43
43
 
44
44
  const howToData = [
45
45
  {
46
- name: 'Wybierz urzadzenie',
47
- text: 'Dotknij ikony pralki, zmywarki lub suszarki u gory kalkulatora. Kazde urzadzenie ma inny profil zuycia energii i wody.',
46
+ name: 'Wybierz urządzenie',
47
+ text: 'Dotknij ikony pralki, zmywarki lub suszarki u góry kalkulatora. Każde urządzenie ma inny profil zużycia energii i wody.',
48
48
  },
49
49
  {
50
50
  name: 'Wybierz typ cyklu',
51
- text: 'Uzyj suwaka Eco, Normalny lub Intensywny, aby ustawic program prania. Eco zuzywa mniej energii i wody. Intensywny zuzywa najwiecej.',
51
+ text: 'Użyj suwaka Eco, Normalny lub Intensywny, aby ustawić program prania. Eco zużywa mniej energii i wody. Intensywny zużywa najwięcej.',
52
52
  },
53
53
  {
54
- name: 'Wprowadz lokalne ceny',
55
- text: 'Wpisz cene energii elektrycznej za kWh i cene wody za litro. Mozesz je znalezc na rachunku lub na stronie dostawcy.',
54
+ name: 'Wprowadź lokalne ceny',
55
+ text: 'Wpisz cenę energii elektrycznej za kWh i cenę wody za litr. Możesz je znaleźć na rachunku lub na stronie dostawcy.',
56
56
  },
57
57
  {
58
- name: 'Ustaw czestotliwosc uzycia',
59
- text: 'Podaj, ile cykli uruchamiasz w tygodniu. Pozwala to kalkulatorowi oszacowac koszt roczny i potencjalne oszczednosci.',
58
+ name: 'Ustaw częstotliwość użycia',
59
+ text: 'Podaj, ile cykli uruchamiasz w tygodniu. Pozwala to kalkulatorowi oszacować koszt roczny i potencjalne oszczędności.',
60
60
  },
61
61
  {
62
- name: 'Wybierz godzine uzycia',
63
- text: 'Wybierz godzine, w ktorej planujesz uruchomic urzadzenie. Godziny szczytu sa podswietlone na czerwono. Godziny poza szczytem sa zielone i tansze, jesli masz taryfe z roznicowaniem godzinowym.',
62
+ name: 'Wybierz godzinę użycia',
63
+ text: 'Wybierz godzinę, w której planujesz uruchomić urządzenie. Godziny szczytu podświetlone na czerwono. Godziny poza szczytem zielone i tańsze, jeśli masz taryfę z różnicowaniem godzinowym.',
64
64
  },
65
65
  {
66
- name: 'Wlacz koszty srodka czyszczacego',
67
- text: 'Wlacz przelacznik srodka czyszczacego, jesli chcesz, aby rachunek zawieral szacunek proszku, plynu lub kapsulek na cykl.',
66
+ name: 'Włącz koszty środka czyszczącego',
67
+ text: 'Włącz przełącznik środka czyszczącego, jeśli chcesz, aby rachunek zawierał szacunek proszku, płynu lub kapsułek na cykl.',
68
68
  },
69
69
  ];
70
70
 
@@ -112,45 +112,45 @@ export const content: ToolLocaleContent<ApplianceCostCalculatorUI> = {
112
112
  seo: [
113
113
  {
114
114
  type: 'title',
115
- text: 'Ukryty Koszt Kazdego Prania',
115
+ text: 'Ukryty Koszt Każdego Prania',
116
116
  level: 2,
117
117
  },
118
118
  {
119
119
  type: 'paragraph',
120
- html: 'Wiekszosc osob nie ma pojecia, ile rzeczywiscie kosztuje cykl prania w pralce. Widza kwartalny rachunek za energie elektryczna i zakladaja, ze urzadzenia sa tanie w eksploatacji. Rzeczywistosc jest taka, ze rodzina wykonujaca piec pran w tygodniu moze wydawac ponad 200 euro rocznie tylko na pranie. Dodajmy zmywarke trzy razy w tygodniu i suszarke dwa razy, a suma przekracza 500 euro rocznie. Ten kalkulator szczegolowo analizuje kazdy grosz, aby zoptymalizowac Twoje nawyki.',
120
+ html: 'Większość osób nie ma pojęcia, ile rzeczywiście kosztuje cykl prania w pralce. Widzą kwartalny rachunek za energię elektryczną i zakładają, że urządzenia tanie w eksploatacji. Rzeczywistość jest taka, że rodzina wykonująca pięć prań w tygodniu może wydawać ponad 200 euro rocznie tylko na pranie. Dodajmy zmywarkę trzy razy w tygodniu i suszarkę dwa razy, a suma przekracza 500 euro rocznie. Ten kalkulator szczegółowo analizuje każdy grosz, aby zoptymalizować Twoje nawyki.',
121
121
  },
122
122
  {
123
123
  type: 'stats',
124
124
  items: [
125
125
  { value: '0,45', label: 'kWh Pranie Eco', icon: 'mdi:washing-machine' },
126
126
  { value: '40L', label: 'Woda Pranie Eco', icon: 'mdi:water' },
127
- { value: '30%', label: 'Doplata Szczytowa', icon: 'mdi:lightning-bolt' },
127
+ { value: '30%', label: 'Dopłata Szczytowa', icon: 'mdi:lightning-bolt' },
128
128
  ],
129
129
  columns: 3,
130
130
  },
131
131
  {
132
132
  type: 'title',
133
- text: 'Dlaczego Male Zmiany Sie Mnoza',
133
+ text: 'Dlaczego Małe Zmiany Się Mnożą',
134
134
  level: 3,
135
135
  },
136
136
  {
137
137
  type: 'paragraph',
138
- html: 'Jeden stopien temperatury lub kilka litrow wody wydaje sie nieistotne w jednym cyklu. Ale w ciagu 250 cykli rocznie, te male roznice przekladaja sie na powazne pieniadze. Spadek z 60C do 40C zmniejsza zuycie energii o okolo 40 procent na pranie. Przejscie z Normalnego na Eco w zmywarce oszczedza okolo 3 litrow wody i 0,3 kWh za kazdym razem. To nie sa marginalne zyski. To najszybszy sposob na zmniejszenie rachunkow bez kupowania nowych urzadzen.',
138
+ html: 'Jeden stopień temperatury lub kilka litrów wody wydaje się nieistotne w jednym cyklu. Ale w ciągu 250 cykli rocznie, te małe różnice przekładają się na poważne pieniądze. Spadek z 60°C do 40°C zmniejsza zużycie energii o około 40 procent na pranie. Przejście z Normalnego na Eco w zmywarce oszczędza około 3 litrów wody i 0,3 kWh za każdym razem. To nie marginalne zyski. To najszybszy sposób na zmniejszenie rachunków bez kupowania nowych urządzeń.',
139
139
  },
140
140
  {
141
141
  type: 'comparative',
142
142
  items: [
143
143
  {
144
144
  title: 'Cykl Intensywny',
145
- description: 'Maksymalne cieplo i woda dla silnie zabrudzonych pran.',
145
+ description: 'Maksymalne ciepło i woda dla silnie zabrudzonych prań.',
146
146
  icon: 'mdi:alert',
147
- points: ['Od 1,2 do 3,5 kWh na pranie', '70 litrow w pralce', 'Obowiazuje doplata w szczycie', 'Najwyzszy koszt roczny'],
147
+ points: ['Od 1,2 do 3,5 kWh na pranie', '70 litrów w pralce', 'Obowiązuje dopłata w szczycie', 'Najwyższy koszt roczny'],
148
148
  },
149
149
  {
150
150
  title: 'Cykl Eco',
151
- description: 'Nizsza temperatura, zoptymalizowany czas, minimalna ilosc wody.',
151
+ description: 'Niższa temperatura, zoptymalizowany czas, minimalna ilość wody.',
152
152
  icon: 'mdi:check-circle',
153
- points: ['Od 0,45 do 1,5 kWh na pranie', 'Tylko 8 do 40 litrow', 'Mozliwosc uruchomienia poza szczytem', 'Najnizszy koszt roczny'],
153
+ points: ['Od 0,45 do 1,5 kWh na pranie', 'Tylko 8 do 40 litrów', 'Możliwość uruchomienia poza szczytem', 'Najniższy koszt roczny'],
154
154
  },
155
155
  ],
156
156
  columns: 2,
@@ -158,35 +158,35 @@ export const content: ToolLocaleContent<ApplianceCostCalculatorUI> = {
158
158
  {
159
159
  type: 'diagnostic',
160
160
  variant: 'info',
161
- title: 'Szybki Audyt Urzadzen',
161
+ title: 'Szybki Audyt Urządzeń',
162
162
  icon: 'mdi:clipboard-check',
163
- badge: 'Dzialanie',
164
- html: '<p style="margin:0">Uzywaj pralki tylko przy pelnym zaladowaniu. Uzywaj programu Eco przy normalnym zabrudzeniu. Ustaw zmywarke na opozniony start po 22:00, jesli masz taryfe z roznicowaniem godzinowym. Czysc filtr suszarki po kazdym cyklu, aby zachowac wydajnosc. Susz odziez na zewnatrz lub na suszarce, gdy pozwala na to pogoda.</p>',
163
+ badge: 'Działanie',
164
+ html: '<p style="margin:0">Używaj pralki tylko przy pełnym załadowaniu. Używaj programu Eco przy normalnym zabrudzeniu. Ustaw zmywarkę na opóźniony start po 22:00, jeśli masz taryfę z różnicowaniem godzinowym. Czyść filtr suszarki po każdym cyklu, aby zachować wydajność. Susz odzież na zewnątrz lub na suszarce, gdy pozwala na to pogoda.</p>',
165
165
  },
166
166
  {
167
167
  type: 'title',
168
- text: 'Taryfy z Roznicowaniem Godzinowym i Godziny Szczytu',
168
+ text: 'Taryfy z Różnicowaniem Godzinowym i Godziny Szczytu',
169
169
  level: 3,
170
170
  },
171
171
  {
172
172
  type: 'paragraph',
173
- html: 'Wielu dostawcow energii elektrycznej obecnie stosuje rozne stawki w zaleznosci od pory dnia. Okresy szczytowe, zazwyczaj poranki i wieczory w dni powszednie, moga byc o 30 do 50 procent drozsze niz okresy poza szczytem. Takie samo pranie o 20:00 moze kosztowac 0,18 euro, podczas gdy to samo pranie o 02:00 kosztuje tylko 0,12 euro. W ciagu roku, wlasciwe planowanie korzystania z urzadzen moze zaoszczedzic od 50 do 100 euro bez zmiany niczego innego. Ten kalkulator podswietla godziny szczytu na czerwono, a poza szczytem na zielono, aby mozna bylo planowac.',
173
+ html: 'Wielu dostawców energii elektrycznej obecnie stosuje różne stawki w zależności od pory dnia. Okresy szczytowe, zazwyczaj poranki i wieczory w dni powszednie, mogą być o 30 do 50 procent droższe niż okresy poza szczytem. Takie samo pranie o 20:00 może kosztować 0,18 euro, podczas gdy to samo pranie o 02:00 kosztuje tylko 0,12 euro. W ciągu roku, właściwe planowanie korzystania z urządzeń może zaoszczędzić od 50 do 100 euro bez zmiany niczego innego. Ten kalkulator podświetla godziny szczytu na czerwono, a poza szczytem na zielono, aby można było planować.',
174
174
  },
175
175
  {
176
176
  type: 'summary',
177
- title: 'Jak Ograniczyc Koszty Urzadzen',
177
+ title: 'Jak Ograniczyć Koszty Urządzeń',
178
178
  items: [
179
- 'Uzyj tego kalkulatora, aby poznac rzeczywisty koszt kazdego cyklu Twoich urzadzen.',
180
- 'Przechodz w tryb Eco zawsze, gdy to mozliwe, aby zaoszczedzic do 40 procent energii.',
181
- 'Uruchamiaj pelne zaladowania zamiast czesciowych, aby zmaksymalizowac wydajnosc.',
182
- 'Opozniaj cykle na godziny poza szczytem, jesli Twoja taryfa przewiduje ceny godzinowe.',
183
- 'Susz odziez na zewnatrz zamiast uzywac suszarki, aby calkowicie wyeliminowac ten koszt.',
184
- 'Regularnie czysc filtry i odkamieniaj urzadzenia, aby utrzymac je na nominalnej wydajnosci.',
179
+ 'Użyj tego kalkulatora, aby poznać rzeczywisty koszt każdego cyklu Twoich urządzeń.',
180
+ 'Przechodź w tryb Eco zawsze, gdy to możliwe, aby zaoszczędzić do 40 procent energii.',
181
+ 'Uruchamiaj pełne załadowania zamiast częściowych, aby zmaksymalizować wydajność.',
182
+ 'Opóźniaj cykle na godziny poza szczytem, jeśli Twoja taryfa przewiduje ceny godzinowe.',
183
+ 'Susz odzież na zewnątrz zamiast używać suszarki, aby całkowicie wyeliminować ten koszt.',
184
+ 'Regularnie czyść filtry i odkamieniaj urządzenia, aby utrzymać je na nominalnej wydajności.',
185
185
  ],
186
186
  },
187
187
  ],
188
188
  ui: {
189
- labelAppliance: 'Urzadzenie',
189
+ labelAppliance: 'Urządzenie',
190
190
  applianceWasher: 'Pralka',
191
191
  applianceDishwasher: 'Zmywarka',
192
192
  applianceDryer: 'Suszarka',
@@ -198,21 +198,21 @@ export const content: ToolLocaleContent<ApplianceCostCalculatorUI> = {
198
198
  unitPriceKwh: '€/kWh',
199
199
  labelWaterPrice: 'Cena Wody',
200
200
  unitPriceLiter: '€/L',
201
- labelDetergent: 'Srodek Czyszczacy',
202
- labelDetergentToggle: 'Uwzglednij koszt srodka',
201
+ labelDetergent: 'Środek Czyszczący',
202
+ labelDetergentToggle: 'Uwzględnij koszt środka',
203
203
  labelHour: 'Planowana Godzina',
204
204
  peakBadge: 'Aktywna taryfa szczytowa',
205
205
  offPeakBadge: 'Taryfa poza szczytem',
206
- receiptTitle: 'Podsumowanie Kosztow',
206
+ receiptTitle: 'Podsumowanie Kosztów',
207
207
  receiptElectricity: 'Energia',
208
208
  receiptWater: 'Woda',
209
- receiptDetergent: 'Srodek',
209
+ receiptDetergent: 'Środek',
210
210
  receiptTotal: 'Razem na cykl',
211
211
  receiptPerYear: 'Szacunkowo rocznie',
212
212
  comparisonTitle: 'Inteligentna Zmiana',
213
- comparisonText: 'Przejscie z Normalnego na Eco oszczedza pieniadze przy kazdym praniu.',
214
- comparisonSavings: 'Oszczednosci roczne',
215
- comparisonMonths: 'miesiecy srodka',
213
+ comparisonText: 'Przejście z Normalnego na Eco oszczędza pieniądze przy każdym praniu.',
214
+ comparisonSavings: 'Oszczędności roczne',
215
+ comparisonMonths: 'miesięcy środka',
216
216
  badgePeak: 'Szczyt',
217
217
  badgeOffPeak: 'Poza Szczytem',
218
218
  unitKwh: 'kWh',