diff --git a/assets/js/code-copy.js b/assets/js/code-copy.js index 8af92d5..f4ac628 100644 --- a/assets/js/code-copy.js +++ b/assets/js/code-copy.js @@ -5,6 +5,12 @@ (function () { 'use strict'; + var i18n = (window.wavecast && window.wavecast.i18n) || {}; + var CODE_COPY_TITLE = i18n.code_copy_title || 'Copy code'; + var CODE_COPY_LABEL = i18n.code_copy_label || 'Copy code to clipboard'; + var CODE_COPIED = i18n.code_copied || 'Copied!'; + var CODE_COPIED_LABEL = i18n.code_copied_label || 'Code copied'; + // Collect both .highlight wrappers and standalone
 elements.
   // Filter out 
 that are already inside .highlight (they get a button
   // via the parent).  Also skip 
 that are empty or inside the
@@ -31,8 +37,8 @@
     var btn = document.createElement('button');
     btn.className = 'code-copy-btn';
     btn.type = 'button';
-    btn.title = 'Copy code';
-    btn.setAttribute('aria-label', 'Copy code to clipboard');
+    btn.title = CODE_COPY_TITLE;
+    btn.setAttribute('aria-label', CODE_COPY_LABEL);
     btn.innerHTML =
       '
+

- +
+ title="${this._t("player_rewind_title")}" aria-label="${this._t("player_rewind_label")}">${ICON_SKIP_BACK} + title="${this._t("player_play")}" aria-label="${this._t("player_play")}" aria-pressed="false"> + title="${this._t("player_forward_title")}" aria-label="${this._t("player_forward_label")}">${ICON_SKIP_FWD}
@@ -427,7 +469,7 @@ class PodcastPlayer extends HTMLElement {
+ aria-label="${this._t("player_seek")}" aria-valuetext="0:00 of 0:00">
--:-- / @@ -436,13 +478,13 @@ class PodcastPlayer extends HTMLElement {
+ title="${this._t("player_mute_title")}" aria-label="${this._t("player_mute_label")}" aria-pressed="false">${ICON_VOL_FULL} + aria-label="${this._t("player_volume")}">
+ title="${this._t("player_speed")}" aria-label="${this._t("player_speed")}">1×
@@ -588,7 +630,7 @@ class PodcastPlayer extends HTMLElement { this._els.poster.src = val; this._els.poster.hidden = false; const title = this.getAttribute("title") || ""; - this._els.poster.alt = title ? `Cover: ${title}` : "Cover"; + this._els.poster.alt = title ? this._t("player_cover_of", {title}) : this._t("player_cover_alt"); } else { this._els.poster.src = ""; this._els.poster.hidden = true; @@ -755,7 +797,7 @@ class PodcastPlayer extends HTMLElement { this._audio.playbackRate = next; } this._els.rateBtn.textContent = next + "\u00d7"; - this._els.rateBtn.setAttribute("aria-label", `Playback speed ${next}\u00d7`); + this._els.rateBtn.setAttribute("aria-label", this._t("player_speed_label", {rate: next+"\u00d7"})); this._dispatchStateChange({ playbackRate: next }); } @@ -803,7 +845,7 @@ class PodcastPlayer extends HTMLElement { // Update Media Session metadata if ("mediaSession" in navigator) { navigator.mediaSession.metadata = new MediaMetadata({ - title: this.getAttribute("title") || "Podcast", + title: this.getAttribute("title") || this._t("player_episode"), artist: "", album: "", artwork: this.getAttribute("poster") @@ -815,9 +857,9 @@ class PodcastPlayer extends HTMLElement { _onPlay() { this._els.playBtn.innerHTML = ''; - this._els.playBtn.setAttribute("aria-label", "Pause"); + this._els.playBtn.setAttribute("aria-label", this._t("player_pause")); this._els.playBtn.setAttribute("aria-pressed", "true"); - this._els.playBtn.title = "Pause"; + this._els.playBtn.title = this._t("player_pause"); this._dispatchState(); this._updateMediaSessionPlayback(); @@ -856,9 +898,9 @@ class PodcastPlayer extends HTMLElement { _onPause() { this._els.playBtn.innerHTML = ''; - this._els.playBtn.setAttribute("aria-label", "Play"); + this._els.playBtn.setAttribute("aria-label", this._t("player_play")); this._els.playBtn.setAttribute("aria-pressed", "false"); - this._els.playBtn.title = "Play"; + this._els.playBtn.title = this._t("player_play"); this._dispatchState(); this._updateMediaSessionPlayback(); @@ -892,10 +934,10 @@ class PodcastPlayer extends HTMLElement { _onError() { const err = this._audio.error; - let msg = "Playback error"; + let msg = this._t("player_error"); if (err && err.message) msg += ": " + err.message; else if (this._audio.networkState === this._audio.NETWORK_NO_SOURCE) { - msg = "No audio source available"; + msg = this._t("player_no_source"); } this._els.error.textContent = msg; this._els.error.hidden = false; @@ -973,9 +1015,9 @@ class PodcastPlayer extends HTMLElement { } else { // Ensure button shows play icon this._els.playBtn.innerHTML = ''; - this._els.playBtn.setAttribute("aria-label", "Play"); + this._els.playBtn.setAttribute("aria-label", this._t("player_play")); this._els.playBtn.setAttribute("aria-pressed", "false"); - this._els.playBtn.title = "Play"; + this._els.playBtn.title = this._t("player_play"); } } @@ -986,9 +1028,9 @@ class PodcastPlayer extends HTMLElement { } // Always show play icon on close this._els.playBtn.innerHTML = ''; - this._els.playBtn.setAttribute("aria-label", "Play"); + this._els.playBtn.setAttribute("aria-label", this._t("player_play")); this._els.playBtn.setAttribute("aria-pressed", "false"); - this._els.playBtn.title = "Play"; + this._els.playBtn.title = this._t("player_play"); } /** Respond to a play event from the footer or another inline player. @@ -1003,9 +1045,9 @@ class PodcastPlayer extends HTMLElement { if (mySrc === src || src.endsWith(mySrc) || mySrc.endsWith(src)) { // Same source: someone else started our track — sync button to show pause this._els.playBtn.innerHTML = ''; - this._els.playBtn.setAttribute("aria-label", "Pause"); + this._els.playBtn.setAttribute("aria-label", this._t("player_pause")); this._els.playBtn.setAttribute("aria-pressed", "true"); - this._els.playBtn.title = "Pause"; + this._els.playBtn.title = this._t("player_pause"); } else if (!this._audio.paused) { // Different source: stop our playback (only one song at a time) this._audio.pause(); @@ -1363,6 +1405,35 @@ class PodcastFooter extends HTMLElement { static PERSISTENCE_KEY = "podcastPlayerState:footer"; static STATE_TTL_SECONDS = 3600; // 1 hour + /* ------------------------------------------------------------------ */ + /* i18n — default English strings, overridable via window.wavecast */ + /* ------------------------------------------------------------------ */ + + static get DEFAULT_I18N() { + return { + player_region: "Podcast Player", + player_cover_alt: "Cover", + player_cover_of: "Cover: {title}", + player_rewind_title: "Rewind 15s", + player_rewind_label: "Rewind 15 seconds", + player_play: "Play", + player_pause: "Pause", + player_forward_title: "Forward 15s", + player_forward_label: "Forward 15 seconds", + player_seek: "Seek position", + player_mute_title: "Mute", + player_mute_label: "Toggle mute", + player_volume: "Volume", + player_speed: "Playback speed", + player_speed_label: "Playback speed {rate}×", + player_episode: "Podcast", + player_error: "Playback error", + player_no_source: "No audio source available", + player_close: "Close player", + player_unknown_episode: "Unknown Episode", + }; + } + constructor() { super(); this._shadow = this.attachShadow({ mode: "open" }); @@ -1381,6 +1452,21 @@ class PodcastFooter extends HTMLElement { this._onExternalPause = this._onExternalPause.bind(this); this._onExternalSeek = this._onExternalSeek.bind(this); this._onStateChange = this._onStateChange.bind(this); + + // i18n — merge window.wavecast.i18n over English defaults + const extI18n = (window.wavecast && window.wavecast.i18n) || {}; + this._i = Object.assign({}, PodcastFooter.DEFAULT_I18N, extI18n); + + // Tiny helper: replace {key} placeholders in translated strings + this._t = (key, vars) => { + let s = this._i[key] || key; + if (vars) { + Object.keys(vars).forEach(k => { + s = s.replace("{" + k + "}", vars[k]); + }); + } + return s; + }; } connectedCallback() { @@ -1408,12 +1494,12 @@ class PodcastFooter extends HTMLElement { _syncUI() { if (this._audio.paused) { this._els.playBtn.innerHTML = ``; - this._els.playBtn.setAttribute("aria-label", "Play"); - this._els.playBtn.title = "Play"; + this._els.playBtn.setAttribute("aria-label", this._t("player_play")); + this._els.playBtn.title = this._t("player_play"); } else { this._els.playBtn.innerHTML = ``; - this._els.playBtn.setAttribute("aria-label", "Pause"); - this._els.playBtn.title = "Pause"; + this._els.playBtn.setAttribute("aria-label", this._t("player_pause")); + this._els.playBtn.title = this._t("player_pause"); } this._setMuteIcon(); this._els.volume.value = this._audio.muted ? 0 : this._audio.volume; @@ -1571,35 +1657,35 @@ class PodcastFooter extends HTMLElement {
+ aria-label="${this._t("player_seek")}" aria-valuetext="0:00 of 0:00">
--:-- / --:--
- + aria-label="${this._t("player_volume")}">
- +
@@ -1673,7 +1759,7 @@ class PodcastFooter extends HTMLElement { } // New source — load and play - this._els.title.textContent = title || "Unknown Episode"; + this._els.title.textContent = title || this._t("player_unknown_episode"); this._els.source.textContent = src.replace(/^https?:\/\//, "").split("/")[0] || src; if (poster) { @@ -1859,8 +1945,8 @@ class PodcastFooter extends HTMLElement { _onPlay() { this._els.playBtn.innerHTML = ``; - this._els.playBtn.setAttribute("aria-label", "Pause"); - this._els.playBtn.title = "Pause"; + this._els.playBtn.setAttribute("aria-label", this._t("player_pause")); + this._els.playBtn.title = this._t("player_pause"); // Notify inline players to sync their UI this.dispatchEvent(new CustomEvent("podcast-play", { @@ -1877,8 +1963,8 @@ class PodcastFooter extends HTMLElement { _onPause() { this._els.playBtn.innerHTML = ``; - this._els.playBtn.setAttribute("aria-label", "Play"); - this._els.playBtn.title = "Play"; + this._els.playBtn.setAttribute("aria-label", this._t("player_play")); + this._els.playBtn.title = this._t("player_play"); // Notify inline players to sync their UI this.dispatchEvent(new CustomEvent("podcast-pause", { @@ -1950,7 +2036,7 @@ class PodcastFooter extends HTMLElement { this._els.volume.value = this._audio.muted ? 0 : this._audio.volume; this._setMuteIcon(); this._els.rateBtn.textContent = (state.playbackRate || 1) + "×"; - this._els.title.textContent = state.title || "Unknown Episode"; + this._els.title.textContent = state.title || this._t("player_unknown_episode"); this._els.source.textContent = state.src.replace(/^https?:\/\//, "").split("/")[0] || state.src; if (state.poster) { diff --git a/exampleSite/content/es/_index.md b/exampleSite/content/es/_index.md new file mode 100644 index 0000000..2cd55b5 --- /dev/null +++ b/exampleSite/content/es/_index.md @@ -0,0 +1,19 @@ +--- +title: Wavecast Radio +tagline: Sintoniza la tecnología +description: "Bienvenido a Wavecast Radio: una estación de radio demo creada con el tema Hugo Wavecast. Sintoniza nuestros programas, explora los episodios y descubre cómo funciona el reproductor de audio persistente." +featured_episode: "episodes/cr-620-cloudflare-sunil-pai" +layout: "index" +buttons: + - text: "Explorar Programas" + url: "/es/programs/" + class: "nav-button-primary" + - text: "Horario" + url: "/es/schedule/" + class: "" + - text: "Feed RSS" + url: "/es/index.xml" + class: "" +--- + +{{< podcast-player src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" title="Episodio de ejemplo" poster="/wavecast/logo.png" chapters="00:00:00-Introducción,00:00:30-Medio" persistent="true" >}} diff --git a/exampleSite/content/es/about.md b/exampleSite/content/es/about.md new file mode 100644 index 0000000..552e103 --- /dev/null +++ b/exampleSite/content/es/about.md @@ -0,0 +1,4 @@ +--- +title: "Acerca de" +layout: "about" +--- diff --git a/exampleSite/content/es/contact.md b/exampleSite/content/es/contact.md new file mode 100644 index 0000000..fbbd851 --- /dev/null +++ b/exampleSite/content/es/contact.md @@ -0,0 +1,34 @@ +--- +title: "Contacto" +--- + +¿Tienes una pregunta, sugerencia o quieres ser invitado en uno de nuestros programas? Nos encantaría saber de ti. + +
+
+

+ Nota: Este es un sitio de demostración. El formulario siguiente es solo para demostración y no envía mensajes reales. Para contacto real, abre un issue o discusión en GitHub. +

+
+

Ponte en Contacto

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ +## Otras Formas de Contactarnos + +- [**GitHub**](https://github.com/adurrr/wavecast) : código fuente, issues, solicitudes de funciones y pull requests +- [**README**](https://github.com/adurrr/wavecast#readme) : documentación completa con instalación, temas y referencia de API diff --git a/exampleSite/content/es/docs/shortcodes.md b/exampleSite/content/es/docs/shortcodes.md new file mode 100644 index 0000000..2bab1bb --- /dev/null +++ b/exampleSite/content/es/docs/shortcodes.md @@ -0,0 +1,191 @@ +--- +title: "Shortcodes de Utilidad" +description: "Advertencias, botones, figuras, videos, pestañas, galerías y carruseles: todo incluido con Wavecast." +--- + +Wavecast incluye un conjunto de shortcodes de utilidad para crear páginas de contenido enriquecido sin HTML personalizado. +Todos son compatibles con el modo oscuro, responsivos y accesibles. + +--- + +## Advertencias / Llamadas + +Cuatro variantes: `note`, `tip`, `warning`, `danger`. Acepta un `title` opcional. + +### Nota + +``` +{{}} +Esto es una **nota** con soporte de markdown. +{{}} +``` + +{{< admonition type="note" title="Atención" >}} +Esto es una **nota** con soporte de markdown. +{{< /admonition >}} + +### Consejo + +``` +{{}} +Consejo: usa `hugo serve --disableFastRender` durante el desarrollo. +{{}} +``` + +{{< admonition type="tip" >}} +Consejo: usa `hugo serve --disableFastRender` durante el desarrollo. +{{< /admonition >}} + +### Advertencia + +``` +{{}} +Esta API será eliminada en la próxima versión principal. +{{}} +``` + +{{< admonition type="warning" title="Aviso de Obsolescencia" >}} +Esta API será eliminada en la próxima versión principal. +{{< /admonition >}} + +### Peligro + +``` +{{}} +**No ejecutes** esto en producción sin una copia de seguridad. +{{}} +``` + +{{< admonition type="danger" >}} +**No ejecutes** esto en producción sin una copia de seguridad. +{{< /admonition >}} + +--- + +## Botones + +Tres variantes: `primary`, `secondary`, `outline`. Las URLs externas se abren en una nueva pestaña. + +| Variante | Código | +|---|---| +| Primario | `{{}}Inicio{{}}` | +| Secundario | `{{}}Docs{{}}` | +| Contorno | `{{}}Saber Más{{}}` | +| Con icono | `{{}}Ver en GitHub{{}}` | + +{{< button url="/" variant="primary" >}}Inicio{{< /button >}} +{{< button url="/" variant="secondary" >}}Docs{{< /button >}} +{{< button url="/" variant="outline" >}}Saber Más{{< /button >}} +{{< button url="https://github.com/adurrr/wavecast" icon="→" variant="outline" >}}Ver en GitHub{{< /button >}} + +--- + +## Figure + +Elemento `
` HTML5 mejorado con caption opcional, carga diferida y soporte de recursos. + +``` +{{}} +``` + +{{< figure src="https://picsum.photos/800/400" caption="Una imagen aleatoria de Lorem Picsum." alt="Foto aleatoria" >}} + +--- + +## Video + +`