@jjlmoya/utils-audiovisual 1.2.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 +60 -0
- package/src/category/i18n/en.ts +198 -0
- package/src/category/i18n/es.ts +198 -0
- package/src/category/i18n/fr.ts +198 -0
- package/src/category/index.ts +17 -0
- package/src/category/seo.astro +15 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +143 -0
- package/src/data.ts +4 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +32 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +146 -0
- package/src/pages/[locale].astro +251 -0
- package/src/pages/index.astro +4 -0
- package/src/tests/faq_count.test.ts +19 -0
- package/src/tests/locale_completeness.test.ts +42 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/no_h1_in_components.test.ts +48 -0
- package/src/tests/seo_length.test.ts +22 -0
- package/src/tests/tool_validation.test.ts +17 -0
- package/src/tool/chromaticLens/bibliography.astro +17 -0
- package/src/tool/chromaticLens/component.astro +178 -0
- package/src/tool/chromaticLens/i18n/en.ts +246 -0
- package/src/tool/chromaticLens/i18n/es.ts +244 -0
- package/src/tool/chromaticLens/i18n/fr.ts +244 -0
- package/src/tool/chromaticLens/index.ts +43 -0
- package/src/tool/chromaticLens/logic.ts +87 -0
- package/src/tool/chromaticLens/seo.astro +15 -0
- package/src/tool/chromaticLens/style.css +308 -0
- package/src/tool/chromaticLens/ui.ts +109 -0
- package/src/tool/collageMaker/bibliography.astro +17 -0
- package/src/tool/collageMaker/component.astro +302 -0
- package/src/tool/collageMaker/i18n/en.ts +233 -0
- package/src/tool/collageMaker/i18n/es.ts +231 -0
- package/src/tool/collageMaker/i18n/fr.ts +231 -0
- package/src/tool/collageMaker/index.ts +51 -0
- package/src/tool/collageMaker/logic.ts +134 -0
- package/src/tool/collageMaker/seo.astro +15 -0
- package/src/tool/collageMaker/style.css +386 -0
- package/src/tool/exifCleaner/bibliography.astro +18 -0
- package/src/tool/exifCleaner/component.astro +162 -0
- package/src/tool/exifCleaner/i18n/en.ts +277 -0
- package/src/tool/exifCleaner/i18n/es.ts +277 -0
- package/src/tool/exifCleaner/i18n/fr.ts +277 -0
- package/src/tool/exifCleaner/index.ts +57 -0
- package/src/tool/exifCleaner/logic.ts +135 -0
- package/src/tool/exifCleaner/seo.astro +18 -0
- package/src/tool/exifCleaner/style.css +289 -0
- package/src/tool/exifCleaner/ui.ts +117 -0
- package/src/tool/imageCompressor/bibliography.astro +17 -0
- package/src/tool/imageCompressor/component.astro +262 -0
- package/src/tool/imageCompressor/i18n/en.ts +232 -0
- package/src/tool/imageCompressor/i18n/es.ts +230 -0
- package/src/tool/imageCompressor/i18n/fr.ts +230 -0
- package/src/tool/imageCompressor/index.ts +50 -0
- package/src/tool/imageCompressor/logic.ts +79 -0
- package/src/tool/imageCompressor/seo.astro +15 -0
- package/src/tool/imageCompressor/style.css +503 -0
- package/src/tool/printQualityCalculator/bibliography.astro +18 -0
- package/src/tool/printQualityCalculator/component.astro +318 -0
- package/src/tool/printQualityCalculator/i18n/en.ts +247 -0
- package/src/tool/printQualityCalculator/i18n/es.ts +245 -0
- package/src/tool/printQualityCalculator/i18n/fr.ts +245 -0
- package/src/tool/printQualityCalculator/index.ts +56 -0
- package/src/tool/printQualityCalculator/logic.ts +53 -0
- package/src/tool/printQualityCalculator/seo.astro +18 -0
- package/src/tool/printQualityCalculator/style.css +491 -0
- package/src/tool/printQualityCalculator/ui.ts +122 -0
- package/src/tool/privacyBlur/bibliography.astro +17 -0
- package/src/tool/privacyBlur/component.astro +230 -0
- package/src/tool/privacyBlur/i18n/en.ts +238 -0
- package/src/tool/privacyBlur/i18n/es.ts +236 -0
- package/src/tool/privacyBlur/i18n/fr.ts +236 -0
- package/src/tool/privacyBlur/index.ts +49 -0
- package/src/tool/privacyBlur/logic.ts +249 -0
- package/src/tool/privacyBlur/seo.astro +15 -0
- package/src/tool/privacyBlur/style.css +332 -0
- package/src/tool/privacyBlur/ui.ts +124 -0
- package/src/tool/subtitleSync/bibliography.astro +17 -0
- package/src/tool/subtitleSync/component.astro +187 -0
- package/src/tool/subtitleSync/i18n/en.ts +241 -0
- package/src/tool/subtitleSync/i18n/es.ts +241 -0
- package/src/tool/subtitleSync/i18n/fr.ts +241 -0
- package/src/tool/subtitleSync/index.ts +49 -0
- package/src/tool/subtitleSync/logic.ts +91 -0
- package/src/tool/subtitleSync/seo.astro +15 -0
- package/src/tool/subtitleSync/style.css +325 -0
- package/src/tool/subtitleSync/ui.ts +152 -0
- package/src/tool/timelapseCalculator/bibliography.astro +15 -0
- package/src/tool/timelapseCalculator/component.astro +148 -0
- package/src/tool/timelapseCalculator/i18n/en.ts +169 -0
- package/src/tool/timelapseCalculator/i18n/es.ts +169 -0
- package/src/tool/timelapseCalculator/i18n/fr.ts +169 -0
- package/src/tool/timelapseCalculator/index.ts +52 -0
- package/src/tool/timelapseCalculator/logic.ts +46 -0
- package/src/tool/timelapseCalculator/seo.astro +18 -0
- package/src/tool/timelapseCalculator/style.css +285 -0
- package/src/tool/tvDistance/bibliography.astro +17 -0
- package/src/tool/tvDistance/component.astro +178 -0
- package/src/tool/tvDistance/i18n/en.ts +223 -0
- package/src/tool/tvDistance/i18n/es.ts +223 -0
- package/src/tool/tvDistance/i18n/fr.ts +223 -0
- package/src/tool/tvDistance/index.ts +49 -0
- package/src/tool/tvDistance/logic.ts +47 -0
- package/src/tool/tvDistance/seo.astro +15 -0
- package/src/tool/tvDistance/style.css +435 -0
- package/src/tool/tvDistance/ui.ts +66 -0
- package/src/tool/videoFrameExtractor/bibliography.astro +17 -0
- package/src/tool/videoFrameExtractor/component.astro +285 -0
- package/src/tool/videoFrameExtractor/i18n/en.ts +235 -0
- package/src/tool/videoFrameExtractor/i18n/es.ts +235 -0
- package/src/tool/videoFrameExtractor/i18n/fr.ts +235 -0
- package/src/tool/videoFrameExtractor/index.ts +53 -0
- package/src/tool/videoFrameExtractor/logic.ts +49 -0
- package/src/tool/videoFrameExtractor/seo.astro +15 -0
- package/src/tool/videoFrameExtractor/style.css +426 -0
- package/src/tool/videoFrameExtractor/ui.ts +179 -0
- package/src/tools.ts +25 -0
- package/src/types.ts +72 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import type { SubtitleSyncUI, SubtitleSyncLocaleContent } from '../index';
|
|
3
|
+
|
|
4
|
+
const slug = 'sincronizar-subtitulos';
|
|
5
|
+
const title = 'Sincronizar Subtítulos SRT Online - Ajusta el Tiempo Gratis';
|
|
6
|
+
const description = 'Herramienta online para adelantar o retrasar subtítulos SRT. Corrige el desfase de tiempo de forma sencilla y descarga el archivo sincronizado al instante.';
|
|
7
|
+
|
|
8
|
+
const ui: SubtitleSyncUI = {
|
|
9
|
+
dropTitle: "Arrastra tu archivo .SRT aquí",
|
|
10
|
+
dropSubtitle: "o haz clic para explorar",
|
|
11
|
+
adjustTitle: "Ajustar Tiempo",
|
|
12
|
+
offsetLabel: "Desplazamiento (segundos)",
|
|
13
|
+
offsetHelp: "Usa valores negativos para adelantar (ej: -1.5) y positivos para retrasar.",
|
|
14
|
+
linesStat: "Líneas",
|
|
15
|
+
firstStat: "Primer Subtítulo",
|
|
16
|
+
lastStat: "Último Subtítulo",
|
|
17
|
+
originalLabel: "ORIGINAL",
|
|
18
|
+
resultLabel: "RESULTADO",
|
|
19
|
+
downloadButton: "Descargar Corregido",
|
|
20
|
+
previewBadge: "VISTA PREVIA",
|
|
21
|
+
unitSeconds: "seg"
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const faq: SubtitleSyncLocaleContent['faq'] = [
|
|
25
|
+
{
|
|
26
|
+
question: "¿Cómo puedo sincronizar mis subtítulos si el audio va por delante?",
|
|
27
|
+
answer: "Si el audio aparece antes que el texto, debes retrasar los subtítulos. Introduce un valor positivo en la calculadora (ej. 2.0 para retrasarlos 2 segundos).",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
question: "¿Qué formato de archivos acepta esta herramienta?",
|
|
31
|
+
answer: "Actualmente la herramienta está optimizada para archivos .SRT (SubRip), que es el estándar más común en reproductores de vídeo y plataformas de streaming.",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
question: "¿Es seguro subir mis archivos de subtítulos?",
|
|
35
|
+
answer: "Sí, porque el procesamiento es 100% local en tu dispositivo. Tus archivos no se envían a ningún servidor; la sincronización ocurre directamente en tu navegador.",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
question: "¿Puedo sincronizar solo una parte del archivo?",
|
|
39
|
+
answer: "No, esta herramienta aplica un desplazamiento constante a todo el archivo. Si el desfase es progresivo, podrías necesitar una edición más avanzada.",
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const howTo: SubtitleSyncLocaleContent['howTo'] = [
|
|
44
|
+
{
|
|
45
|
+
name: "Carga tu archivo SRT",
|
|
46
|
+
text: "Arrastra el archivo de subtítulos que quieres corregir al área de carga.",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Identifica el desfase",
|
|
50
|
+
text: "Mide cuánto tiempo de retraso o adelanto tienen los subtítulos respecto al audio en tu reproductor.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "Ajusta el desplazamiento",
|
|
54
|
+
text: "Introduce los segundos positivos (retrasar) o negativos (adelantar) en el panel de control.",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "Descarga el archivo",
|
|
58
|
+
text: "Verifica en la vista previa que los tiempos son correctos y haz clic en descargar para obtener tu nuevo SRT.",
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const bibliography: SubtitleSyncLocaleContent['bibliography'] = [
|
|
63
|
+
{
|
|
64
|
+
name: "SubRip (SRT) format specification",
|
|
65
|
+
url: "https://matroska.org/technical/subtitles.html#srt-subtitles",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "MDN Web Docs - FileReader API",
|
|
69
|
+
url: "https://developer.mozilla.org/en-US/docs/Web/API/FileReader",
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const seo: SubtitleSyncLocaleContent['seo'] = [
|
|
74
|
+
{
|
|
75
|
+
type: 'summary',
|
|
76
|
+
title: 'Sincronización de Subtítulos Profesional',
|
|
77
|
+
items: [
|
|
78
|
+
'Corrección instantánea de desfases de audio-subtítulos',
|
|
79
|
+
'Soporta archivos SRT (SubRip) estándar',
|
|
80
|
+
'Procesamiento 100% local - máxima privacidad',
|
|
81
|
+
'Sin instalación, sin suscripción, completamente gratuito'
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
{ type: 'title', text: 'Sincronización Perfecta de Subtítulos SRT', level: 2 },
|
|
85
|
+
{ type: 'paragraph', html: 'No hay nada más frustrante para un espectador que ver diálogos que no coinciden con las voces. El desfase de subtítulos suele ocurrir por diferencias entre versiones de vídeo: variaciones en framerate, cortes de publicidad, intros de productoras o cambios en la compresión. Con esta herramienta, corriges el problema en segundos.' },
|
|
86
|
+
|
|
87
|
+
{ type: 'stats', items: [
|
|
88
|
+
{ value: '100%', label: 'Procesamiento Local', icon: 'mdi:shield' },
|
|
89
|
+
{ value: 'Milisegundos', label: 'Precisión', icon: 'mdi:clock-outline' },
|
|
90
|
+
{ value: 'Cualquier Tamaño', label: 'SIN Límite de Archivo', icon: 'mdi:file-document' }
|
|
91
|
+
], columns: 3 },
|
|
92
|
+
|
|
93
|
+
{ type: 'title', text: 'Adelantar vs Retrasar: Guía Práctica', level: 3 },
|
|
94
|
+
{ type: 'paragraph', html: 'El primer paso es identificar correctamente el tipo de desfase. Aquí está la lógica:' },
|
|
95
|
+
{ type: 'list', items: [
|
|
96
|
+
'<strong>Retrasar (Valor Positivo):</strong> Cuando ves el texto aparecer ANTES que el sonido. Los subtítulos van adelantados. Ejemplo: +2.0 segundos.',
|
|
97
|
+
'<strong>Adelantar (Valor Negativo):</strong> Cuando ves el texto aparecer DESPUÉS que el sonido. Los subtítulos van retrasados. Ejemplo: -2.0 segundos.',
|
|
98
|
+
'<strong>Prueba y Ajusta:</strong> Comienza con incrementos pequeños (0.5s) y usa la vista previa para validar.'
|
|
99
|
+
], icon: 'mdi:arrow-right' },
|
|
100
|
+
|
|
101
|
+
{ type: 'card', title: 'Privacidad de Nivel Profesional', html: 'Al procesar el archivo mediante JavaScript en el lado del cliente, garantizamos que el contenido de tus subtítulos nunca abandona tu ordenador. Esencial para traductores y profesionales que manejan material confidencial o bajo contrato de confidencialidad.' },
|
|
102
|
+
|
|
103
|
+
{ type: 'title', text: 'Casos de Uso Comunes', level: 3 },
|
|
104
|
+
{ type: 'comparative', items: [
|
|
105
|
+
{
|
|
106
|
+
title: 'Traductores y Subtituladores',
|
|
107
|
+
description: 'Sincronizar traducciones después de trabajar con múltiples versiones de vídeo',
|
|
108
|
+
icon: 'mdi:translate',
|
|
109
|
+
points: [
|
|
110
|
+
'Archivos SRT de diferentes fuentes',
|
|
111
|
+
'Versionado de contenido (theatrical vs streaming)',
|
|
112
|
+
'Entrega rápida sin cambiar herramientas'
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
title: 'Creadores de Contenido',
|
|
117
|
+
description: 'Recuperar subtítulos cuando el vídeo se procesó con diferente framerate',
|
|
118
|
+
icon: 'mdi:video',
|
|
119
|
+
points: [
|
|
120
|
+
'Reutilizar subtítulos existentes',
|
|
121
|
+
'Cambios de formato (720p a 1080p)',
|
|
122
|
+
'Evitar retiming manual de 1000+ líneas'
|
|
123
|
+
],
|
|
124
|
+
highlight: true
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
title: 'Usuarios Casuales',
|
|
128
|
+
description: 'Corregir subtítulos descargados que no encajan perfectamente',
|
|
129
|
+
icon: 'mdi:account',
|
|
130
|
+
points: [
|
|
131
|
+
'Subtítulos genéricos desincronizados',
|
|
132
|
+
'Películas en diferente región (PAL vs NTSC)',
|
|
133
|
+
'Streaming con versiones editadas'
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
], columns: 3 },
|
|
137
|
+
|
|
138
|
+
{ type: 'title', text: 'Por Qué los Subtítulos Se Desincroniza', level: 3 },
|
|
139
|
+
{ type: 'table', headers: ['Causa Común', 'Descripción Técnica', 'Solución'], rows: [
|
|
140
|
+
['Diferencia de Framerate', '23.976 fps vs 25 fps - acumulación de diferencia', 'Ajuste de offset único (esta herramienta)'],
|
|
141
|
+
['Editorialización', 'Cortes de publicidad o contenido adicional removido', 'Cálculo manual + sincronización'],
|
|
142
|
+
['Versión Regional', 'PAL (25 fps Europa) vs NTSC (29.97 fps USA)', 'Offset matemático simple'],
|
|
143
|
+
['Cambio de Resolución', 'Reencoding con diferente velocidad de procesamiento', 'Recálculo del archivo original']
|
|
144
|
+
] },
|
|
145
|
+
|
|
146
|
+
{ type: 'diagnostic', variant: 'info', title: 'Limitaciones Técnicas a Considerar', icon: 'mdi:information', badge: 'Importante', html: 'Esta herramienta aplica un desplazamiento <strong>constante</strong> a todo el archivo. Si el desfase es <strong>progresivo</strong> (empieza bien pero se va desincronizando), significa una diferencia de framerate persistente. En ese caso, el archivo original necesita re-procesamiento en software de edición profesional.' },
|
|
147
|
+
|
|
148
|
+
{ type: 'proscons', items: [
|
|
149
|
+
{
|
|
150
|
+
pro: 'Velocidad extrema - procesa archivos grandes en milisegundos',
|
|
151
|
+
con: 'Solo ajusta offset fijo, no desfases progresivos'
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
pro: 'Privacidad total - el contenido nunca abandona tu navegador',
|
|
155
|
+
con: 'Requiere navegador moderno con JavaScript habilitado'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
pro: 'Formato universal - funciona con cualquier SRT estándar',
|
|
159
|
+
con: 'No soporta otros formatos (ASS, VTT, SCC, etc.)'
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
pro: 'Completamente gratuito, sin publicidad, sin tracking',
|
|
163
|
+
con: 'Sin historial de cambios o versioning'
|
|
164
|
+
}
|
|
165
|
+
], proTitle: 'Ventajas', conTitle: 'Limitaciones' },
|
|
166
|
+
|
|
167
|
+
{ type: 'glossary', items: [
|
|
168
|
+
{
|
|
169
|
+
term: 'SRT (SubRip)',
|
|
170
|
+
definition: 'Formato de subtítulos más universal: archivo de texto con números de secuencia, tiempos (hh:mm:ss,mmm) y texto. Estándar de facto en reproductores y plataformas.'
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
term: 'Offset (Desplazamiento)',
|
|
174
|
+
definition: 'Cantidad fija de tiempo que se suma o resta a todos los tiempos del archivo. Pueden ser segundos positivos (retrasar) o negativos (adelantar).'
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
term: 'Framerate (fps)',
|
|
178
|
+
definition: 'Fotogramas por segundo. 24p (cine), 25p (PAL/Europa), 29.97p (NTSC/USA), 60p (video fluido). Diferencias causan desfases acumulativos.'
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
term: 'NTSC vs PAL',
|
|
182
|
+
definition: 'Estándares de televisión regional: PAL (25 fps, 50 Hz) en Europa/Australia; NTSC (29.97 fps, 60 Hz) en USA/Japón. Diferencias de ~4% en velocidad total.'
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
term: 'Desfase Progresivo',
|
|
186
|
+
definition: 'Cuando la sincronización empieza correcta pero se va desincronizando gradualmente. Indica diferencia de framerate persistente, requiere re-procesamiento.'
|
|
187
|
+
}
|
|
188
|
+
] },
|
|
189
|
+
|
|
190
|
+
{ type: 'message', title: 'Edición Profesional con Control Total', ariaLabel: 'Información técnica sobre sincronización', html: 'Nuestro enfoque es simple pero poderoso: un desplazamiento único, aplicado instantáneamente, procesado 100% en tu navegador. Sin cloud, sin almacenamiento, sin seguimiento. Simplemente carga, ajusta, descarga. Control total sobre tu contenido.' },
|
|
191
|
+
|
|
192
|
+
{ type: 'title', text: 'Conclusión: Cine Sin Interrupciones', level: 3 },
|
|
193
|
+
{ type: 'paragraph', html: 'La sincronización perfecta de subtítulos es fundamental para una experiencia audiovisual de calidad. Con esta herramienta, transformas una experiencia frustrante en una noche de cine perfecta, sin necesidad de software costoso o complicado.' }
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
197
|
+
'@context': 'https://schema.org',
|
|
198
|
+
'@type': 'FAQPage',
|
|
199
|
+
mainEntity: faq.map((item) => ({
|
|
200
|
+
'@type': 'Question',
|
|
201
|
+
name: item.question,
|
|
202
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
203
|
+
})),
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const howToSchema: WithContext<HowTo> = {
|
|
207
|
+
'@context': 'https://schema.org',
|
|
208
|
+
'@type': 'HowTo',
|
|
209
|
+
name: title,
|
|
210
|
+
description,
|
|
211
|
+
step: howTo.map((step) => ({
|
|
212
|
+
'@type': 'HowToStep',
|
|
213
|
+
name: step.name,
|
|
214
|
+
text: step.text,
|
|
215
|
+
})),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
219
|
+
'@context': 'https://schema.org',
|
|
220
|
+
'@type': 'SoftwareApplication',
|
|
221
|
+
name: title,
|
|
222
|
+
description,
|
|
223
|
+
applicationCategory: 'UtilitiesApplication',
|
|
224
|
+
operatingSystem: 'Web',
|
|
225
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
226
|
+
inLanguage: 'es',
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export const content: SubtitleSyncLocaleContent = {
|
|
230
|
+
slug,
|
|
231
|
+
title,
|
|
232
|
+
description,
|
|
233
|
+
ui,
|
|
234
|
+
seo,
|
|
235
|
+
faq,
|
|
236
|
+
faqTitle: 'Preguntas frecuentes sobre sincronización',
|
|
237
|
+
bibliography,
|
|
238
|
+
bibliographyTitle: 'Recursos técnicos sobre formatos de subtítulos',
|
|
239
|
+
howTo,
|
|
240
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
241
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import type { SubtitleSyncUI, SubtitleSyncLocaleContent } from '../index';
|
|
3
|
+
|
|
4
|
+
const slug = 'synchroniser-sous-titres-srt-en-ligne-ajuster-temps-gratuit';
|
|
5
|
+
const title = 'Synchroniser les Sous-titres SRT en Ligne - Ajustez le Temps Gratuitement';
|
|
6
|
+
const description = 'Outil en ligne pour avancer ou retarder les sous-titres SRT. Corrigez le décalage de temps simplement et téléchargez le fichier synchronisé instantanément.';
|
|
7
|
+
|
|
8
|
+
const ui: SubtitleSyncUI = {
|
|
9
|
+
dropTitle: "Faites glisser votre fichier .SRT ici",
|
|
10
|
+
dropSubtitle: "ou cliquez pour parcourir",
|
|
11
|
+
adjustTitle: "Ajuster le temps",
|
|
12
|
+
offsetLabel: "Décalage (secondes)",
|
|
13
|
+
offsetHelp: "Utilisez des valeurs négatives pour avancer (ex : -1.5) et positives pour retarder.",
|
|
14
|
+
linesStat: "Lignes",
|
|
15
|
+
firstStat: "Premier sous-titre",
|
|
16
|
+
lastStat: "Dernier sous-titre",
|
|
17
|
+
originalLabel: "ORIGINAL",
|
|
18
|
+
resultLabel: "RÉSULTAT",
|
|
19
|
+
downloadButton: "Télécharger le corrigé",
|
|
20
|
+
previewBadge: "APERÇU",
|
|
21
|
+
unitSeconds: "sec"
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const faq: SubtitleSyncLocaleContent['faq'] = [
|
|
25
|
+
{
|
|
26
|
+
question: "Comment synchroniser mes sous-titres si l'audio est en avance ?",
|
|
27
|
+
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
|
+
},
|
|
29
|
+
{
|
|
30
|
+
question: "Quels formats de fichiers cet outil accepte-t-il ?",
|
|
31
|
+
answer: "Actuellement, l'outil est optimisé pour les fichiers .SRT (SubRip), qui est le standard le plus courant pour les lecteurs vidéo et les plateformes de streaming.",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
question: "Est-il sûr de télécharger mes fichiers de sous-titres ?",
|
|
35
|
+
answer: "Oui, car le traitement est 100 % local sur votre appareil. Vos fichiers ne sont envoyés à aucun serveur ; la synchronisation se fait directement dans votre navigateur.",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
question: "Puis-je synchroniser seulement une partie du fichier ?",
|
|
39
|
+
answer: "Non, cet outil applique un décalage constant à l'ensemble du fichier. Si le décalage est progressif, vous pourriez avoir besoin d'une édition plus avancée.",
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const howTo: SubtitleSyncLocaleContent['howTo'] = [
|
|
44
|
+
{
|
|
45
|
+
name: "Importez votre fichier SRT",
|
|
46
|
+
text: "Faites glisser le fichier de sous-titres que vous souhaitez corriger dans la zone de téléchargement.",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Identifiez le décalage",
|
|
50
|
+
text: "Mesurez le retard ou l'avance des sous-titres par rapport à l'audio dans votre lecteur.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "Ajustez le décalage",
|
|
54
|
+
text: "Entrez les secondes positives (retarder) ou négatives (avancer) dans le panneau de contrôle.",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "Téléchargez le fichier",
|
|
58
|
+
text: "Vérifiez dans l'aperçu que les temps sont corrects et cliquez sur télécharger pour obtenir votre nouveau SRT.",
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const bibliography: SubtitleSyncLocaleContent['bibliography'] = [
|
|
63
|
+
{
|
|
64
|
+
name: "Spécification du format SubRip (SRT)",
|
|
65
|
+
url: "https://matroska.org/technical/subtitles.html#srt-subtitles",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "MDN Web Docs - FileReader API",
|
|
69
|
+
url: "https://developer.mozilla.org/fr/docs/Web/API/FileReader",
|
|
70
|
+
},
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const seo: SubtitleSyncLocaleContent['seo'] = [
|
|
74
|
+
{
|
|
75
|
+
type: 'summary',
|
|
76
|
+
title: 'Synchronisation de Sous-titres Professionnelle',
|
|
77
|
+
items: [
|
|
78
|
+
'Correction instantanée des décalages audio-sous-titres',
|
|
79
|
+
'Supporte les fichiers SRT (SubRip) standards',
|
|
80
|
+
'Traitement 100 % local - confidentialité maximale',
|
|
81
|
+
'Sans installation, sans abonnement, complètement gratuit'
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
{ type: 'title', text: 'Synchronisation Parfaite de Sous-titres SRT', level: 2 },
|
|
85
|
+
{ 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.' },
|
|
86
|
+
|
|
87
|
+
{ type: 'stats', items: [
|
|
88
|
+
{ value: '100 %', label: 'Traitement Local', icon: 'mdi:shield' },
|
|
89
|
+
{ value: 'Millisecondes', label: 'Précision', icon: 'mdi:clock-outline' },
|
|
90
|
+
{ value: 'Illimité', label: 'Sans Limite de Fichier', icon: 'mdi:file-document' }
|
|
91
|
+
], columns: 3 },
|
|
92
|
+
|
|
93
|
+
{ type: 'title', text: 'Avancer vs Retarder : Guide Pratique', level: 3 },
|
|
94
|
+
{ type: 'paragraph', html: 'La première étape consiste à identifier correctement le type de décalage. Voici la logique :' },
|
|
95
|
+
{ type: 'list', items: [
|
|
96
|
+
'<strong>Retarder (Valeur Positive) :</strong> Quand le texte apparaît AVANT le son. Les sous-titres sont en avance. Exemple : +2.0 secondes.',
|
|
97
|
+
'<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.',
|
|
98
|
+
'<strong>Testez et Ajustez :</strong> Commencez par de petits incréments (0,5 s) et utilisez l\'aperçu pour valider.'
|
|
99
|
+
], icon: 'mdi:arrow-right' },
|
|
100
|
+
|
|
101
|
+
{ type: 'card', title: 'Confidentialité de Niveau Professionnel', html: 'En traitant le fichier via JavaScript côté client, nous garantissons que le contenu de vos sous-titres ne quitte jamais votre ordinateur. Essentiel pour les traducteurs et les professionnels manipulant du matériel confidentiel ou sous contrat de confidentialité.' },
|
|
102
|
+
|
|
103
|
+
{ type: 'title', text: 'Cas d\'Utilisation Courants', level: 3 },
|
|
104
|
+
{ type: 'comparative', items: [
|
|
105
|
+
{
|
|
106
|
+
title: 'Traducteurs et Sous-titreurs',
|
|
107
|
+
description: 'Synchroniser des traductions après avoir travaillé avec plusieurs versions vidéo',
|
|
108
|
+
icon: 'mdi:translate',
|
|
109
|
+
points: [
|
|
110
|
+
'Fichiers SRT de sources différentes',
|
|
111
|
+
'Gestion des versions de contenu (cinéma vs streaming)',
|
|
112
|
+
'Livraison rapide sans changer d\'outils'
|
|
113
|
+
]
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
title: 'Créateurs de Contenu',
|
|
117
|
+
description: 'Récupérer des sous-titres quand la vidéo a été traitée avec une fréquence d\'images différente',
|
|
118
|
+
icon: 'mdi:video',
|
|
119
|
+
points: [
|
|
120
|
+
'Réutilisation de sous-titres existants',
|
|
121
|
+
'Changements de format (720p vers 1080p)',
|
|
122
|
+
'Éviter le resynchronisation manuelle de plus de 1000 lignes'
|
|
123
|
+
],
|
|
124
|
+
highlight: true
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
title: 'Utilisateurs Particuliers',
|
|
128
|
+
description: 'Corriger des sous-titres téléchargés qui ne s\'adaptent pas parfaitement',
|
|
129
|
+
icon: 'mdi:account',
|
|
130
|
+
points: [
|
|
131
|
+
'Sous-titres génériques désynchronisés',
|
|
132
|
+
'Films de différentes régions (PAL vs NTSC)',
|
|
133
|
+
'Streaming avec versions éditées'
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
], columns: 3 },
|
|
137
|
+
|
|
138
|
+
{ type: 'title', text: 'Pourquoi les Sous-titres se Désynchronisent', level: 3 },
|
|
139
|
+
{ type: 'table', headers: ['Cause Commune', 'Description Technique', 'Solution'], rows: [
|
|
140
|
+
['Différence de Framerate', '23,976 fps vs 25 fps - accumulation de différence', 'Ajustement du décalage unique (cet outil)'],
|
|
141
|
+
['Éditorialisation', 'Coupures publicitaires ou contenu supprimé', 'Calcul manuel + synchronisation'],
|
|
142
|
+
['Version Régionale', 'PAL (25 fps Europe) vs NTSC (29,97 fps USA)', 'Décalage mathématique simple'],
|
|
143
|
+
['Changement de Résolution', 'Réencodage avec une vitesse de traitement différente', 'Recalcul du fichier original']
|
|
144
|
+
] },
|
|
145
|
+
|
|
146
|
+
{ type: 'diagnostic', variant: 'info', title: 'Limitations Techniques à Considérer', icon: 'mdi:information', badge: 'Important', html: 'Cet outil applique un décalage <strong>constant</strong> à l\'ensemble du fichier. Si le décalage est <strong>progressif</strong> (commence bien mais se décale peu à peu), c\'est une différence de fréquence d\'images persistante. Dans ce cas, le fichier nécessite un traitement dans un logiciel professionnel.' },
|
|
147
|
+
|
|
148
|
+
{ type: 'proscons', items: [
|
|
149
|
+
{
|
|
150
|
+
pro: 'Vitesse extrême - traite de gros fichiers en millisecondes',
|
|
151
|
+
con: 'Ajuste uniquement un décalage fixe, pas progressif'
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
pro: 'Confidentialité totale - le contenu ne quitte jamais votre navigateur',
|
|
155
|
+
con: 'Nécessite un navigateur moderne avec JavaScript activé'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
pro: 'Format universel - fonctionne avec tout SRT standard',
|
|
159
|
+
con: 'Ne supporte pas les autres formats (ASS, VTT, etc.)'
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
pro: 'Complètement gratuit, sans publicité, sans suivi',
|
|
163
|
+
con: 'Pas d\'historique des modifications ou de versionnage'
|
|
164
|
+
}
|
|
165
|
+
], proTitle: 'Avantages', conTitle: 'Limitations' },
|
|
166
|
+
|
|
167
|
+
{ type: 'glossary', items: [
|
|
168
|
+
{
|
|
169
|
+
term: 'SRT (SubRip)',
|
|
170
|
+
definition: 'Format de sous-titres le plus universel : fichier texte avec numéros de séquence, temps et texte.'
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
term: 'Offset (Décalage)',
|
|
174
|
+
definition: 'Quantité fixe de temps ajoutée ou soustraite à tous les temps du fichier.'
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
term: 'Framerate (fps)',
|
|
178
|
+
definition: 'Images par seconde. Les différences entre 24, 25 ou 30 fps causent des décalages cumulatifs.'
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
term: 'NTSC vs PAL',
|
|
182
|
+
definition: 'Standards régionaux de télévision. Les différences de ~4 % de vitesse causent des décalages.'
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
term: 'Décalage Progressif',
|
|
186
|
+
definition: 'Quand la synchronisation est correcte au début mais se décale graduellement. Indique une fréquence d\'images différente.'
|
|
187
|
+
}
|
|
188
|
+
] },
|
|
189
|
+
|
|
190
|
+
{ 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.' },
|
|
191
|
+
|
|
192
|
+
{ type: 'title', text: 'Conclusion : Le Cinéma Sans Interruptions', level: 3 },
|
|
193
|
+
{ 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.' }
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
197
|
+
'@context': 'https://schema.org',
|
|
198
|
+
'@type': 'FAQPage',
|
|
199
|
+
mainEntity: faq.map((item) => ({
|
|
200
|
+
'@type': 'Question',
|
|
201
|
+
name: item.question,
|
|
202
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
203
|
+
})),
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const howToSchema: WithContext<HowTo> = {
|
|
207
|
+
'@context': 'https://schema.org',
|
|
208
|
+
'@type': 'HowTo',
|
|
209
|
+
name: title,
|
|
210
|
+
description,
|
|
211
|
+
step: howTo.map((step) => ({
|
|
212
|
+
'@type': 'HowToStep',
|
|
213
|
+
name: step.name,
|
|
214
|
+
text: step.text,
|
|
215
|
+
})),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
219
|
+
'@context': 'https://schema.org',
|
|
220
|
+
'@type': 'SoftwareApplication',
|
|
221
|
+
name: title,
|
|
222
|
+
description,
|
|
223
|
+
applicationCategory: 'UtilitiesApplication',
|
|
224
|
+
operatingSystem: 'Web',
|
|
225
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
|
|
226
|
+
inLanguage: 'fr',
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export const content: SubtitleSyncLocaleContent = {
|
|
230
|
+
slug,
|
|
231
|
+
title,
|
|
232
|
+
description,
|
|
233
|
+
ui,
|
|
234
|
+
seo,
|
|
235
|
+
faq,
|
|
236
|
+
faqTitle: 'Foire Aux Questions sur la Synchronisation des Sous-titres',
|
|
237
|
+
bibliography,
|
|
238
|
+
bibliographyTitle: 'Ressources Techniques sur les Formats de Sous-titres',
|
|
239
|
+
howTo,
|
|
240
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
241
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AudiovisualToolEntry, ToolLocaleContent, ToolDefinition } from '../../types';
|
|
2
|
+
import SubtitleSync from './component.astro';
|
|
3
|
+
import SubtitleSyncSEO from './seo.astro';
|
|
4
|
+
import SubtitleSyncBibliography from './bibliography.astro';
|
|
5
|
+
|
|
6
|
+
export interface SubtitleSyncUI {
|
|
7
|
+
dropTitle: string;
|
|
8
|
+
dropSubtitle: string;
|
|
9
|
+
adjustTitle: string;
|
|
10
|
+
offsetLabel: string;
|
|
11
|
+
offsetHelp: string;
|
|
12
|
+
linesStat: string;
|
|
13
|
+
firstStat: string;
|
|
14
|
+
lastStat: string;
|
|
15
|
+
originalLabel: string;
|
|
16
|
+
resultLabel: string;
|
|
17
|
+
downloadButton: string;
|
|
18
|
+
previewBadge: string;
|
|
19
|
+
unitSeconds: string;
|
|
20
|
+
[key: string]: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type SubtitleSyncLocaleContent = ToolLocaleContent<SubtitleSyncUI>;
|
|
24
|
+
|
|
25
|
+
import { content as es } from './i18n/es';
|
|
26
|
+
import { content as en } from './i18n/en';
|
|
27
|
+
import { content as fr } from './i18n/fr';
|
|
28
|
+
|
|
29
|
+
export const subtitleSync: AudiovisualToolEntry<SubtitleSyncUI> = {
|
|
30
|
+
id: 'sincronizar-subtitulos',
|
|
31
|
+
icons: {
|
|
32
|
+
bg: 'mdi:movie-open-edit-outline',
|
|
33
|
+
fg: 'mdi:clock-time-four-outline',
|
|
34
|
+
},
|
|
35
|
+
i18n: {
|
|
36
|
+
es: async () => es as unknown as SubtitleSyncLocaleContent,
|
|
37
|
+
en: async () => en as unknown as SubtitleSyncLocaleContent,
|
|
38
|
+
fr: async () => fr as unknown as SubtitleSyncLocaleContent,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export { SubtitleSync, SubtitleSyncSEO, SubtitleSyncBibliography };
|
|
43
|
+
|
|
44
|
+
export const SUBTITLE_SYNC_TOOL: ToolDefinition = {
|
|
45
|
+
entry: subtitleSync as unknown as AudiovisualToolEntry,
|
|
46
|
+
Component: SubtitleSync,
|
|
47
|
+
SEOComponent: SubtitleSyncSEO,
|
|
48
|
+
BibliographyComponent: SubtitleSyncBibliography,
|
|
49
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export interface SrtItem {
|
|
2
|
+
id: string;
|
|
3
|
+
start: number;
|
|
4
|
+
end: number;
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function parseSRT(data: string): SrtItem[] {
|
|
9
|
+
const cleanData = data.replace(/\r/g, "");
|
|
10
|
+
const parts = cleanData.split("\n\n");
|
|
11
|
+
|
|
12
|
+
return parts
|
|
13
|
+
.map((part) => {
|
|
14
|
+
const lines = part.trim().split("\n");
|
|
15
|
+
if (lines.length < 3) return null;
|
|
16
|
+
|
|
17
|
+
const id = lines[0];
|
|
18
|
+
const timeLine = lines[1];
|
|
19
|
+
if (!timeLine || !timeLine.includes("-->")) return null;
|
|
20
|
+
|
|
21
|
+
const timeParts = timeLine.split(" --> ");
|
|
22
|
+
const start = timeParts[0];
|
|
23
|
+
const end = timeParts[1];
|
|
24
|
+
if (!start || !end) return null;
|
|
25
|
+
|
|
26
|
+
const text = lines.slice(2).join("\n");
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
id,
|
|
30
|
+
start: timeMs(start),
|
|
31
|
+
end: timeMs(end),
|
|
32
|
+
text,
|
|
33
|
+
};
|
|
34
|
+
})
|
|
35
|
+
.filter((x): x is SrtItem => x !== null);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function timeMs(timeStr: string): number {
|
|
39
|
+
const parts = timeStr.split(",");
|
|
40
|
+
const hms = parts[0];
|
|
41
|
+
const ms = parts[1] || "0";
|
|
42
|
+
|
|
43
|
+
if (!hms) return 0;
|
|
44
|
+
|
|
45
|
+
const hmsParts = hms.split(":");
|
|
46
|
+
const h = Number(hmsParts[0] || 0);
|
|
47
|
+
const m = Number(hmsParts[1] || 0);
|
|
48
|
+
const s = Number(hmsParts[2] || 0);
|
|
49
|
+
|
|
50
|
+
return h * 3600000 + m * 60000 + s * 1000 + Number(ms);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function msToTime(duration: number): string {
|
|
54
|
+
if (duration < 0) return "00:00:00,000";
|
|
55
|
+
|
|
56
|
+
const h = Math.floor(duration / 3600000);
|
|
57
|
+
const m = Math.floor((duration % 3600000) / 60000);
|
|
58
|
+
const s = Math.floor((duration % 60000) / 1000);
|
|
59
|
+
const ms = Math.floor(duration % 1000);
|
|
60
|
+
|
|
61
|
+
const hh = h.toString().padStart(2, "0");
|
|
62
|
+
const mm = m.toString().padStart(2, "0");
|
|
63
|
+
const ss = s.toString().padStart(2, "0");
|
|
64
|
+
const msStr = ms.toString().padStart(3, "0");
|
|
65
|
+
|
|
66
|
+
return `${hh}:${mm}:${ss},${msStr}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function generateSRT(items: SrtItem[]): string {
|
|
70
|
+
return items
|
|
71
|
+
.map((item, index) => {
|
|
72
|
+
return `${index + 1}\n${msToTime(item.start)} --> ${msToTime(item.end)}\n${item.text}`;
|
|
73
|
+
})
|
|
74
|
+
.join("\n\n");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function shiftSubtitles(items: SrtItem[], offsetMs: number): SrtItem[] {
|
|
78
|
+
return items.map((item) => {
|
|
79
|
+
let newStart = item.start + offsetMs;
|
|
80
|
+
let newEnd = item.end + offsetMs;
|
|
81
|
+
|
|
82
|
+
if (newStart < 0) newStart = 0;
|
|
83
|
+
if (newEnd < 0) newEnd = 0;
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
...item,
|
|
87
|
+
start: newStart,
|
|
88
|
+
end: newEnd,
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { subtitleSync } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'es' } = Astro.props;
|
|
11
|
+
const content = await subtitleSync.i18n[locale]?.();
|
|
12
|
+
if (!content) return null;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<SEORenderer content={{ locale, sections: content.seo || [] }} />
|