PageSourceSearch

https://www.wheelofnamesgenerator.com/js/i18n.js

js wheelofnamesgenerator.com collected 2026-10-02 00:35:02 UTC 4,745 bytes, 140 lines download raw bytes

1/* ============================================
2   WHEEL OF NAMES — Internationalization (i18n)
3   ============================================ */
4
5export class I18n {
6  constructor() {
7    this.currentLang = 'en';
8    this.translations = {};
9    this.fallback = {};
10    this.languages = {
11      en: { name: 'English', flag: '🇬🇧' },
12      ar: { name: 'العربية', flag: '🇸🇦' },
13      bn: { name: 'বাংলা', flag: '🇧🇩' },
14      de: { name: 'Deutsch', flag: '🇩🇪' },
15      es: { name: 'Español', flag: '🇪🇸' },
16      fr: { name: 'Français', flag: '🇫🇷' },
17      hi: { name: 'हिन्दी', flag: '🇮🇳' },
18      id: { name: 'Bahasa Indonesia', flag: '🇮🇩' },
19      it: { name: 'Italiano', flag: '🇮🇹' },
20      ja: { name: '日本語', flag: '🇯🇵' },
21      kn: { name: 'ಕನ್ನಡ', flag: '🇮🇳' },
22      ko: { name: '한국어', flag: '🇰🇷' },
23      mr: { name: 'मराठी', flag: '🇮🇳' },
24      nl: { name: 'Nederlands', flag: '🇳🇱' },
25      pl: { name: 'Polski', flag: '🇵🇱' },
26      pt: { name: 'Português', flag: '🇵🇹' },
27      ru: { name: 'Русский', flag: '🇷🇺' },
28      sv: { name: 'Svenska', flag: '🇸🇪' },
29      ta: { name: 'தமிழ்', flag: '🇮🇳' },
30      te: { name: 'తెలుగు', flag: '🇮🇳' },
31      th: { name: 'ไทย', flag: '🇹🇭' },
32      tr: { name: 'Türkçe', flag: '🇹🇷' },
33      uk: { name: 'Українська', flag: '🇺🇦' },
34      vi: { name: 'Tiếng Việt', flag: '🇻🇳' },
35      zh: { name: '中文', flag: '🇨🇳' },
36    };
37  }
38
39  async init() {
40    // 1. Check URL path first (e.g. /uk/ -> 'uk')
41    const pathParts = window.location.pathname.split('/').filter(Boolean);
42    const pathLang = pathParts[0];
43
44    let validLang = 'en';
45    if (pathLang && this.languages[pathLang]) {
46      validLang = pathLang;
47    } else if (window.location.pathname === '/' || window.location.pathname === '') {
48      // 2. If exactly on the root URL, check if there's a saved layout preference
49      const storedLang = localStorage.getItem('wof_lang') || this._getCookie('wof_lang');
50      if (storedLang && storedLang !== 'en' && this.languages[storedLang]) {
51        // Auto-redirect returning users to their language directory instead of serving English
52        window.location.replace(`/${storedLang}/`);
53        return;
54      }
55    }
56
57    // Load English as fallback
58    this.fallback = await this._loadLang('en');
59    if (validLang !== 'en') {
60      this.translations = await this._loadLang(validLang);
61    } else {
62      this.translations = this.fallback;
63    }
64
65    this.currentLang = validLang;
66    this._persist(validLang);
67    this.apply();
68    return validLang;
69  }
70
71  async _loadLang(code) {
72    try {
73      const resp = await fetch(`/lang/${code}.json`);
74      if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
75      return await resp.json();
76    } catch (e) {
77      console.warn(`Failed to load language: ${code}`, e);
78      return {};
79    }
80  }
81
82  async setLanguage(code) {
83    if (!this.languages[code]) return;
84    if (code === 'en') {
85      this.translations = this.fallback;
86    } else {
87      this.translations = await this._loadLang(code);
88    }
89    this.currentLang = code;
90    this._persist(code);
91    this.apply();
92
93    // Update HTML lang attribute
94    document.documentElement.lang = code;
95  }
96
97  t(key, params = {}) {
98    let text = this.translations[key] || this.fallback[key] || key;
99    // Replace {{param}} placeholders
100    Object.keys(params).forEach(k => {
101      text = text.replace(new RegExp(`\\{\\{${k}\\}\\}`, 'g'), params[k]);
102    });
103    return text;
104  }
105
106  apply() {
107    document.querySelectorAll('[data-i18n]').forEach(el => {
108      const key = el.getAttribute('data-i18n');
109      const text = this.t(key);
110      if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
111        el.placeholder = text;
112      } else {
113        el.textContent = text;
114      }
115    });
116
117    document.querySelectorAll('[data-i18n-title]').forEach(el => {
118      el.title = this.t(el.getAttribute('data-i18n-title'));
119    });
120
121    document.querySelectorAll('[data-i18n-aria]').forEach(el => {
122      el.setAttribute('aria-label', this.t(el.getAttribute('data-i18n-aria')));
123    });
124
125    document.documentElement.lang = this.currentLang;
126  }
127
128  _persist(code) {
129    localStorage.setItem('wof_lang', code);
130    document.cookie = `wof_lang=${code};path=/;max-age=31536000;SameSite=Lax`;
131  }
132
133  _getCookie(name) {
134    const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
135    return match ? match[2] : null;
136  }
137
138  getLanguages() { return this.languages; }
139  getCurrentLang() { return this.currentLang; }
140}

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.