PageSourceSearch

https://festivaldelvalle.com/assets/js/i18n.js?v=2

js festivaldelvalle.com collected 2026-10-02 00:49:35 UTC 9,367 bytes, 267 lines download raw bytes

1(function () {
2  'use strict';
3
4  var DEFAULT_LANG = 'es';
5  var SUPPORTED_LANGS = ['es', 'en'];
6  var translations = {};
7  var currentLang = DEFAULT_LANG;
8
9  function getStoredLang() {
10    try {
11      return localStorage.getItem('festivalle-lang');
12    } catch (e) {
13      return null;
14    }
15  }
16
17  function setStoredLang(lang) {
18    try {
19      localStorage.setItem('festivalle-lang', lang);
20    } catch (e) {}
21  }
22
23  function detectLang() {
24    var stored = getStoredLang();
25    if (stored && SUPPORTED_LANGS.indexOf(stored) !== -1) return stored;
26
27    var urlParams = new URLSearchParams(window.location.search);
28    var urlLang = urlParams.get('lang');
29    if (urlLang && SUPPORTED_LANGS.indexOf(urlLang) !== -1) return urlLang;
30
31    var browserLang = (navigator.language || navigator.userLanguage || '').substring(0, 2);
32    if (SUPPORTED_LANGS.indexOf(browserLang) !== -1) return browserLang;
33
34    return DEFAULT_LANG;
35  }
36
37  function getNestedValue(obj, path) {
38    var keys = path.split('.');
39    var val = obj;
40    for (var i = 0; i < keys.length; i++) {
41      if (val == null) return undefined;
42      val = val[keys[i]];
43    }
44    return val;
45  }
46
47  function t(key) {
48    var val = getNestedValue(translations[currentLang], key);
49    if (val !== undefined) return val;
50    val = getNestedValue(translations[DEFAULT_LANG], key);
51    return val !== undefined ? val : key;
52  }
53
54  function applyTranslations() {
55    document.documentElement.lang = currentLang;
56
57    // data-i18n: sets textContent
58    var elements = document.querySelectorAll('[data-i18n]');
59    for (var i = 0; i < elements.length; i++) {
60      var el = elements[i];
61      var key = el.getAttribute('data-i18n');
62      var val = t(key);
63      if (typeof val === 'string') {
64        el.textContent = val;
65      }
66    }
67
68    // data-i18n-html: sets innerHTML (for content with HTML)
69    var htmlElements = document.querySelectorAll('[data-i18n-html]');
70    for (var i = 0; i < htmlElements.length; i++) {
71      var el = htmlElements[i];
72      var key = el.getAttribute('data-i18n-html');
73      var val = t(key);
74      if (typeof val === 'string') {
75        el.innerHTML = val;
76      }
77    }
78
79    // data-i18n-placeholder
80    var placeholders = document.querySelectorAll('[data-i18n-placeholder]');
81    for (var i = 0; i < placeholders.length; i++) {
82      var el = placeholders[i];
83      var key = el.getAttribute('data-i18n-placeholder');
84      var val = t(key);
85      if (typeof val === 'string') {
86        el.placeholder = val;
87      }
88    }
89
90    // Document title + meta description.
91    // Páginas con data-i18n-meta="<scope>" en <html> usan <scope>.metaTitle/.metaDescription;
92    // el resto usa las claves globales meta.title / meta.description.
93    var metaScope = document.documentElement.getAttribute('data-i18n-meta');
94    var titleKey = metaScope ? metaScope + '.metaTitle' : 'meta.title';
95    var descKey = metaScope ? metaScope + '.metaDescription' : 'meta.description';
96    var title = t(titleKey);
97    if (title) document.title = title;
98
99    // Update meta description
100    var metaDesc = document.querySelector('meta[name="description"]');
101    if (metaDesc) {
102      var desc = t(descKey);
103      if (desc) metaDesc.setAttribute('content', desc);
104    }
105
106    // Update language switcher active state
107    var switchers = document.querySelectorAll('[data-lang-switch]');
108    for (var i = 0; i < switchers.length; i++) {
109      var btn = switchers[i];
110      if (btn.getAttribute('data-lang-switch') === currentLang) {
111        btn.classList.add('active');
112      } else {
113        btn.classList.remove('active');
114      }
115    }
116
117    // Translate press cards (history page)
118    var isEn = currentLang === 'en';
119    var pressCards = document.querySelectorAll('.press-card');
120    for (var i = 0; i < pressCards.length; i++) {
121      var card = pressCards[i];
122      var titleEl = card.querySelector('.press-card-title');
123      var descEl = card.querySelector('.press-card-desc');
124      var linkEl = card.querySelector('.press-card-link');
125      if (titleEl) {
126        var enTitle = card.getAttribute('data-title-en');
127        var esTitle = card.getAttribute('data-title-es') || titleEl.getAttribute('data-original');
128        if (!titleEl.getAttribute('data-original')) titleEl.setAttribute('data-original', titleEl.textContent);
129        if (isEn && enTitle) titleEl.textContent = enTitle;
130        else if (!isEn && titleEl.getAttribute('data-original')) titleEl.textContent = titleEl.getAttribute('data-original');
131      }
132      if (descEl) {
133        var enDesc = card.getAttribute('data-desc-en');
134        var esDesc = descEl.getAttribute('data-original');
135        if (!descEl.getAttribute('data-original')) descEl.setAttribute('data-original', descEl.textContent);
136        if (isEn && enDesc) descEl.textContent = enDesc;
137        else if (!isEn && descEl.getAttribute('data-original')) descEl.textContent = descEl.getAttribute('data-original');
138      }
139      if (linkEl) {
140        if (!linkEl.getAttribute('data-original')) linkEl.setAttribute('data-original', linkEl.textContent);
141        if (isEn) {
142          var origLink = linkEl.getAttribute('data-original') || linkEl.textContent;
143          linkEl.textContent = origLink.indexOf('deo') >= 0 ? 'Watch video →' : 'Read article →';
144        } else {
145          linkEl.textContent = linkEl.getAttribute('data-original');
146        }
147      }
148    }
149    // Translate press awards
150    var awards = document.querySelectorAll('.press-award');
151    for (var i = 0; i < awards.length; i++) {
152      var award = awards[i];
153      var aTitle = award.querySelector('.press-award-title');
154      var aSub = award.querySelector('.press-award-subtitle');
155      if (aTitle) {
156        if (!aTitle.getAttribute('data-original')) aTitle.setAttribute('data-original', aTitle.textContent);
157        if (isEn) {
158          var txt = aTitle.getAttribute('data-original');
159          if (txt === 'Mejor Festival del Año') aTitle.textContent = 'Best Festival of the Year';
160          else if (txt === 'Vuelta tras la pandemia') aTitle.textContent = 'Back After the Pandemic';
161          else aTitle.textContent = txt;
162        } else {
163          aTitle.textContent = aTitle.getAttribute('data-original');
164        }
165      }
166      if (aSub) {
167        if (!aSub.getAttribute('data-original')) aSub.setAttribute('data-original', aSub.textContent);
168        if (isEn) {
169          var stxt = aSub.getAttribute('data-original');
170          if (stxt.indexOf('2º año') >= 0) aSub.textContent = 'Ruta 66 Magazine — 2nd consecutive year';
171          else if (stxt.indexOf('Revista Ruta') >= 0) aSub.textContent = 'Ruta 66 Magazine';
172          else if (stxt.indexOf('festival regresa') >= 0) aSub.textContent = 'XIV Edition — The festival returns';
173          else aSub.textContent = stxt;
174        } else {
175          aSub.textContent = aSub.getAttribute('data-original');
176        }
177      }
178    }
179
180    // Re-apply circular text rotation (scripts.js splits text into spans)
181    var rotateTexts = document.querySelectorAll('.rotate-text p, .rotate-text2 p, .rotate-text3 p');
182    for (var i = 0; i < rotateTexts.length; i++) {
183      var el = rotateTexts[i];
184      var plainText = el.textContent;
185      if (plainText && plainText.length > 0) {
186        el.innerHTML = plainText.split('').map(function (char, idx) {
187          return '<span style="transform:rotate(' + (idx * 9.6) + 'deg)">' + char + '</span>';
188        }).join('');
189      }
190    }
191
192    // Avisar a otros módulos (banner de cookies) de que se aplicaron traducciones.
193    try {
194      document.dispatchEvent(new CustomEvent('fv:i18n-applied', { detail: { lang: currentLang } }));
195    } catch (e) {}
196  }
197
198  function loadTranslation(lang, callback) {
199    if (translations[lang]) {
200      callback();
201      return;
202    }
203    var xhr = new XMLHttpRequest();
204    xhr.open('GET', 'assets/i18n/' + lang + '.json', true);
205    xhr.onreadystatechange = function () {
206      if (xhr.readyState === 4) {
207        if (xhr.status === 200) {
208          try {
209            translations[lang] = JSON.parse(xhr.responseText);
210          } catch (e) {
211            console.error('i18n: parse error for ' + lang, e);
212          }
213        }
214        callback();
215      }
216    };
217    xhr.send();
218  }
219
220  function switchLanguage(lang) {
221    if (SUPPORTED_LANGS.indexOf(lang) === -1) return;
222    currentLang = lang;
223    setStoredLang(lang);
224    loadTranslation(lang, function () {
225      applyTranslations();
226    });
227  }
228
229  function init() {
230    currentLang = detectLang();
231
232    // Load default + current language
233    loadTranslation(DEFAULT_LANG, function () {
234      if (currentLang !== DEFAULT_LANG) {
235        loadTranslation(currentLang, function () {
236          applyTranslations();
237        });
238      } else {
239        applyTranslations();
240      }
241    });
242
243    // Bind language switcher clicks
244    document.addEventListener('click', function (e) {
245      var btn = e.target.closest('[data-lang-switch]');
246      if (btn) {
247        e.preventDefault();
248        var lang = btn.getAttribute('data-lang-switch');
249        switchLanguage(lang);
250      }
251    });
252  }
253
254  // Expose globally
255  window.i18n = {
256    t: t,
257    switchLanguage: switchLanguage,
258    getCurrentLang: function () { return currentLang; }
259  };
260
261  // Init on DOM ready
262  if (document.readyState === 'loading') {
263    document.addEventListener('DOMContentLoaded', init);
264  } else {
265    init();
266  }
267})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.