PageSourceSearch

https://royal1950.com/i18n.js

js royal1950.com collected 2026-10-08 11:25:58 UTC 6,012 bytes, 153 lines download raw bytes

1/* =========================================================================
2   i18n.js — client-side переводчик для AG3-шаблона
3   Default: English. 9 языков. Поддержка RTL для арабского.
4   Применяет переводы как можно раньше (до DOMContentLoaded) чтобы избежать
5   мерцания исходного текста.
6   ========================================================================= */
7(function () {
8  'use strict';
9
10  var DEFAULT_LANG = 'en';
11  var DICT = window.i18n || {};
12  var META = window.i18n_meta || {};
13  var SUPPORTED = Object.keys(DICT);
14  var STORAGE_KEY = 'siteLang';
15  var STORAGE_MANUAL = 'siteLangManual';
16
17  /* Берём первый поддерживаемый язык из navigator.languages (массив с fallback'ами),
18     иначе из navigator.language. "en-US" → "en", "zh-CN" → "zh", "ar-SA" → "ar". */
19  function browserLang() {
20    var list = [];
21    if (navigator.languages && navigator.languages.length) {
22      list = navigator.languages;
23    } else if (navigator.language) {
24      list = [navigator.language];
25    } else if (navigator.userLanguage) {
26      list = [navigator.userLanguage];
27    }
28    for (var i = 0; i < list.length; i++) {
29      var code = (list[i] + '').slice(0, 2).toLowerCase();
30      if (SUPPORTED.indexOf(code) !== -1) return code;
31    }
32    return null;
33  }
34
35  /* Приоритет:
36     1) URL ?lang=xx — прямая ссылка на язык (для шеринга)
37     2) localStorage — ТОЛЬКО если юзер ЯВНО кликал на флаг (siteLangManual=1)
38     3) navigator.languages → navigator.language — автодетект браузера
39     4) "en" — английский по умолчанию */
40  function detectLang() {
41    try {
42      var u = new URLSearchParams(location.search).get('lang');
43      if (u && SUPPORTED.indexOf(u) !== -1) return u;
44    } catch (e) {}
45    try {
46      if (localStorage.getItem(STORAGE_MANUAL) === '1') {
47        var s = localStorage.getItem(STORAGE_KEY);
48        if (s && SUPPORTED.indexOf(s) !== -1) return s;
49      }
50    } catch (e) {}
51    var b = browserLang();
52    if (b) return b;
53    return DEFAULT_LANG;
54  }
55
56  function applyTranslations(lang) {
57    var dict = DICT[lang] || DICT[DEFAULT_LANG] || {};
58    var meta = META[lang] || META[DEFAULT_LANG] || {};
59
60    /* TextContent для [data-i18n] */
61    var nodes = document.querySelectorAll('[data-i18n]');
62    for (var i = 0; i < nodes.length; i++) {
63      var key = nodes[i].getAttribute('data-i18n');
64      if (dict[key] !== undefined) {
65        nodes[i].textContent = dict[key];
66      }
67    }
68
69    /* Атрибуты через [data-i18n-attr="placeholder:key,title:key"] */
70    var attrNodes = document.querySelectorAll('[data-i18n-attr]');
71    for (var j = 0; j < attrNodes.length; j++) {
72      var pairs = attrNodes[j].getAttribute('data-i18n-attr').split(',');
73      for (var k = 0; k < pairs.length; k++) {
74        var parts = pairs[k].split(':');
75        if (parts.length === 2) {
76          var attr = parts[0].trim();
77          var akey = parts[1].trim();
78          if (dict[akey] !== undefined) {
79            attrNodes[j].setAttribute(attr, dict[akey]);
80          }
81        }
82      }
83    }
84
85    /* <html lang> + dir */
86    document.documentElement.lang = lang;
87    document.documentElement.dir = meta.rtl ? 'rtl' : 'ltr';
88    if (meta.rtl) document.documentElement.classList.add('rtl');
89    else document.documentElement.classList.remove('rtl');
90
91    /* Активная кнопка в горизонтальной полосе языков */
92    var btns = document.querySelectorAll('.lang-btn');
93    for (var b = 0; b < btns.length; b++) {
94      btns[b].classList.toggle('active', btns[b].getAttribute('data-lang') === lang);
95    }
96  }
97
98  function changeLang(lang) {
99    if (SUPPORTED.indexOf(lang) === -1) return;
100    applyTranslations(lang);
101    /* Сохраняем выбор + помечаем как явный (manual) — теперь localStorage
102       будет иметь приоритет над автодетектом при следующих визитах. */
103    try {
104      localStorage.setItem(STORAGE_KEY, lang);
105      localStorage.setItem(STORAGE_MANUAL, '1');
106    } catch (e) {}
107    /* Обновляем URL без перезагрузки */
108    try {
109      var url = new URL(location.href);
110      url.searchParams.set('lang', lang);
111      history.replaceState(null, '', url);
112    } catch (e) {}
113  }
114
115  /* Инициализация: язык уже задетекчен inline-скриптом в <head> через
116     window.__i18nLang. Это предотвращает мерцание текста (FOUC).
117     Если переменной нет (например inline-скрипт не выполнился) — детектим заново. */
118  var initialLang = (window.__i18nLang && SUPPORTED.indexOf(window.__i18nLang) !== -1)
119    ? window.__i18nLang
120    : detectLang();
121  document.documentElement.lang = initialLang;
122  if ((META[initialLang] || {}).rtl) {
123    document.documentElement.dir = 'rtl';
124    document.documentElement.classList.add('rtl');
125  }
126
127  function bindUI() {
128    /* Кнопки выбора языка в горизонтальной полосе */
129    var btns = document.querySelectorAll('.lang-btn');
130    for (var i = 0; i < btns.length; i++) {
131      btns[i].addEventListener('click', function (e) {
132        e.preventDefault();
133        var lang = this.getAttribute('data-lang');
134        changeLang(lang);
135      });
136    }
137  }
138
139  if (document.readyState === 'loading') {
140    document.addEventListener('DOMContentLoaded', function () {
141      applyTranslations(initialLang);
142      bindUI();
143    });
144  } else {
145    applyTranslations(initialLang);
146    bindUI();
147  }
148
149  /* Экспорт */
150  window.changeLang = changeLang;
151  window.applyTranslations = applyTranslations;
152  window.detectLang = detectLang;
153})();

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.