@jjlmoya/utils-tools 1.13.0 → 1.14.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 (95) hide show
  1. package/package.json +1 -1
  2. package/src/category/i18n/de.ts +3 -3
  3. package/src/category/i18n/en.ts +1 -1
  4. package/src/category/i18n/es.ts +1 -1
  5. package/src/category/i18n/fr.ts +16 -16
  6. package/src/category/i18n/id.ts +1 -1
  7. package/src/category/i18n/nl.ts +1 -1
  8. package/src/category/i18n/pl.ts +1 -1
  9. package/src/category/i18n/pt.ts +1 -1
  10. package/src/category/i18n/ru.ts +10 -10
  11. package/src/category/i18n/sv.ts +1 -1
  12. package/src/category/i18n/tr.ts +1 -1
  13. package/src/category/i18n/zh.ts +4 -4
  14. package/src/layouts/PreviewLayout.astro +7 -2
  15. package/src/tests/diacritics_density.test.ts +118 -0
  16. package/src/tests/inverted_punctuation.test.ts +84 -0
  17. package/src/tests/no_en_dash.test.ts +70 -0
  18. package/src/tests/script_density.test.ts +94 -0
  19. package/src/tool/date-diff-calculator/i18n/de.ts +4 -4
  20. package/src/tool/date-diff-calculator/i18n/pl.ts +1 -1
  21. package/src/tool/date-diff-calculator/i18n/ru.ts +4 -4
  22. package/src/tool/date-diff-calculator/i18n/zh.ts +3 -3
  23. package/src/tool/drive-direct-link/i18n/de.ts +4 -4
  24. package/src/tool/drive-direct-link/i18n/es.ts +1 -1
  25. package/src/tool/drive-direct-link/i18n/fr.ts +1 -1
  26. package/src/tool/drive-direct-link/i18n/pl.ts +4 -4
  27. package/src/tool/drive-direct-link/i18n/ru.ts +8 -8
  28. package/src/tool/drive-direct-link/i18n/zh.ts +4 -4
  29. package/src/tool/email-list-cleaner/i18n/de.ts +2 -2
  30. package/src/tool/email-list-cleaner/i18n/pl.ts +2 -2
  31. package/src/tool/email-list-cleaner/i18n/ru.ts +7 -7
  32. package/src/tool/email-list-cleaner/i18n/sv.ts +1 -1
  33. package/src/tool/email-list-cleaner/i18n/zh.ts +3 -3
  34. package/src/tool/env-badge-spain/i18n/ru.ts +10 -10
  35. package/src/tool/morse-beacon/bibliography.ts +3 -3
  36. package/src/tool/morse-beacon/i18n/de.ts +4 -4
  37. package/src/tool/morse-beacon/i18n/en.ts +2 -2
  38. package/src/tool/morse-beacon/i18n/es.ts +2 -2
  39. package/src/tool/morse-beacon/i18n/fr.ts +5 -5
  40. package/src/tool/morse-beacon/i18n/id.ts +2 -2
  41. package/src/tool/morse-beacon/i18n/it.ts +2 -2
  42. package/src/tool/morse-beacon/i18n/ja.ts +2 -2
  43. package/src/tool/morse-beacon/i18n/ko.ts +2 -2
  44. package/src/tool/morse-beacon/i18n/nl.ts +2 -2
  45. package/src/tool/morse-beacon/i18n/pl.ts +3 -3
  46. package/src/tool/morse-beacon/i18n/pt.ts +2 -2
  47. package/src/tool/morse-beacon/i18n/ru.ts +3 -3
  48. package/src/tool/morse-beacon/i18n/sv.ts +2 -2
  49. package/src/tool/morse-beacon/i18n/tr.ts +2 -2
  50. package/src/tool/morse-beacon/i18n/zh.ts +3 -3
  51. package/src/tool/password-generator/bibliography.ts +3 -3
  52. package/src/tool/password-generator/i18n/de.ts +5 -5
  53. package/src/tool/password-generator/i18n/fr.ts +1 -1
  54. package/src/tool/password-generator/i18n/pl.ts +3 -3
  55. package/src/tool/password-generator/i18n/ru.ts +4 -4
  56. package/src/tool/password-generator/i18n/zh.ts +3 -3
  57. package/src/tool/routes/bibliography.ts +5 -5
  58. package/src/tool/routes/i18n/de.ts +1 -1
  59. package/src/tool/routes/i18n/fr.ts +3 -3
  60. package/src/tool/routes/i18n/ru.ts +3 -3
  61. package/src/tool/routes/i18n/zh.ts +1 -1
  62. package/src/tool/rule-of-three/bibliography.ts +2 -2
  63. package/src/tool/rule-of-three/i18n/de.ts +2 -2
  64. package/src/tool/rule-of-three/i18n/fr.ts +7 -7
  65. package/src/tool/rule-of-three/i18n/nl.ts +2 -2
  66. package/src/tool/rule-of-three/i18n/pl.ts +2 -2
  67. package/src/tool/rule-of-three/i18n/ru.ts +3 -3
  68. package/src/tool/rule-of-three/i18n/tr.ts +1 -1
  69. package/src/tool/rule-of-three/i18n/zh.ts +2 -2
  70. package/src/tool/seo-content-optimizer/i18n/fr.ts +3 -3
  71. package/src/tool/seo-content-optimizer/i18n/ru.ts +1 -1
  72. package/src/tool/speed-reader/i18n/de.ts +3 -3
  73. package/src/tool/speed-reader/i18n/en.ts +2 -2
  74. package/src/tool/speed-reader/i18n/fr.ts +9 -9
  75. package/src/tool/speed-reader/i18n/id.ts +2 -2
  76. package/src/tool/speed-reader/i18n/it.ts +2 -2
  77. package/src/tool/speed-reader/i18n/nl.ts +2 -2
  78. package/src/tool/speed-reader/i18n/pl.ts +3 -3
  79. package/src/tool/speed-reader/i18n/pt.ts +2 -2
  80. package/src/tool/speed-reader/i18n/ru.ts +10 -10
  81. package/src/tool/speed-reader/i18n/sv.ts +2 -2
  82. package/src/tool/speed-reader/i18n/zh.ts +4 -4
  83. package/src/tool/text-pixel-calculator/i18n/de.ts +2 -2
  84. package/src/tool/text-pixel-calculator/i18n/fr.ts +1 -1
  85. package/src/tool/text-pixel-calculator/i18n/pl.ts +3 -3
  86. package/src/tool/text-pixel-calculator/i18n/ru.ts +4 -4
  87. package/src/tool/text-pixel-calculator/i18n/zh.ts +3 -3
  88. package/src/tool/whatsapp-link/bibliography.ts +2 -2
  89. package/src/tool/whatsapp-link/i18n/de.ts +4 -4
  90. package/src/tool/whatsapp-link/i18n/es.ts +2 -2
  91. package/src/tool/whatsapp-link/i18n/fr.ts +2 -2
  92. package/src/tool/whatsapp-link/i18n/pl.ts +4 -4
  93. package/src/tool/whatsapp-link/i18n/ru.ts +5 -5
  94. package/src/tool/whatsapp-link/i18n/zh.ts +4 -4
  95. package/src/tool/whatsapp-link/whatsapp-link-generator.css +41 -4
@@ -0,0 +1,70 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ function getFiles(dir: string): string[] {
6
+ const results: string[] = [];
7
+ if (!fs.existsSync(dir)) {
8
+ return results;
9
+ }
10
+ const list = fs.readdirSync(dir);
11
+ for (const file of list) {
12
+ const fullPath = path.join(dir, file);
13
+ const stat = fs.statSync(fullPath);
14
+ if (stat && stat.isDirectory()) {
15
+ if (file !== 'tests' && file !== 'node_modules' && file !== '.astro') {
16
+ results.push(...getFiles(fullPath));
17
+ }
18
+ } else {
19
+ results.push(fullPath);
20
+ }
21
+ }
22
+ return results;
23
+ }
24
+
25
+ function isContentFile(filePath: string): boolean {
26
+ return /\\i18n\\/.test(filePath) || filePath.endsWith('bibliography.ts');
27
+ }
28
+
29
+ const srcDir = path.join(process.cwd(), 'src');
30
+ const scriptsDir = path.join(process.cwd(), 'scripts');
31
+ const filesToTest = [
32
+ ...getFiles(srcDir).filter(isContentFile),
33
+ ...getFiles(scriptsDir).filter(isContentFile),
34
+ ];
35
+
36
+ const aiTypographyGarbage = [
37
+ '\u2013',
38
+ '\u2014',
39
+ '\u2026',
40
+ '\u201C',
41
+ '\u201D',
42
+ '\u2018',
43
+ '\u2019',
44
+ '\u00AB',
45
+ '\u00BB',
46
+ '\u200B',
47
+ '\u201E',
48
+ ];
49
+
50
+ describe('Typography Garbage Character Validation', () => {
51
+ filesToTest.forEach((filePath) => {
52
+ const relativePath = path.relative(process.cwd(), filePath);
53
+ it(`should not contain typography garbage characters in ${relativePath}`, () => {
54
+ const content = fs.readFileSync(filePath, 'utf-8');
55
+ const hasAiPatterns = aiTypographyGarbage.some(char => content.includes(char));
56
+ expect(hasAiPatterns).toBe(false);
57
+ });
58
+
59
+ it(`should not contain space before colon in ${relativePath}`, () => {
60
+ const content = fs.readFileSync(filePath, 'utf-8');
61
+ const spaceBeforeColon = / : /.test(content);
62
+ expect(spaceBeforeColon).toBe(false);
63
+ });
64
+
65
+ it(`should not contain double hyphen in ${relativePath}`, () => {
66
+ const content = fs.readFileSync(filePath, 'utf-8');
67
+ expect(content).not.toContain('--');
68
+ });
69
+ });
70
+ });
@@ -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
+ });
@@ -23,8 +23,8 @@ const faqData = [
23
23
  ];
24
24
 
25
25
  const howToData = [
26
- { name: 'Startdatum auswählen', text: 'Geben Sie das Startdatum und die Uhrzeit ein oder klicken Sie auf Heute“, um sie sofort festzulegen.' },
27
- { name: 'Enddatum auswählen', text: 'Definieren Sie den Zielzeitpunkt. Verwenden Sie Jetzt“, um die bis zu diesem exakten Moment verstrichene Zeit zu messen.' },
26
+ { name: 'Startdatum auswählen', text: 'Geben Sie das Startdatum und die Uhrzeit ein oder klicken Sie auf "Heute", um sie sofort festzulegen.' },
27
+ { name: 'Enddatum auswählen', text: 'Definieren Sie den Zielzeitpunkt. Verwenden Sie "Jetzt", um die bis zu diesem exakten Moment verstrichene Zeit zu messen.' },
28
28
  { name: 'Ergebnisse ablesen', text: 'Überprüfen Sie die Aufteilung in Tage, Stunden, Minuten und Sekunden sowie die kumulierten Summen für Wochen und Stunden.' },
29
29
  ];
30
30
 
@@ -88,7 +88,7 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
88
88
  { type: 'title', level: 2, text: 'Datumsdifferenzrechner mit Tagen, Stunden und Minuten' },
89
89
  {
90
90
  type: 'paragraph',
91
- html: 'Die Berechnung der <strong>Differenz zwischen zwei Daten</strong> ist eine der am häufigsten unterschätzten Aufgaben sowohl im Alltag als auch im beruflichen Umfeld. Egal, ob Sie eine Produkteinführung planen, das exakte Alter einer Person berechnen oder die verbleibende Zeit bis zu einem besonderen Ereignis messen ein präzises Werkzeug ist für ein effizientes Zeitmanagement unerlässlich.',
91
+ html: 'Die Berechnung der <strong>Differenz zwischen zwei Daten</strong> ist eine der am häufigsten unterschätzten Aufgaben sowohl im Alltag als auch im beruflichen Umfeld. Egal, ob Sie eine Produkteinführung planen, das exakte Alter einer Person berechnen oder die verbleibende Zeit bis zu einem besonderen Ereignis messen - ein präzises Werkzeug ist für ein effizientes Zeitmanagement unerlässlich.',
92
92
  },
93
93
  { type: 'title', level: 3, text: 'Wofür wird der Datumsdifferenzrechner verwendet?' },
94
94
  {
@@ -108,7 +108,7 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
108
108
  { type: 'title', level: 3, text: 'Zeitwahrnehmung und digitale Präzision' },
109
109
  {
110
110
  type: 'paragraph',
111
- html: 'Oft neigen wir dazu, die Zeit zu runden. Wir sagen etwa ein Monat“, wenn es tatsächlich noch 27 Tage und 14 Stunden sind. Durch die Verwendung eines digitalen Rechners eliminieren wir die Subjektivität und erhalten klare Daten für eine fundierte Entscheidungsfindung.',
111
+ html: 'Oft neigen wir dazu, die Zeit zu runden. Wir sagen "etwa ein Monat", wenn es tatsächlich noch 27 Tage und 14 Stunden sind. Durch die Verwendung eines digitalen Rechners eliminieren wir die Subjektivität und erhalten klare Daten für eine fundierte Entscheidungsfindung.',
112
112
  },
113
113
  { type: 'title', level: 3, text: 'Kalendertage versus Arbeitstage' },
114
114
  {
@@ -108,7 +108,7 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
108
108
  { type: 'title', level: 3, text: 'Percepcja czasu i cyfrowa precyzja' },
109
109
  {
110
110
  type: 'paragraph',
111
- html: 'Często mamy tendencję do zaokrąglania czasu. Mówimy około miesiąca”, gdy w rzeczywistości pozostało 27 dni i 14 godzin. Korzystając z kalkulatora cyfrowego, eliminujemy subiektywizm i uzyskujemy czyste dane do podejmowania świadomych decyzji.',
111
+ html: 'Często mamy tendencję do zaokrąglania czasu. Mówimy "około miesiąca", gdy w rzeczywistości pozostało 27 dni i 14 godzin. Korzystając z kalkulatora cyfrowego, eliminujemy subiektywizm i uzyskujemy czyste dane do podejmowania świadomych decyzji.',
112
112
  },
113
113
  { type: 'title', level: 3, text: 'Dni kalendarzowe a dni robocze' },
114
114
  {
@@ -23,8 +23,8 @@ const faqData = [
23
23
  ];
24
24
 
25
25
  const howToData = [
26
- { name: 'Выберите дату начала', text: 'Введите дату и время начала или нажмите «Сегодня», чтобы установить их мгновенно.' },
27
- { name: 'Выберите дату окончания', text: 'Определите целевой момент. Используйте «Сейчас», чтобы измерить время, прошедшее до этого точного момента.' },
26
+ { name: 'Выберите дату начала', text: 'Введите дату и время начала или нажмите "Сегодня", чтобы установить их мгновенно.' },
27
+ { name: 'Выберите дату окончания', text: 'Определите целевой момент. Используйте "Сейчас", чтобы измерить время, прошедшее до этого точного момента.' },
28
28
  { name: 'Получите результаты', text: 'Проверьте разбивку по дням, часам, минутам и секундам, а также накопленные итоги за недели и часы.' },
29
29
  ];
30
30
 
@@ -88,7 +88,7 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
88
88
  { type: 'title', level: 2, text: 'Калькулятор разницы дат с днями, часами и минутами' },
89
89
  {
90
90
  type: 'paragraph',
91
- html: 'Расчет <strong>разницы между двумя датами</strong> одна из самых распространенных и недооцененных задач как в повседневной жизни, так и в профессиональной среде. Планируете ли вы запуск продукта, рассчитываете точный возраст человека или измеряете время, оставшееся до особого события, наличие точного инструмента необходимо для эффективного тайм-менеджмента.',
91
+ html: 'Расчет <strong>разницы между двумя датами</strong> - одна из самых распространенных и недооцененных задач как в повседневной жизни, так и в профессиональной среде. Планируете ли вы запуск продукта, рассчитываете точный возраст человека или измеряете время, оставшееся до особого события, наличие точного инструмента необходимо для эффективного тайм-менеджмента.',
92
92
  },
93
93
  { type: 'title', level: 3, text: 'Для чего используется калькулятор разницы дат?' },
94
94
  {
@@ -108,7 +108,7 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
108
108
  { type: 'title', level: 3, text: 'Восприятие времени и цифровая точность' },
109
109
  {
110
110
  type: 'paragraph',
111
- html: 'Мы часто склонны округлять время. Мы говорим «около месяца», когда на самом деле до события осталось 27 дней и 14 часов. Используя цифровой калькулятор, мы исключаем субъективность и получаем чистые данные для принятия обоснованных решений.',
111
+ html: 'Мы часто склонны округлять время. Мы говорим "около месяца", когда на самом деле до события осталось 27 дней и 14 часов. Используя цифровой калькулятор, мы исключаем субъективность и получаем чистые данные для принятия обоснованных решений.',
112
112
  },
113
113
  { type: 'title', level: 3, text: 'Календарные дни против рабочих дней' },
114
114
  {
@@ -23,8 +23,8 @@ const faqData = [
23
23
  ];
24
24
 
25
25
  const howToData = [
26
- { name: '选择开始日期', text: '输入开始日期和时间,或点击“今天”立即设置。' },
27
- { name: '选择结束日期', text: '定义目标时刻。使用“现在”来测量截至这一精确时刻所经过的时间。' },
26
+ { name: '选择开始日期', text: '输入开始日期和时间,或点击"今天"立即设置。' },
27
+ { name: '选择结束日期', text: '定义目标时刻。使用"现在"来测量截至这一精确时刻所经过的时间。' },
28
28
  { name: '读取结果', text: '检查天、小时、分钟和秒的详细分解,以及周和小时的累积总计。' },
29
29
  ];
30
30
 
@@ -108,7 +108,7 @@ export const content: ToolLocaleContent<DateDiffCalculatorUI> = {
108
108
  { type: 'title', level: 3, text: '时间感知与数字精度' },
109
109
  {
110
110
  type: 'paragraph',
111
- html: '我们通常倾向于四舍五入时间。当实际距离还有 27 天 14 小时时,我们会说“大约一个月”。通过使用数字计算器,我们消除了主观性,并获得了用于明智决策的清晰数据。',
111
+ html: '我们通常倾向于四舍五入时间。当实际距离还有 27 天 14 小时时,我们会说"大约一个月"。通过使用数字计算器,我们消除了主观性,并获得了用于明智决策的清晰数据。',
112
112
  },
113
113
  { type: 'title', level: 3, text: '自然日与工作日' },
114
114
  {
@@ -6,11 +6,11 @@ import type { DriveDirectLinkUI } from '../ui';
6
6
  const faqData = [
7
7
  {
8
8
  question: 'Wie verwende ich den Google Drive Download-Konverter?',
9
- answer: 'Kopieren Sie den vollständigen Freigeben“-Link einer beliebigen auf Google Drive gespeicherten Datei (diese muss über öffentliche Berechtigungen verfügen). Fügen Sie ihn in das Textfeld des Tools ein und klicken Sie auf Direktlink generieren“. Es erscheint eine neue URL, die den Download startet, ohne den Viewer von Google zu öffnen.',
9
+ answer: 'Kopieren Sie den vollständigen "Freigeben"-Link einer beliebigen auf Google Drive gespeicherten Datei (diese muss über öffentliche Berechtigungen verfügen). Fügen Sie ihn in das Textfeld des Tools ein und klicken Sie auf "Direktlink generieren". Es erscheint eine neue URL, die den Download startet, ohne den Viewer von Google zu öffnen.',
10
10
  },
11
11
  {
12
12
  question: 'Kann ich einen Direkt-Download-Link für eine private Datei erstellen?',
13
- answer: 'Wenn die Datei privat oder der Zugriff eingeschränkt ist, fordert Google Sie auf, sich mit einem autorisierten Konto anzumelden. Für saubere Downloads ohne Registrierung müssen die Dateiberechtigungen auf Jeder, der über den Link verfügt, kann ansehen eingestellt sein.',
13
+ answer: 'Wenn die Datei privat oder der Zugriff eingeschränkt ist, fordert Google Sie auf, sich mit einem autorisierten Konto anzumelden. Für saubere Downloads ohne Registrierung müssen die Dateiberechtigungen auf "Jeder, der über den Link verfügt, kann ansehen" eingestellt sein.',
14
14
  },
15
15
  {
16
16
  question: 'Ist es sicher, meine Drive-Links hier zu konvertieren?',
@@ -23,8 +23,8 @@ const faqData = [
23
23
  ];
24
24
 
25
25
  const howToData = [
26
- { name: 'Freigabelink kopieren', text: 'Klicken Sie in Google Drive mit der rechten Maustaste auf die Datei und wählen Sie Link abrufen“. Stellen Sie sicher, dass die Berechtigungen den Zugriff für jeden erlauben, der über den Link verfügt.' },
27
- { name: 'Link in den Generator einfügen', text: 'Geben Sie die vollständige Drive-URL in das Textfeld des Tools ein und klicken Sie auf die Schaltfläche Direktlink generieren“.' },
26
+ { name: 'Freigabelink kopieren', text: 'Klicken Sie in Google Drive mit der rechten Maustaste auf die Datei und wählen Sie "Link abrufen". Stellen Sie sicher, dass die Berechtigungen den Zugriff für jeden erlauben, der über den Link verfügt.' },
27
+ { name: 'Link in den Generator einfügen', text: 'Geben Sie die vollständige Drive-URL in das Textfeld des Tools ein und klicken Sie auf die Schaltfläche "Direktlink generieren".' },
28
28
  { name: 'Generierten Link kopieren und verwenden', text: 'Kopieren Sie den neuen Direkt-Download-Link und verwenden Sie ihn in E-Mails, auf Websites, in Newslettern oder überall dort, wo die Datei beim Klicken sofort heruntergeladen werden soll.' },
29
29
  ];
30
30
 
@@ -100,7 +100,7 @@ export const content: ToolLocaleContent<DriveDirectLinkUI> = {
100
100
  { type: 'title', level: 3, text: '¿Cómo funciona nuestro creador de links directos?' },
101
101
  {
102
102
  type: 'paragraph',
103
- html: 'El proceso ocurre 100% en tu navegador. Google Drive estructura sus enlaces de compartir de dos formas principales, ambas con un identificador único de archivo. La herramienta extrae ese ID y construye una nueva URL con el parámetro de exportación nativo de Google: <code>https://drive.google.com/uc?export=download&amp;id=TU_ID</code>.',
103
+ html: 'El proceso ocurre 100% en tu navegador. Google Drive estructura sus enlaces de compartir de dos formas principales, ambas con un identificador único de archivo. La herramienta extrae ese ID y construye una nueva URL con el parametro de exportacion nativo de Google, formada por <code>https://drive.google.com/uc</code> seguido de los parametros <code>export=download</code> e <code>id</code> del archivo.',
104
104
  },
105
105
  {
106
106
  type: 'tip',
@@ -100,7 +100,7 @@ export const content: ToolLocaleContent<DriveDirectLinkUI> = {
100
100
  { type: 'title', level: 3, text: 'Comment fonctionne le générateur de lien direct ?' },
101
101
  {
102
102
  type: 'paragraph',
103
- html: 'Tout se passe à 100 % dans votre navigateur. Les liens de partage Google Drive contiennent un identifiant unique de fichier. L\'outil extrait cet identifiant et construit une nouvelle URL avec le paramètre d\'export natif de Google : <code>https://drive.google.com/uc?export=download&amp;id=VOTRE_ID</code>.',
103
+ html: 'Tout se passe à 100 % dans votre navigateur. Les liens de partage Google Drive contiennent un identifiant unique de fichier. L\'outil extrait cet identifiant et construit une nouvelle URL avec le paramètre d\'export natif de Google: <code>https://drive.google.com/uc?export=download&amp;id=VOTRE_ID</code>.',
104
104
  },
105
105
  {
106
106
  type: 'tip',
@@ -6,11 +6,11 @@ import type { DriveDirectLinkUI } from '../ui';
6
6
  const faqData = [
7
7
  {
8
8
  question: 'Jak korzystać z konwertera pobierania Google Drive?',
9
- answer: 'Skopiuj pełny link Udostępnij dowolnego pliku zapisanego na Dysku Google (musi on mieć uprawnienia publiczne). Wklej go do pola tekstowego narzędzia i kliknij Generuj link bezpośredni”. Pojawi się nowy adres URL, który rozpocznie pobieranie bez otwierania przeglądarki Google.',
9
+ answer: 'Skopiuj pełny link "Udostępnij" dowolnego pliku zapisanego na Dysku Google (musi on mieć uprawnienia publiczne). Wklej go do pola tekstowego narzędzia i kliknij "Generuj link bezpośredni". Pojawi się nowy adres URL, który rozpocznie pobieranie bez otwierania przeglądarki Google.',
10
10
  },
11
11
  {
12
12
  question: 'Czy mogę wygenerować bezpośredni link do pobrania prywatnego pliku?',
13
- answer: 'Jeśli plik jest prywatny lub dostęp do niego jest ograniczony, Google poprosi Cię o zalogowanie się na autoryzowane konto. Aby pobieranie przebiegało bez zakłóceń i bez rejestracji, uprawnienia do pliku muszą być ustawione na Każda osoba mająca link może przeglądać”.',
13
+ answer: 'Jeśli plik jest prywatny lub dostęp do niego jest ograniczony, Google poprosi Cię o zalogowanie się na autoryzowane konto. Aby pobieranie przebiegało bez zakłóceń i bez rejestracji, uprawnienia do pliku muszą być ustawione na "Każda osoba mająca link może przeglądać".',
14
14
  },
15
15
  {
16
16
  question: 'Czy konwertowanie moich linków do Drive tutaj jest bezpieczne?',
@@ -23,8 +23,8 @@ const faqData = [
23
23
  ];
24
24
 
25
25
  const howToData = [
26
- { name: 'Skopiuj link do udostępniania', text: 'Na Dysku Google kliknij plik prawym przyciskiem myszy i wybierz Pobierz link”. Upewnij się, że uprawnienia pozwalają na dostęp każdej osobie mającej link.' },
27
- { name: 'Wklej link do generatora', text: 'Wprowadź pełny adres URL Dysku w polu tekstowym narzędzia i kliknij przycisk Generuj link bezpośredni”.' },
26
+ { name: 'Skopiuj link do udostępniania', text: 'Na Dysku Google kliknij plik prawym przyciskiem myszy i wybierz "Pobierz link". Upewnij się, że uprawnienia pozwalają na dostęp każdej osobie mającej link.' },
27
+ { name: 'Wklej link do generatora', text: 'Wprowadź pełny adres URL Dysku w polu tekstowym narzędzia i kliknij przycisk "Generuj link bezpośredni".' },
28
28
  { name: 'Skopiuj i użyj wygenerowanego linku', text: 'Skopiuj nowy bezpośredni link do pobrania i używaj go w wiadomościach e-mail, na stronach internetowych, w newsletterach lub wszędzie tam, gdzie potrzebujesz, aby plik został pobrany natychmiast po kliknięciu.' },
29
29
  ];
30
30
 
@@ -6,11 +6,11 @@ import type { DriveDirectLinkUI } from '../ui';
6
6
  const faqData = [
7
7
  {
8
8
  question: 'Как пользоваться конвертером ссылок для скачивания с Google Диска?',
9
- answer: 'Скопируйте полную ссылку из меню «Поделиться» для любого файла, хранящегося на Google Диске (у него должен быть публичный доступ). Вставьте ее в текстовое поле инструмента и нажмите «Создать прямую ссылку». Появится новый URL-адрес, который начнет загрузку без открытия окна предварительного просмотра Google.',
9
+ answer: 'Скопируйте полную ссылку из меню "Поделиться" для любого файла, хранящегося на Google Диске (у него должен быть публичный доступ). Вставьте ее в текстовое поле инструмента и нажмите "Создать прямую ссылку". Появится новый URL-адрес, который начнет загрузку без открытия окна предварительного просмотра Google.',
10
10
  },
11
11
  {
12
12
  question: 'Могу ли я создать прямую ссылку на скачивание для приватного файла?',
13
- answer: 'Если файл является приватным или доступ к нему ограничен, Google попросит вас войти в систему с авторизованной учетной записью. Для прямой загрузки без регистрации права доступа к файлу должны быть установлены как «Все, у кого есть ссылка, могут просматривать».',
13
+ answer: 'Если файл является приватным или доступ к нему ограничен, Google попросит вас войти в систему с авторизованной учетной записью. Для прямой загрузки без регистрации права доступа к файлу должны быть установлены как "Все, у кого есть ссылка, могут просматривать".',
14
14
  },
15
15
  {
16
16
  question: 'Безопасно ли конвертировать здесь мои ссылки на Диск?',
@@ -23,8 +23,8 @@ const faqData = [
23
23
  ];
24
24
 
25
25
  const howToData = [
26
- { name: 'Скопируйте ссылку доступа', text: 'В Google Диске щелкните файл правой кнопкой мыши и выберите «Копировать ссылку». Убедитесь, что настройки доступа позволяют просматривать файл всем, у кого есть ссылка.' },
27
- { name: 'Вставьте ссылку в генератор', text: 'Введите полный URL-адрес Диска в текстовое поле инструмента и нажмите кнопку «Создать прямую ссылку».' },
26
+ { name: 'Скопируйте ссылку доступа', text: 'В Google Диске щелкните файл правой кнопкой мыши и выберите "Копировать ссылку". Убедитесь, что настройки доступа позволяют просматривать файл всем, у кого есть ссылка.' },
27
+ { name: 'Вставьте ссылку в генератор', text: 'Введите полный URL-адрес Диска в текстовое поле инструмента и нажмите кнопку "Создать прямую ссылку".' },
28
28
  { name: 'Скопируйте и используйте созданную ссылку', text: 'Скопируйте новую прямую ссылку на скачивание и используйте ее в электронных письмах, на веб-сайтах, в рассылках или везде, где вам нужно, чтобы файл скачивался сразу после нажатия.' },
29
29
  ];
30
30
 
@@ -52,7 +52,7 @@ const appSchema: WithContext<SoftwareApplication> = {
52
52
  applicationCategory: 'UtilitiesApplication',
53
53
  operatingSystem: 'Web',
54
54
  offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
55
- description: 'Преобразуйте любую ссылку «Поделиться» с Google Диска в прямую ссылку на скачивание одним щелчком мыши, минуя страницу предварительного просмотра файла. Бесплатный инструмент.',
55
+ description: 'Преобразуйте любую ссылку "Поделиться" с Google Диска в прямую ссылку на скачивание одним щелчком мыши, минуя страницу предварительного просмотра файла. Бесплатный инструмент.',
56
56
  };
57
57
 
58
58
  const ui: DriveDirectLinkUI = {
@@ -69,7 +69,7 @@ const ui: DriveDirectLinkUI = {
69
69
  export const content: ToolLocaleContent<DriveDirectLinkUI> = {
70
70
  slug: 'ssylka-na-pryamoe-skachivanie-google-drive',
71
71
  title: 'Генератор прямых ссылок на скачивание с Google Диска',
72
- description: 'Легко преобразуйте любую ссылку «Поделиться» с Google Диска в прямую ссылку на скачивание одним щелчком мыши, минуя предварительный просмотр файла. Бесплатный инструмент.',
72
+ description: 'Легко преобразуйте любую ссылку "Поделиться" с Google Диска в прямую ссылку на скачивание одним щелчком мыши, минуя предварительный просмотр файла. Бесплатный инструмент.',
73
73
  ui,
74
74
 
75
75
  faq: faqData,
@@ -86,7 +86,7 @@ export const content: ToolLocaleContent<DriveDirectLinkUI> = {
86
86
  { type: 'title', level: 3, text: 'Зачем использовать прямую ссылку на скачивание?' },
87
87
  {
88
88
  type: 'paragraph',
89
- html: 'Основное преимущество гораздо лучший пользовательский опыт. При обмене PDF-файлом, изображением или установщиком программного обеспечения пользователи ожидают, что нажатие на ссылку немедленно начнет загрузку:',
89
+ html: 'Основное преимущество - гораздо лучший пользовательский опыт. При обмене PDF-файлом, изображением или установщиком программного обеспечения пользователи ожидают, что нажатие на ссылку немедленно начнет загрузку:',
90
90
  },
91
91
  {
92
92
  type: 'list',
@@ -100,7 +100,7 @@ export const content: ToolLocaleContent<DriveDirectLinkUI> = {
100
100
  { type: 'title', level: 3, text: 'Как работает генератор прямых ссылок?' },
101
101
  {
102
102
  type: 'paragraph',
103
- html: 'Все происходит на 100% в вашем браузере. Ссылки «Поделиться» с Google Диска содержат уникальный идентификатор файла (ID). Инструмент извлекает этот ID и создает новый URL-адрес, используя собственный параметр экспорта Google: <code>https://drive.google.com/uc?export=download&amp;id=ВАШ_ID</code>.',
103
+ html: 'Все происходит на 100% в вашем браузере. Ссылки "Поделиться" с Google Диска содержат уникальный идентификатор файла (ID). Инструмент извлекает этот ID и создает новый URL-адрес, используя собственный параметр экспорта Google: <code>https://drive.google.com/uc?export=download&amp;id=ВАШ_ID</code>.',
104
104
  },
105
105
  {
106
106
  type: 'tip',
@@ -6,11 +6,11 @@ import type { DriveDirectLinkUI } from '../ui';
6
6
  const faqData = [
7
7
  {
8
8
  question: '如何使用 Google 云端硬盘下载转换器?',
9
- answer: '从 Google 云端硬盘中存储的任何文件复制完整的“共享”链接(必须具有公共权限)。将其粘贴到工具的文本框中,然后点击“生成直链”。将出现一个新的 URL,点击该 URL 即可开始下载,而无需打开 Google 的查看器。',
9
+ answer: '从 Google 云端硬盘中存储的任何文件复制完整的"共享"链接(必须具有公共权限)。将其粘贴到工具的文本框中,然后点击"生成直链"。将出现一个新的 URL,点击该 URL 即可开始下载,而无需打开 Google 的查看器。',
10
10
  },
11
11
  {
12
12
  question: '我可以为私有文件生成直接下载链接吗?',
13
- answer: '如果文件是私有的或访问受限,Google 会要求您使用授权账号登录。为了实现无需注册的纯净下载,文件权限必须设置为“任何拥有链接的人都可以查看”。',
13
+ answer: '如果文件是私有的或访问受限,Google 会要求您使用授权账号登录。为了实现无需注册的纯净下载,文件权限必须设置为"任何拥有链接的人都可以查看"。',
14
14
  },
15
15
  {
16
16
  question: '在这里转换我的云端硬盘链接安全吗?',
@@ -23,8 +23,8 @@ const faqData = [
23
23
  ];
24
24
 
25
25
  const howToData = [
26
- { name: '复制共享链接', text: '在 Google 云端硬盘中,右键点击文件并选择“获取链接”。确保权限允许任何拥有链接的人访问。' },
27
- { name: '将链接粘贴到生成器中', text: '在工具的文本框中输入完整的云端硬盘 URL,然后点击“生成直链”按钮。' },
26
+ { name: '复制共享链接', text: '在 Google 云端硬盘中,右键点击文件并选择"获取链接"。确保权限允许任何拥有链接的人访问。' },
27
+ { name: '将链接粘贴到生成器中', text: '在工具的文本框中输入完整的云端硬盘 URL,然后点击"生成直链"按钮。' },
28
28
  { name: '复制并使用生成的链接', text: '复制新的直接下载链接,并将其用于电子邮件、网站、新闻通讯或任何需要在点击时立即下载文件的地方。' },
29
29
  ];
30
30
 
@@ -24,7 +24,7 @@ const faqData = [
24
24
 
25
25
  const howToData = [
26
26
  { name: 'E-Mails einfügen', text: 'Kopieren Sie Ihre E-Mail-Liste und fügen Sie sie in das Textfeld ein. Sie können durch Zeilen, Kommas oder Leerzeichen getrennt sein.' },
27
- { name: 'Liste bereinigen', text: 'Klicken Sie auf die Schaltfläche Liste bereinigen“, um den Validierungs- und Duplikatentfernungsprozess zu starten.' },
27
+ { name: 'Liste bereinigen', text: 'Klicken Sie auf die Schaltfläche "Liste bereinigen", um den Validierungs- und Duplikatentfernungsprozess zu starten.' },
28
28
  { name: 'Ergebnisse überprüfen', text: 'Prüfen Sie die Statistiken, die originale vs. finale E-Mails zeigen, und überprüfen Sie die bereinigte Liste im unteren Bereich.' },
29
29
  { name: 'Kopieren oder Herunterladen', text: 'Kopieren Sie das Ergebnis direkt in die Zwischenablage oder laden Sie es als gebrauchsfertige .txt-Datei herunter.' },
30
30
  ];
@@ -92,7 +92,7 @@ export const content: ToolLocaleContent<EmailListCleanerUI> = {
92
92
  { type: 'title', level: 3, text: 'Die Risiken einer unsauberen E-Mail-Liste' },
93
93
  {
94
94
  type: 'paragraph',
95
- html: 'Das Führen einer unbereinigten Datenbank kann schwerwiegende Folgen haben. Das Versenden von E-Mails an Adressen, die nicht existieren, erzeugt einen Hard Bounce“. Wenn dieser Prozentsatz 2-3 % übersteigt, beginnen Spamfilter Ihre Domain genauer zu prüfen und Sie könnten auf einer internationalen Blacklist landen.',
95
+ html: 'Das Führen einer unbereinigten Datenbank kann schwerwiegende Folgen haben. Das Versenden von E-Mails an Adressen, die nicht existieren, erzeugt einen "Hard Bounce". Wenn dieser Prozentsatz 2-3 % übersteigt, beginnen Spamfilter Ihre Domain genauer zu prüfen und Sie könnten auf einer internationalen Blacklist landen.',
96
96
  },
97
97
  {
98
98
  type: 'tip',
@@ -24,7 +24,7 @@ const faqData = [
24
24
 
25
25
  const howToData = [
26
26
  { name: 'Wklej swoje e-maile', text: 'Skopiuj swoją listę e-mailową i wklej ją do pola tekstowego. Mogą być oddzielone liniami, przecinkami lub spacjami.' },
27
- { name: 'Wyczyść listę', text: 'Kliknij przycisk Wyczyść Listę”, aby rozpocząć proces walidacji i usuwania duplikatów.' },
27
+ { name: 'Wyczyść listę', text: 'Kliknij przycisk "Wyczyść Listę", aby rozpocząć proces walidacji i usuwania duplikatów.' },
28
28
  { name: 'Przejrzyj wyniki', text: 'Sprawdź statystyki pokazujące e-maile oryginalne vs. finalne i przejrzyj wyczyszczoną listę w dolnym panelu.' },
29
29
  { name: 'Skopiuj lub pobierz', text: 'Skopiuj wynik bezpośrednio do schowka lub pobierz go jako gotowy do użycia plik .txt.' },
30
30
  ];
@@ -92,7 +92,7 @@ export const content: ToolLocaleContent<EmailListCleanerUI> = {
92
92
  { type: 'title', level: 3, text: 'Ryzyko posiadania brudnej listy e-mailowej' },
93
93
  {
94
94
  type: 'paragraph',
95
- html: 'Utrzymywanie niewyczyszczonej bazy danych może mieć poważne konsekwencje. Wysyłanie e-maili na adresy, które nie istnieją, generuje hard bounce”. Jeśli ten procent przekroczy 2-3%, filtry antyspamowe zaczną uważnie przyglądać się Twojej domenie i możesz trafić na międzynarodową czarną listę.',
95
+ html: 'Utrzymywanie niewyczyszczonej bazy danych może mieć poważne konsekwencje. Wysyłanie e-maili na adresy, które nie istnieją, generuje "hard bounce". Jeśli ten procent przekroczy 2-3%, filtry antyspamowe zaczną uważnie przyglądać się Twojej domenie i możesz trafić na międzynarodową czarną listę.',
96
96
  },
97
97
  {
98
98
  type: 'tip',
@@ -24,7 +24,7 @@ const faqData = [
24
24
 
25
25
  const howToData = [
26
26
  { name: 'Вставьте адреса', text: 'Скопируйте список адресов и вставьте его в текстовое поле. Они могут быть разделены строками, запятыми или пробелами.' },
27
- { name: 'Очистите список', text: 'Нажмите кнопку «Очистить список», чтобы запустить процесс проверки и удаления дубликатов.' },
27
+ { name: 'Очистите список', text: 'Нажмите кнопку "Очистить список", чтобы запустить процесс проверки и удаления дубликатов.' },
28
28
  { name: 'Просмотрите результаты', text: 'Проверьте статистику, показывающую количество исходных и конечных адресов, и просмотрите очищенный список на нижней панели.' },
29
29
  { name: 'Скопируйте или скачайте', text: 'Скопируйте результат прямо в буфер обмена или скачайте его в виде готового к использованию файла .txt.' },
30
30
  ];
@@ -87,17 +87,17 @@ export const content: ToolLocaleContent<EmailListCleanerUI> = {
87
87
  { type: 'title', level: 2, text: 'Почему использование очистителя списков адресов электронной почты крайне важно?' },
88
88
  {
89
89
  type: 'paragraph',
90
- html: 'В мире цифрового маркетинга качество вашей базы данных имеет гораздо большее значение, чем ее размер. Наличие списка из 50 000 адресов бесполезно, если половина из них несуществующие адреса, содержат опечатки или дублируются. Именно здесь <strong>очиститель списков адресов</strong> становится незаменимым.',
90
+ html: 'В мире цифрового маркетинга качество вашей базы данных имеет гораздо большее значение, чем ее размер. Наличие списка из 50 000 адресов бесполезно, если половина из них - несуществующие адреса, содержат опечатки или дублируются. Именно здесь <strong>очиститель списков адресов</strong> становится незаменимым.',
91
91
  },
92
- { type: 'title', level: 3, text: 'Риски использования «грязного» списка адресов' },
92
+ { type: 'title', level: 3, text: 'Риски использования "грязного" списка адресов' },
93
93
  {
94
94
  type: 'paragraph',
95
- html: 'Использование неочищенной базы данных может иметь серьезные последствия. Отправка писем на несуществующие адреса генерирует «жесткий отказ» (hard bounce). Если этот процент превышает 2-3%, спам-фильтры начнут внимательно изучать ваш домен, и вы можете попасть в международный черный список.',
95
+ html: 'Использование неочищенной базы данных может иметь серьезные последствия. Отправка писем на несуществующие адреса генерирует "жесткий отказ" (hard bounce). Если этот процент превышает 2-3%, спам-фильтры начнут внимательно изучать ваш домен, и вы можете попасть в международный черный список.',
96
96
  },
97
97
  {
98
98
  type: 'tip',
99
99
  title: 'Совет эксперта',
100
- html: 'Никогда не покупайте списки адресов электронной почты. Это самый быстрый способ испортить репутацию вашего домена. Создавайте свой собственный список органически и очищайте его каждые 36 месяцев.',
100
+ html: 'Никогда не покупайте списки адресов электронной почты. Это самый быстрый способ испортить репутацию вашего домена. Создавайте свой собственный список органически и очищайте его каждые 3-6 месяцев.',
101
101
  },
102
102
  { type: 'title', level: 3, text: 'Как наш инструмент оптимизирует вашу базу данных' },
103
103
  {
@@ -126,12 +126,12 @@ export const content: ToolLocaleContent<EmailListCleanerUI> = {
126
126
  { type: 'title', level: 3, text: 'Влияние на окупаемость инвестиций' },
127
127
  {
128
128
  type: 'paragraph',
129
- html: 'Электронный маркетинг остается одним из каналов с самым высоким ROI, но только в том случае, если сообщения доходят до папки «Входящие». Используя <strong>очиститель адресов</strong>, вы оптимизируете свою воронку продаж у источника и перестаете платить платформам за контакты, которые никогда не увидят ваших сообщений.',
129
+ html: 'Электронный маркетинг остается одним из каналов с самым высоким ROI, но только в том случае, если сообщения доходят до папки "Входящие". Используя <strong>очиститель адресов</strong>, вы оптимизируете свою воронку продаж у источника и перестаете платить платформам за контакты, которые никогда не увидят ваших сообщений.',
130
130
  },
131
131
  {
132
132
  type: 'tip',
133
133
  title: 'Внедрите Double Opt In',
134
- html: 'Лучший способ избежать поддельных адресов требовать от пользователей подтверждения подписки путем перехода по ссылке, отправленной на их почту. Это гарантирует, что адрес существует и у пользователя есть к нему доступ.',
134
+ html: 'Лучший способ избежать поддельных адресов - требовать от пользователей подтверждения подписки путем перехода по ссылке, отправленной на их почту. Это гарантирует, что адрес существует и у пользователя есть к нему доступ.',
135
135
  },
136
136
  ],
137
137
  };
@@ -92,7 +92,7 @@ export const content: ToolLocaleContent<EmailListCleanerUI> = {
92
92
  { type: 'title', level: 3, text: 'Riskerna med en smutsig e-postlista' },
93
93
  {
94
94
  type: 'paragraph',
95
- html: 'Att behålla en orensad databas kan få allvarliga konsekvenser. Att skicka e-post till adresser som inte existerar genererar en "hard bounce". Om denna procentsats överstiger 23 % kommer spamfilter att börja granska din domän noggrant och du kan hamna på en internationell svartlista.',
95
+ html: 'Att behålla en orensad databas kan få allvarliga konsekvenser. Att skicka e-post till adresser som inte existerar genererar en "hard bounce". Om denna procentsats överstiger 2-3 % kommer spamfilter att börja granska din domän noggrant och du kan hamna på en internationell svartlista.',
96
96
  },
97
97
  {
98
98
  type: 'tip',
@@ -24,7 +24,7 @@ const faqData = [
24
24
 
25
25
  const howToData = [
26
26
  { name: '粘贴电子邮件', text: '复制您的电子邮件列表并将其粘贴到文本框中。可以使用换行、逗号或空格进行分隔。' },
27
- { name: '清洗列表', text: '点击“清洗列表”按钮开始验证和去重过程。' },
27
+ { name: '清洗列表', text: '点击"清洗列表"按钮开始验证和去重过程。' },
28
28
  { name: '查看结果', text: '检查显示原始邮件数与最终邮件数的统计信息,并在下方面板查阅清洗后的列表。' },
29
29
  { name: '复制或下载', text: '直接将结果复制到剪贴板,或下载为开箱即用的 .txt 文件。' },
30
30
  ];
@@ -89,10 +89,10 @@ export const content: ToolLocaleContent<EmailListCleanerUI> = {
89
89
  type: 'paragraph',
90
90
  html: '在数字营销领域,数据库的质量远比其规模重要。如果你拥有一个包含 50,000 个电子邮件的列表,但其中一半是不存在的地址、包含错别字或者是重复的,那么这个列表就毫无价值。这就是<strong>邮件列表清洗工具</strong>变得不可或缺的原因。',
91
91
  },
92
- { type: 'title', level: 3, text: '“脏”邮件列表的风险' },
92
+ { type: 'title', level: 3, text: '"脏"邮件列表的风险' },
93
93
  {
94
94
  type: 'paragraph',
95
- html: '维护一个未经清洗的数据库可能会导致严重的后果。向不存在的地址发送电子邮件会产生“硬退信”。如果这个比例超过 2-3%,垃圾邮件过滤器就会开始仔细审查您的域名,您最终可能会被列入国际黑名单。',
95
+ html: '维护一个未经清洗的数据库可能会导致严重的后果。向不存在的地址发送电子邮件会产生"硬退信"。如果这个比例超过 2-3%,垃圾邮件过滤器就会开始仔细审查您的域名,您最终可能会被列入国际黑名单。',
96
96
  },
97
97
  {
98
98
  type: 'tip',