@jjlmoya/utils-audiovisual 1.18.0 → 1.20.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 +1 -1
  3. package/src/category/i18n/fr.ts +1 -1
  4. package/src/category/i18n/ru.ts +1 -1
  5. package/src/category/index.ts +2 -0
  6. package/src/entries.ts +4 -1
  7. package/src/index.ts +1 -0
  8. package/src/tests/diacritics_density.test.ts +118 -0
  9. package/src/tests/inverted_punctuation.test.ts +84 -0
  10. package/src/tests/locale_completeness.test.ts +2 -2
  11. package/src/tests/no_en_dash.test.ts +70 -0
  12. package/src/tests/script_density.test.ts +94 -0
  13. package/src/tests/tool_validation.test.ts +2 -2
  14. package/src/tool/chromaticLens/i18n/de.ts +6 -6
  15. package/src/tool/chromaticLens/i18n/fr.ts +3 -3
  16. package/src/tool/chromaticLens/i18n/pl.ts +1 -1
  17. package/src/tool/chromaticLens/i18n/ru.ts +10 -10
  18. package/src/tool/chromaticLens/i18n/zh.ts +2 -2
  19. package/src/tool/collageMaker/i18n/de.ts +6 -6
  20. package/src/tool/collageMaker/i18n/fr.ts +4 -4
  21. package/src/tool/collageMaker/i18n/pl.ts +5 -5
  22. package/src/tool/collageMaker/i18n/ru.ts +12 -12
  23. package/src/tool/collageMaker/i18n/sv.ts +3 -3
  24. package/src/tool/collageMaker/i18n/zh.ts +1 -1
  25. package/src/tool/depthOfFieldCalculator/i18n/de.ts +3 -3
  26. package/src/tool/depthOfFieldCalculator/i18n/en.ts +7 -7
  27. package/src/tool/depthOfFieldCalculator/i18n/es.ts +2 -2
  28. package/src/tool/depthOfFieldCalculator/i18n/fr.ts +6 -6
  29. package/src/tool/depthOfFieldCalculator/i18n/id.ts +2 -2
  30. package/src/tool/depthOfFieldCalculator/i18n/it.ts +2 -2
  31. package/src/tool/depthOfFieldCalculator/i18n/ja.ts +1 -1
  32. package/src/tool/depthOfFieldCalculator/i18n/ko.ts +1 -1
  33. package/src/tool/depthOfFieldCalculator/i18n/nl.ts +2 -2
  34. package/src/tool/depthOfFieldCalculator/i18n/pl.ts +2 -2
  35. package/src/tool/depthOfFieldCalculator/i18n/pt.ts +2 -2
  36. package/src/tool/depthOfFieldCalculator/i18n/ru.ts +6 -6
  37. package/src/tool/depthOfFieldCalculator/i18n/sv.ts +2 -2
  38. package/src/tool/depthOfFieldCalculator/i18n/tr.ts +1 -1
  39. package/src/tool/depthOfFieldCalculator/i18n/zh.ts +3 -3
  40. package/src/tool/exifCleaner/i18n/de.ts +8 -8
  41. package/src/tool/exifCleaner/i18n/fr.ts +11 -11
  42. package/src/tool/exifCleaner/i18n/pl.ts +6 -6
  43. package/src/tool/exifCleaner/i18n/ru.ts +14 -14
  44. package/src/tool/exifCleaner/i18n/zh.ts +3 -3
  45. package/src/tool/imageCompressor/i18n/de.ts +16 -16
  46. package/src/tool/imageCompressor/i18n/fr.ts +8 -8
  47. package/src/tool/imageCompressor/i18n/pl.ts +1 -1
  48. package/src/tool/imageCompressor/i18n/ru.ts +9 -9
  49. package/src/tool/imageCompressor/i18n/zh.ts +1 -1
  50. package/src/tool/printQualityCalculator/component.astro +118 -110
  51. package/src/tool/printQualityCalculator/i18n/de.ts +5 -5
  52. package/src/tool/printQualityCalculator/i18n/fr.ts +11 -11
  53. package/src/tool/printQualityCalculator/i18n/pl.ts +4 -4
  54. package/src/tool/printQualityCalculator/i18n/ru.ts +11 -11
  55. package/src/tool/printQualityCalculator/i18n/zh.ts +4 -4
  56. package/src/tool/printQualityCalculator/print-quality-calculator-pixels-to-cm-dpi.css +193 -40
  57. package/src/tool/privacyBlur/i18n/de.ts +5 -5
  58. package/src/tool/privacyBlur/i18n/fr.ts +9 -9
  59. package/src/tool/privacyBlur/i18n/ru.ts +6 -6
  60. package/src/tool/subtitleSync/i18n/de.ts +1 -1
  61. package/src/tool/subtitleSync/i18n/fr.ts +9 -9
  62. package/src/tool/subtitleSync/i18n/ru.ts +6 -6
  63. package/src/tool/subtitleSync/i18n/sv.ts +4 -4
  64. package/src/tool/timelapseCalculator/i18n/fr.ts +6 -6
  65. package/src/tool/timelapseCalculator/i18n/ru.ts +3 -3
  66. package/src/tool/timelapseCalculator/i18n/zh.ts +4 -4
  67. package/src/tool/tvDistance/i18n/fr.ts +2 -2
  68. package/src/tool/tvDistance/i18n/ru.ts +9 -9
  69. package/src/tool/tvDistance/i18n/zh.ts +4 -4
  70. package/src/tool/videoFrameExtractor/i18n/fr.ts +8 -8
  71. package/src/tool/videoFrameExtractor/i18n/ru.ts +2 -2
  72. package/src/tool/videoMerger/bibliography.astro +17 -0
  73. package/src/tool/videoMerger/bibliography.ts +16 -0
  74. package/src/tool/videoMerger/component.astro +400 -0
  75. package/src/tool/videoMerger/entry.ts +51 -0
  76. package/src/tool/videoMerger/i18n/de.ts +205 -0
  77. package/src/tool/videoMerger/i18n/en.ts +205 -0
  78. package/src/tool/videoMerger/i18n/es.ts +205 -0
  79. package/src/tool/videoMerger/i18n/fr.ts +205 -0
  80. package/src/tool/videoMerger/i18n/id.ts +205 -0
  81. package/src/tool/videoMerger/i18n/it.ts +205 -0
  82. package/src/tool/videoMerger/i18n/ja.ts +205 -0
  83. package/src/tool/videoMerger/i18n/ko.ts +205 -0
  84. package/src/tool/videoMerger/i18n/nl.ts +205 -0
  85. package/src/tool/videoMerger/i18n/pl.ts +205 -0
  86. package/src/tool/videoMerger/i18n/pt.ts +205 -0
  87. package/src/tool/videoMerger/i18n/ru.ts +205 -0
  88. package/src/tool/videoMerger/i18n/sv.ts +205 -0
  89. package/src/tool/videoMerger/i18n/tr.ts +205 -0
  90. package/src/tool/videoMerger/i18n/zh.ts +205 -0
  91. package/src/tool/videoMerger/index.ts +11 -0
  92. package/src/tool/videoMerger/logic.ts +263 -0
  93. package/src/tool/videoMerger/online-video-merger.css +440 -0
  94. package/src/tool/videoMerger/seo.astro +15 -0
  95. package/src/tools.ts +2 -0
@@ -17,34 +17,40 @@
17
17
 
18
18
  .pq-wrapper {
19
19
  width: 100%;
20
- max-width: 56rem;
20
+ max-width: 48rem;
21
21
  margin: 0 auto;
22
- display: flex;
23
- flex-direction: column;
24
- gap: 2rem;
25
- padding: 1rem;
22
+ padding: 0.75rem;
23
+ }
24
+
25
+ .pq-tool-card {
26
+ background: #fff;
27
+ border: 1px solid #e2e8f0;
28
+ border-radius: 1rem;
29
+ box-shadow: 0 12px 32px -16px rgba(15, 23, 42, 0.22);
30
+ overflow: hidden;
31
+ }
32
+
33
+ .theme-dark .pq-tool-card {
34
+ background: #0f172a;
35
+ border-color: #1e293b;
26
36
  }
27
37
 
28
38
  .pq-hidden {
29
- display: none;
39
+ display: none;
30
40
  }
31
41
 
32
42
  /* Drop zone */
33
43
  .pq-drop-zone {
34
44
  position: relative;
35
45
  cursor: pointer;
36
- border: 2px dashed var(--pq-border);
37
- border-radius: 1rem;
38
- padding: 3rem;
46
+ border: 0;
47
+ border-radius: 0;
48
+ padding: 2rem 1.25rem;
39
49
  transition: border-color 0.3s, background 0.3s;
40
50
  }
41
51
 
42
- .theme-dark .pq-drop-zone {
43
- border-color: var(--pq-border);
44
- }
45
52
  .pq-drop-zone:hover,
46
53
  .pq-drop-zone.pq-drop-active {
47
- border-color: var(--pq-primary);
48
54
  background: rgba(99, 102, 241, 0.04);
49
55
  }
50
56
  .pq-drop-inner {
@@ -104,8 +110,14 @@
104
110
  .pq-results {
105
111
  display: flex;
106
112
  flex-direction: column;
107
- gap: 2rem;
113
+ gap: 1.25rem;
114
+ padding: 1rem;
108
115
  animation: pq-fade-up 0.5s ease-out;
116
+ min-width: 0;
117
+ }
118
+
119
+ .pq-results.pq-hidden {
120
+ display: none;
109
121
  }
110
122
 
111
123
  @keyframes pq-fade-up {
@@ -121,49 +133,103 @@
121
133
 
122
134
  /* File card */
123
135
  .pq-file-card {
124
- background: #fff;
125
- border: 1px solid #e2e8f0;
126
- border-radius: 0.75rem;
127
- box-shadow: 0 4px 24px -8px rgba(0,0,0,0.1);
128
- padding: 1.5rem;
136
+ background: transparent;
137
+ border: 0;
138
+ border-bottom: 1px solid #e2e8f0;
139
+ border-radius: 0;
140
+ box-shadow: none;
141
+ padding: 0 0 1rem;
142
+ min-width: 0;
129
143
  }
130
144
 
131
145
  .theme-dark .pq-file-card {
132
- background: #0f172a;
133
146
  border-color: #1e293b;
134
147
  }
135
148
  .pq-file-left {
136
149
  display: flex;
137
- align-items: center;
138
- gap: 1.5rem;
150
+ align-items: flex-start;
151
+ gap: 1rem;
152
+ min-width: 0;
139
153
  }
140
154
  .pq-preview-wrap {
141
155
  position: relative;
142
- width: 8rem;
143
- height: 8rem;
156
+ width: 7rem;
157
+ height: 7rem;
144
158
  flex-shrink: 0;
145
159
  }
146
160
  .pq-preview-img {
147
161
  width: 100%;
148
162
  height: 100%;
149
163
  object-fit: cover;
150
- border-radius: 0.5rem;
151
- box-shadow: 0 2px 8px rgba(0,0,0,0.15);
164
+ border-radius: 0.75rem;
165
+ border: 1px solid rgba(148, 163, 184, 0.22);
166
+ box-shadow: 0 12px 28px -20px rgba(15, 23, 42, 0.7);
152
167
  }
153
168
  .pq-file-name {
154
- font-weight: 700;
155
- color: #0f172a;
156
- font-size: 1rem;
157
- margin: 0 0 0.5rem;
169
+ color: #64748b;
170
+ font-size: 0.8rem;
171
+ line-height: 1.4;
172
+ margin: 0;
158
173
  overflow: hidden;
159
174
  text-overflow: ellipsis;
160
175
  white-space: nowrap;
161
- max-width: 20rem;
176
+ max-width: 100%;
177
+ }
178
+
179
+ .pq-file-info {
180
+ min-width: 0;
181
+ flex: 1;
182
+ }
183
+
184
+ .pq-file-header {
185
+ display: flex;
186
+ align-items: center;
187
+ justify-content: space-between;
188
+ gap: 0.75rem;
189
+ margin-bottom: 0.75rem;
162
190
  }
163
191
 
164
192
  .theme-dark .pq-file-name {
165
- color: #f1f5f9;
193
+ color: #94a3b8;
166
194
  }
195
+ .pq-replace-btn {
196
+ display: inline-flex;
197
+ align-items: center;
198
+ gap: 0.4rem;
199
+ justify-content: center;
200
+ padding: 0.625rem 0.9rem;
201
+ border: 1px solid rgba(99, 102, 241, 0.35);
202
+ border-radius: 999px;
203
+ background: linear-gradient(180deg, rgba(238, 242, 255, 0.9), rgba(224, 231, 255, 0.8));
204
+ color: #3730a3;
205
+ font-size: 0.8rem;
206
+ font-weight: 600;
207
+ cursor: pointer;
208
+ white-space: nowrap;
209
+ flex-shrink: 0;
210
+ box-shadow: 0 8px 20px -16px rgba(67, 56, 202, 0.65);
211
+ transition: transform 0.2s, background 0.2s, border-color 0.2s, color 0.2s;
212
+ }
213
+
214
+ .pq-replace-btn:hover {
215
+ background: linear-gradient(180deg, rgba(224, 231, 255, 1), rgba(199, 210, 254, 0.92));
216
+ border-color: rgba(99, 102, 241, 0.5);
217
+ color: #312e81;
218
+ transform: translateY(-1px);
219
+ }
220
+
221
+ .theme-dark .pq-replace-btn {
222
+ background: linear-gradient(180deg, rgba(49, 46, 129, 0.32), rgba(30, 41, 59, 0.92));
223
+ border-color: rgba(129, 140, 248, 0.45);
224
+ color: #e0e7ff;
225
+ }
226
+
227
+ .theme-dark .pq-replace-btn:hover {
228
+ background: linear-gradient(180deg, rgba(67, 56, 202, 0.35), rgba(30, 41, 59, 0.96));
229
+ border-color: rgba(165, 180, 252, 0.6);
230
+ color: #c7d2fe;
231
+ }
232
+
167
233
  .pq-file-meta {
168
234
  display: flex;
169
235
  flex-wrap: wrap;
@@ -181,7 +247,8 @@
181
247
  .pq-main-grid {
182
248
  display: grid;
183
249
  grid-template-columns: 1fr 1fr;
184
- gap: 2rem;
250
+ gap: 1.25rem;
251
+ min-width: 0;
185
252
  }
186
253
 
187
254
  @media (max-width: 640px) {
@@ -191,9 +258,10 @@
191
258
  /* Config panel */
192
259
  .pq-config-panel {
193
260
  background: #f8fafc;
194
- border: 1px solid #e2e8f0;
261
+ border: 0;
195
262
  border-radius: 0.75rem;
196
- padding: 1.5rem;
263
+ padding: 1.25rem;
264
+ min-width: 0;
197
265
  }
198
266
 
199
267
  .theme-dark .pq-config-panel {
@@ -294,6 +362,7 @@
294
362
  /* Output panel – gradient */
295
363
  .pq-output-panel {
296
364
  display: flex;
365
+ min-width: 0;
297
366
  }
298
367
  .pq-output-gradient {
299
368
  flex: 1;
@@ -302,11 +371,12 @@
302
371
  justify-content: center;
303
372
  background: linear-gradient(135deg, #6366f1, #a855f7);
304
373
  border-radius: 0.75rem;
305
- padding: 1.5rem;
374
+ padding: 1.25rem;
306
375
  color: #fff;
307
376
  box-shadow: 0 10px 40px -10px rgba(99,102,241,0.4);
308
377
  position: relative;
309
378
  overflow: hidden;
379
+ min-width: 0;
310
380
  }
311
381
  .pq-output-blur-bg {
312
382
  position: absolute;
@@ -330,11 +400,14 @@
330
400
  .pq-output-size {
331
401
  display: flex;
332
402
  align-items: baseline;
403
+ flex-wrap: wrap;
333
404
  gap: 0.5rem;
334
405
  font-size: 2.5rem;
335
406
  font-weight: 900;
336
407
  margin-bottom: 0.25rem;
337
408
  position: relative;
409
+ line-height: 1.1;
410
+ min-width: 0;
338
411
  }
339
412
  .pq-size-sep {
340
413
  font-size: 1.5rem;
@@ -357,6 +430,7 @@
357
430
  .pq-quality-badge {
358
431
  display: inline-flex;
359
432
  align-items: center;
433
+ flex-wrap: wrap;
360
434
  gap: 0.375rem;
361
435
  padding: 0.25rem 0.75rem;
362
436
  border-radius: 999px;
@@ -394,14 +468,15 @@
394
468
 
395
469
  /* Formats */
396
470
  .pq-formats-card {
397
- background: #fff;
398
- border: 1px solid #e2e8f0;
471
+ background: transparent;
472
+ border: 0;
473
+ border-top: 1px solid #e2e8f0;
399
474
  border-radius: 0.75rem;
400
- padding: 1.5rem;
475
+ padding: 1rem 0 0;
476
+ min-width: 0;
401
477
  }
402
478
 
403
479
  .theme-dark .pq-formats-card {
404
- background: #0f172a;
405
480
  border-color: #1e293b;
406
481
  }
407
482
  .pq-formats-title {
@@ -419,11 +494,13 @@
419
494
  }
420
495
  .pq-formats-scroll {
421
496
  overflow-x: auto;
497
+ max-width: 100%;
422
498
  }
423
499
  .pq-formats-table {
424
500
  width: 100%;
425
501
  border-collapse: collapse;
426
502
  font-size: 0.875rem;
503
+ min-width: 34rem;
427
504
  }
428
505
  .pq-formats-table th {
429
506
  text-align: left;
@@ -489,3 +566,79 @@
489
566
  .theme-dark .pq-status-no {
490
567
  color: #fbbf24;
491
568
  }
569
+
570
+ @media (max-width: 640px) {
571
+ .pq-wrapper {
572
+ padding: 0.5rem;
573
+ }
574
+
575
+ .pq-results {
576
+ padding: 0.875rem;
577
+ }
578
+
579
+ .pq-drop-zone {
580
+ padding: 1.5rem 1rem;
581
+ }
582
+
583
+ .pq-file-left {
584
+ flex-direction: column;
585
+ align-items: stretch;
586
+ }
587
+
588
+ .pq-preview-wrap {
589
+ width: 100%;
590
+ max-width: 14rem;
591
+ height: auto;
592
+ aspect-ratio: 1;
593
+ margin: 0 auto;
594
+ }
595
+
596
+ .pq-file-name {
597
+ text-align: center;
598
+ white-space: normal;
599
+ overflow-wrap: anywhere;
600
+ }
601
+
602
+ .pq-file-header {
603
+ flex-direction: column;
604
+ align-items: stretch;
605
+ }
606
+
607
+ .pq-replace-btn {
608
+ justify-content: center;
609
+ width: 100%;
610
+ white-space: normal;
611
+ }
612
+
613
+ .pq-file-meta {
614
+ gap: 0.75rem;
615
+ }
616
+
617
+ .pq-dpi-row {
618
+ flex-wrap: wrap;
619
+ align-items: stretch;
620
+ gap: 0.75rem;
621
+ }
622
+
623
+ .pq-dpi-number {
624
+ width: 100%;
625
+ }
626
+
627
+ .pq-output-gradient {
628
+ padding: 1rem;
629
+ }
630
+
631
+ .pq-output-size {
632
+ font-size: 1.9rem;
633
+ gap: 0.35rem;
634
+ }
635
+
636
+ .pq-size-sep,
637
+ .pq-size-unit {
638
+ font-size: 1rem;
639
+ }
640
+
641
+ .pq-formats-card {
642
+ padding: 1rem 0 0;
643
+ }
644
+ }
@@ -4,7 +4,7 @@ import type { PrivacyBlurUI, PrivacyBlurLocaleContent } from '../index';
4
4
 
5
5
  const slug = 'online-privatsphaere-editor-gesichter-verpixeln-unscharf-machen';
6
6
  const title = 'Online Privatsphäre Editor: Gesichter in Fotos verpixeln und verbergen';
7
- const description = 'Schützen Sie Ihre Identität, indem Sie sensible Bereiche Ihrer Fotos zensieren. Verpixeln Sie Gesichter, machen Sie Dokumente unscharf oder decken Sie vertrauliche Informationen zu 100 % lokal.';
7
+ const description = 'Schützen Sie Ihre Identität, indem Sie sensible Bereiche Ihrer Fotos zensieren. Verpixeln Sie Gesichter, machen Sie Dokumente unscharf oder decken Sie vertrauliche Informationen zu - 100 % lokal.';
8
8
 
9
9
  const ui: PrivacyBlurUI = {
10
10
  toolPixel: "Verpixeln",
@@ -59,7 +59,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
59
59
  items: [
60
60
  'Drei Bearbeitungswerkzeuge: Verpixeln, Unschärfe, Solide Füllung',
61
61
  'Automatische Gesichtserkennung mit KI (TinyFaceDetector)',
62
- '100 % lokale Verarbeitung Ihre Fotos verlassen nie den Browser',
62
+ '100 % lokale Verarbeitung - Ihre Fotos verlassen nie den Browser',
63
63
  'Keine Wasserzeichen, keine Limits, völlig kostenlos'
64
64
  ]
65
65
  },
@@ -87,7 +87,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
87
87
  },
88
88
  {
89
89
  title: 'Unschärfe (Blur)',
90
- description: 'Gaußscher Weichzeichner natürlicheres Aussehen',
90
+ description: 'Gaußscher Weichzeichner - natürlicheres Aussehen',
91
91
  icon: 'mdi:blur-off',
92
92
  points: [
93
93
  'Eleganteres visuelles Erscheinungsbild',
@@ -99,7 +99,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
99
99
  },
100
100
  {
101
101
  title: 'Solide Füllung',
102
- description: 'Undurchsichtiger Farbblock maximale Privatsphäre',
102
+ description: 'Undurchsichtiger Farbblock - maximale Privatsphäre',
103
103
  icon: 'mdi:rectangle',
104
104
  points: [
105
105
  'Deutliches, offensichtliches Verbergen',
@@ -139,7 +139,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
139
139
  },
140
140
  {
141
141
  pro: 'Automatische Gesichtserkennung spart Zeit bei der manuellen Arbeit',
142
- con: 'KI ist nicht perfekt Profilansichten oder Teilgesichter werden evtl. nicht erkannt'
142
+ con: 'KI ist nicht perfekt - Profilansichten oder Teilgesichter werden evtl. nicht erkannt'
143
143
  },
144
144
  {
145
145
  pro: 'Drei Methoden erlauben Wahl zwischen Sicherheit und Ästhetik',
@@ -55,15 +55,15 @@ const howTo: PrivacyBlurLocaleContent['howTo'] = [
55
55
  const seo: PrivacyBlurLocaleContent['seo'] = [
56
56
  {
57
57
  type: 'summary',
58
- title: 'Éditeur de Confidentialité : Pixéliser, Flouter et Masquer',
58
+ title: 'Éditeur de Confidentialité: Pixéliser, Flouter et Masquer',
59
59
  items: [
60
- 'Trois outils d\'édition : Pixeliser, Flouter, Masquage Solide',
60
+ 'Trois outils d\'édition: Pixeliser, Flouter, Masquage Solide',
61
61
  'Détection automatique des visages par IA (TinyFaceDetector)',
62
62
  'Traitement 100 % local - vos photos ne quittent jamais le navigateur',
63
63
  'Sans filigrane, sans limites, complètement gratuit'
64
64
  ]
65
65
  },
66
- { type: 'title', text: 'Confidentialité Numérique : Comment Protéger vos Données Visuelles', level: 2 },
66
+ { type: 'title', text: 'Confidentialité Numérique: Comment Protéger vos Données Visuelles', level: 2 },
67
67
  { type: 'paragraph', html: 'À l\'ère des réseaux sociaux, partager des photos sans contrôle peut exposer des données personnelles sensibles. Notre outil permet de masquer les informations critiques (visages, plaques d\'immatriculation, noms, adresses) avant de les mettre en ligne, garantissant que votre vie privée reste sous votre contrôle total.' },
68
68
 
69
69
  { type: 'stats', items: [
@@ -82,7 +82,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
82
82
  'Obfuscation irréversible maximale',
83
83
  'Plus sûr contre la reconnaissance faciale',
84
84
  'Visible, il est clair que quelque chose a été masqué',
85
- 'Idéal : visages sur photos publiques'
85
+ 'Idéal: visages sur photos publiques'
86
86
  ]
87
87
  },
88
88
  {
@@ -93,7 +93,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
93
93
  'Aspect visuel plus élégant',
94
94
  'Conserve une certaine cohérence des tons',
95
95
  'Mathématiquement réversible (théoriquement)',
96
- 'Idéal : informations moins sensibles'
96
+ 'Idéal: informations moins sensibles'
97
97
  ],
98
98
  highlight: true
99
99
  },
@@ -105,7 +105,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
105
105
  'Visible, masquage évident',
106
106
  'Sécurité juridique/confidentialité maximale',
107
107
  'Modifie la composition visuelle',
108
- 'Idéal : documents, données sensibles'
108
+ 'Idéal: documents, données sensibles'
109
109
  ]
110
110
  }
111
111
  ], columns: 3 },
@@ -115,7 +115,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
115
115
  { type: 'list', items: [
116
116
  '<strong>100 % Local :</strong> Le modèle d\'IA s\'exécute sur votre GPU/CPU, pas sur des serveurs distants.',
117
117
  '<strong>Sans Internet :</strong> Après le téléchargement initial, il fonctionne complètement hors ligne.',
118
- '<strong>Confidentialité Garantie :</strong> Personne ne voit les visages : ni Google, ni OpenAI, ni nous.',
118
+ '<strong>Confidentialité Garantie :</strong> Personne ne voit les visages: ni Google, ni OpenAI, ni nous.',
119
119
  '<strong>Un Clic Automatique :</strong> Détecte les visages et vous permet de choisir de les masquer en un clic.'
120
120
  ], icon: 'mdi:check' },
121
121
 
@@ -134,7 +134,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
134
134
 
135
135
  { type: 'proscons', items: [
136
136
  {
137
- pro: 'Confidentialité totale : traitement 100 % local, ni serveurs, ni stockage',
137
+ pro: 'Confidentialité totale: traitement 100 % local, ni serveurs, ni stockage',
138
138
  con: 'Nécessite un navigateur moderne supportant Canvas et WebGL'
139
139
  },
140
140
  {
@@ -151,7 +151,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
151
151
  }
152
152
  ], proTitle: 'Avantages', conTitle: 'Limitations' },
153
153
 
154
- { type: 'diagnostic', variant: 'warning', title: 'Avertissement : Flouter n\'est pas 100 % Sûr', icon: 'mdi:alert', badge: 'Sécurité', html: 'Le flou gaussien est mathématiquement réversible via des algorithmes sophistiqués. Si l\'information est CRITIQUE (documents légaux, identité), utilisez <strong>Pixeliser ou Masquage Solide</strong>. Le flou est esthétiquement meilleur mais moins sûr.' },
154
+ { type: 'diagnostic', variant: 'warning', title: 'Avertissement: Flouter n\'est pas 100 % Sûr', icon: 'mdi:alert', badge: 'Sécurité', html: 'Le flou gaussien est mathématiquement réversible via des algorithmes sophistiqués. Si l\'information est CRITIQUE (documents légaux, identité), utilisez <strong>Pixeliser ou Masquage Solide</strong>. Le flou est esthétiquement meilleur mais moins sûr.' },
155
155
 
156
156
  { type: 'glossary', items: [
157
157
  {
@@ -59,7 +59,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
59
59
  items: [
60
60
  'Три инструмента редактирования: Пикселизация, Размытие, Сплошное покрытие',
61
61
  'Автоматическое обнаружение лиц с помощью ИИ (TinyFaceDetector)',
62
- '100% локальная обработка ваши фото никогда не покидают браузер',
62
+ '100% локальная обработка - ваши фото никогда не покидают браузер',
63
63
  'Без водяных знаков, без ограничений, полностью бесплатно'
64
64
  ]
65
65
  },
@@ -87,7 +87,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
87
87
  },
88
88
  {
89
89
  title: 'Размытие (Blur)',
90
- description: 'Гауссово сглаживание более естественный вид',
90
+ description: 'Гауссово сглаживание - более естественный вид',
91
91
  icon: 'mdi:blur-off',
92
92
  points: [
93
93
  'Более элегантный визуальный вид',
@@ -99,7 +99,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
99
99
  },
100
100
  {
101
101
  title: 'Сплошное покрытие',
102
- description: 'Непрозрачный блок цвета максимальная приватность',
102
+ description: 'Непрозрачный блок цвета - максимальная приватность',
103
103
  icon: 'mdi:rectangle',
104
104
  points: [
105
105
  'Заметное, очевидное скрытие',
@@ -111,7 +111,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
111
111
  ], columns: 3 },
112
112
 
113
113
  { type: 'title', text: 'Автоматическое обнаружение лиц с помощью ИИ', level: 3 },
114
- { type: 'paragraph', html: 'Наш инструмент использует TinyFaceDetector компактную нейронную сеть, которая запускается прямо в вашем браузере для автоматической идентификации лиц:' },
114
+ { type: 'paragraph', html: 'Наш инструмент использует TinyFaceDetector - компактную нейронную сеть, которая запускается прямо в вашем браузере для автоматической идентификации лиц:' },
115
115
  { type: 'list', items: [
116
116
  '<strong>100% Локально:</strong> Модель ИИ работает на вашем GPU/CPU, а не на удаленных серверах.',
117
117
  '<strong>Без интернета:</strong> После первоначальной загрузки работает полностью оффлайн.',
@@ -139,7 +139,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
139
139
  },
140
140
  {
141
141
  pro: 'Автоматическое обнаружение лиц экономит время',
142
- con: 'ИИ не идеален лица в профиль или частично закрытые могут быть не найдены'
142
+ con: 'ИИ не идеален - лица в профиль или частично закрытые могут быть не найдены'
143
143
  },
144
144
  {
145
145
  pro: 'Три метода позволяют выбирать между безопасностью и эстетикой',
@@ -172,7 +172,7 @@ const seo: PrivacyBlurLocaleContent['seo'] = [
172
172
  },
173
173
  {
174
174
  term: 'Privacy by Design (PbD)',
175
- definition: 'Подход, при котором приватность интегрируется на этапе проектирования системы, а не добавляется позже. Наш локальный подход это Privacy by Design.'
175
+ definition: 'Подход, при котором приватность интегрируется на этапе проектирования системы, а не добавляется позже. Наш локальный подход - это Privacy by Design.'
176
176
  }
177
177
  ] },
178
178
 
@@ -67,7 +67,7 @@ const seo: SubtitleSyncLocaleContent['seo'] = [
67
67
  items: [
68
68
  'Sofortige Korrektur von Ton-Untertitel-Versätzen',
69
69
  'Unterstützt Standard-SRT-Dateien (SubRip)',
70
- '100 % lokale Verarbeitung maximale Privatsphäre',
70
+ '100 % lokale Verarbeitung - maximale Privatsphäre',
71
71
  'Keine Installation, kein Abonnement, völlig kostenlos'
72
72
  ]
73
73
  },
@@ -11,7 +11,7 @@ const ui: SubtitleSyncUI = {
11
11
  dropSubtitle: "ou cliquez pour parcourir",
12
12
  adjustTitle: "Ajuster le temps",
13
13
  offsetLabel: "Décalage (secondes)",
14
- offsetHelp: "Utilisez des valeurs négatives pour avancer (ex : -1.5) et positives pour retarder.",
14
+ offsetHelp: "Utilisez des valeurs négatives pour avancer (ex: -1.5) et positives pour retarder.",
15
15
  linesStat: "Lignes",
16
16
  firstStat: "Premier sous-titre",
17
17
  lastStat: "Dernier sous-titre",
@@ -25,7 +25,7 @@ const ui: SubtitleSyncUI = {
25
25
  const faq: SubtitleSyncLocaleContent['faq'] = [
26
26
  {
27
27
  question: "Comment synchroniser mes sous-titres si l'audio est en avance ?",
28
- answer: "Si l'audio apparaît avant le texte, vous devez retarder les sous-titres. Entrez une valeur positive dans la calculatrice (ex : 2.0 pour les retarder de 2 secondes).",
28
+ answer: "Si l'audio apparaît avant le texte, vous devez retarder les sous-titres. Entrez une valeur positive dans la calculatrice (ex: 2.0 pour les retarder de 2 secondes).",
29
29
  },
30
30
  {
31
31
  question: "Quels formats de fichiers cet outil accepte-t-il ?",
@@ -72,7 +72,7 @@ const seo: SubtitleSyncLocaleContent['seo'] = [
72
72
  ]
73
73
  },
74
74
  { type: 'title', text: 'Synchronisation Parfaite de Sous titres SRT', level: 2 },
75
- { type: 'paragraph', html: 'Il n\'y a rien de plus frustrant que de voir des dialogues qui ne correspondent pas aux voix. Le décalage des sous-titres survient souvent à cause de différences entre les versions vidéo : variations de fréquence d\'images, coupures publicitaires, intros de production ou changements de compression. Avec cet outil, vous réglez le problème en quelques secondes.' },
75
+ { type: 'paragraph', html: 'Il n\'y a rien de plus frustrant que de voir des dialogues qui ne correspondent pas aux voix. Le décalage des sous-titres survient souvent à cause de différences entre les versions vidéo: variations de fréquence d\'images, coupures publicitaires, intros de production ou changements de compression. Avec cet outil, vous réglez le problème en quelques secondes.' },
76
76
 
77
77
  { type: 'stats', items: [
78
78
  { value: '100 %', label: 'Traitement Local', icon: 'mdi:shield' },
@@ -80,11 +80,11 @@ const seo: SubtitleSyncLocaleContent['seo'] = [
80
80
  { value: 'Illimité', label: 'Sans Limite de Fichier', icon: 'mdi:file-document' }
81
81
  ], columns: 3 },
82
82
 
83
- { type: 'title', text: 'Avancer vs Retarder : Guide Pratique', level: 3 },
83
+ { type: 'title', text: 'Avancer vs Retarder: Guide Pratique', level: 3 },
84
84
  { type: 'paragraph', html: 'La première étape consiste à identifier correctement le type de décalage. Voici la logique :' },
85
85
  { type: 'list', items: [
86
- '<strong>Retarder (Valeur Positive) :</strong> Quand le texte apparaît AVANT le son. Les sous-titres sont en avance. Exemple : +2.0 secondes.',
87
- '<strong>Avancer (Valeur Négative) :</strong> Quand le texte apparaît APRÈS le son. Les sous-titres sont en retard. Exemple : -2.0 secondes.',
86
+ '<strong>Retarder (Valeur Positive) :</strong> Quand le texte apparaît AVANT le son. Les sous-titres sont en avance. Exemple: +2.0 secondes.',
87
+ '<strong>Avancer (Valeur Négative) :</strong> Quand le texte apparaît APRÈS le son. Les sous-titres sont en retard. Exemple: -2.0 secondes.',
88
88
  '<strong>Testez et Ajustez :</strong> Commencez par de petits incréments (0,5 s) et utilisez l\'aperçu pour valider.'
89
89
  ], icon: 'mdi:arrow-right' },
90
90
 
@@ -157,7 +157,7 @@ const seo: SubtitleSyncLocaleContent['seo'] = [
157
157
  { type: 'glossary', items: [
158
158
  {
159
159
  term: 'SRT (SubRip)',
160
- definition: 'Format de sous-titres le plus universel : fichier texte avec numéros de séquence, temps et texte.'
160
+ definition: 'Format de sous-titres le plus universel: fichier texte avec numéros de séquence, temps et texte.'
161
161
  },
162
162
  {
163
163
  term: 'Offset (Décalage)',
@@ -177,9 +177,9 @@ const seo: SubtitleSyncLocaleContent['seo'] = [
177
177
  }
178
178
  ] },
179
179
 
180
- { type: 'message', title: 'Édition Professionnelle avec Contrôle Total', ariaLabel: 'Informations techniques sur la synchronisation', html: 'Notre approche est simple mais puissante : un décalage unique, appliqué instantanément, traité à 100 % dans votre navigateur. Pas de cloud, pas de stockage, pas de suivi. Simplement importez, ajustez, téléchargez. Un contrôle total sur votre contenu.' },
180
+ { type: 'message', title: 'Édition Professionnelle avec Contrôle Total', ariaLabel: 'Informations techniques sur la synchronisation', html: 'Notre approche est simple mais puissante: un décalage unique, appliqué instantanément, traité à 100 % dans votre navigateur. Pas de cloud, pas de stockage, pas de suivi. Simplement importez, ajustez, téléchargez. Un contrôle total sur votre contenu.' },
181
181
 
182
- { type: 'title', text: 'Conclusion : Le Cinéma Sans Interruptions', level: 3 },
182
+ { type: 'title', text: 'Conclusion: Le Cinéma Sans Interruptions', level: 3 },
183
183
  { type: 'paragraph', html: 'Une synchronisation parfaite des sous-titres est fondamentale pour une expérience audiovisuelle de qualité. Avec cet outil, vous transformez une expérience frustrante en une soirée cinéma parfaite.' }
184
184
  ];
185
185
 
@@ -67,7 +67,7 @@ const seo: SubtitleSyncLocaleContent['seo'] = [
67
67
  items: [
68
68
  'Мгновенное исправление рассинхрона звука и субтитров',
69
69
  'Поддержка стандартных файлов SRT (SubRip)',
70
- '100% локальная обработка максимальная конфиденциальность',
70
+ '100% локальная обработка - максимальная конфиденциальность',
71
71
  'Без установки, без подписки, полностью бесплатно'
72
72
  ]
73
73
  },
@@ -81,7 +81,7 @@ const seo: SubtitleSyncLocaleContent['seo'] = [
81
81
  ], columns: 3 },
82
82
 
83
83
  { type: 'title', text: 'Ускорение vs Задержка: Практическое руководство', level: 3 },
84
- { type: 'paragraph', html: 'Первый шаг правильно определить тип смещения. Вот логика:' },
84
+ { type: 'paragraph', html: 'Первый шаг - правильно определить тип смещения. Вот логика:' },
85
85
  { type: 'list', items: [
86
86
  '<strong>Задержка (Положительное значение):</strong> Когда текст появляется РАНЬШЕ звука. Субтитры спешат. Пример: +2.0 секунды.',
87
87
  '<strong>Ускорение (Отрицательное значение):</strong> Когда текст появляется ПОСЛЕ звука. Субтитры отстают. Пример: -2.0 секунды.',
@@ -127,7 +127,7 @@ const seo: SubtitleSyncLocaleContent['seo'] = [
127
127
 
128
128
  { type: 'title', text: 'Почему субтитры теряют синхронизацию', level: 3 },
129
129
  { type: 'table', headers: ['Частая причина', 'Техническое описание', 'Решение'], rows: [
130
- ['Разница в частоте кадров (FPS)', '23.976 fps vs 25 fps накопленная разница', 'Настройка единого смещения (этот инструмент)'],
130
+ ['Разница в частоте кадров (FPS)', '23.976 fps vs 25 fps - накопленная разница', 'Настройка единого смещения (этот инструмент)'],
131
131
  ['Редактирование', 'Рекламные врезки или удаленный/добавленный контент', 'Ручной расчет + синхронизация'],
132
132
  ['Региональная версия', 'PAL (25 fps Европа) vs NTSC (29.97 fps США)', 'Простое математическое смещение'],
133
133
  ['Изменение разрешения', 'Перекодирование с другой скоростью обработки', 'Перерасчет исходного файла']
@@ -137,15 +137,15 @@ const seo: SubtitleSyncLocaleContent['seo'] = [
137
137
 
138
138
  { type: 'proscons', items: [
139
139
  {
140
- pro: 'Экстремальная скорость обрабатывает большие файлы за миллисекунды',
140
+ pro: 'Экстремальная скорость - обрабатывает большие файлы за миллисекунды',
141
141
  con: 'Настраивает только фиксированное смещение, не прогрессивное'
142
142
  },
143
143
  {
144
- pro: 'Полная конфиденциальность контент никогда не покидает ваш браузер',
144
+ pro: 'Полная конфиденциальность - контент никогда не покидает ваш браузер',
145
145
  con: 'Требуется современный браузер с поддержкой JavaScript'
146
146
  },
147
147
  {
148
- pro: 'Универсальный формат работает с любым стандартным SRT',
148
+ pro: 'Универсальный формат - работает с любым стандартным SRT',
149
149
  con: 'Не поддерживает другие форматы (ASS, VTT, SCC и т. д.)'
150
150
  },
151
151
  {