PageSourceSearch

https://app.simby.pt/js/i18n.js?v=20260929c

js simby.pt collected 2026-10-07 23:04:23 UTC 6,383 bytes, 145 lines download raw bytes

1/* ============================================================
2   PT | EN na plataforma (29/09/2026)
3   Regra: tudo no simby existe em PT e EN. As páginas escrevem-se em PT; este
4   ficheiro traduz para EN quando é essa a língua escolhida:
5   - o texto das páginas (nós de texto e placeholder/title/aria-label) é trocado
6     pelo dicionário SIMBY_EN (js/i18n-en.js), frase a frase, também no que é
7     acrescentado depois (MutationObserver);
8   - textos com variáveis usam t('... {nome} ...', { nome }) no código.
9   A língua fica em localStorage (simby_idioma) e, com sessão, nos metadados
10   da conta (idioma), que decidem a língua dos emails e dos avisos.
11   Carregar depois de js/supabase.js e de js/i18n-en.js.
12   ============================================================ */
13;(function () {
14  const EN = window.SIMBY_EN = window.SIMBY_EN || {}
15  const lingua = () => (window.simbyIdioma ? simbyIdioma() : 'pt')
16  window.simbyEmIngles = () => lingua() === 'en'
17
18  window.t = function (pt, vars) {
19    let s = (lingua() === 'en' && EN[pt]) || pt
20    if (vars) for (const k of Object.keys(vars)) s = s.split('{' + k + '}').join(vars[k])
21    return s
22  }
23
24  const ATRIBUTOS = ['placeholder', 'title', 'aria-label', 'alt']
25  const IGNORAR = new Set(['SCRIPT', 'STYLE', 'TEXTAREA', 'CODE', 'PRE'])
26
27  function traduzirTexto(no) {
28    const v = no.nodeValue
29    const k = v.trim()
30    if (!k) return
31    const en = EN[k] || EN[k.replace(/\s+/g, ' ')]
32    if (en && en !== k) no.nodeValue = v.replace(k, en)
33  }
34  function traduzirElemento(el) {
35    if (el.closest && el.closest('[data-sem-traducao]')) return
36    for (const a of ATRIBUTOS) {
37      const v = el.getAttribute && el.getAttribute(a)
38      if (v && EN[v.trim()]) el.setAttribute(a, EN[v.trim()])
39    }
40    if ((el.tagName === 'INPUT') && (el.type === 'button' || el.type === 'submit') && EN[el.value]) el.value = EN[el.value]
41  }
42  window.simbyTraduzir = function (raiz) {
43    if (lingua() !== 'en' || !raiz) return
44    if (raiz.nodeType === 3) { traduzirTexto(raiz); return }
45    if (raiz.nodeType !== 1 || IGNORAR.has(raiz.tagName)) return
46    traduzirElemento(raiz)
47    const w = document.createTreeWalker(raiz, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, {
48      acceptNode: n => (n.nodeType === 1 && IGNORAR.has(n.tagName)) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT,
49    })
50    let n
51    while ((n = w.nextNode())) {
52      if (n.nodeType === 3) {
53        if (!(n.parentElement && n.parentElement.closest('[data-sem-traducao]'))) traduzirTexto(n)
54      } else traduzirElemento(n)
55    }
56  }
57
58  // Muda a língua: guarda-a, grava-a na conta (emails e avisos) e recarrega.
59  window.simbyMudarIdioma = async function (l) {
60    if (l !== 'pt' && l !== 'en') return
61    try { localStorage.setItem('simby_idioma', l); localStorage.setItem('simby_lang', l) } catch (_) {}
62    try {
63      if (window.db) {
64        const { data } = await db.auth.getSession()
65        if (data && data.session) await db.auth.updateUser({ data: { idioma: l } })
66      }
67    } catch (_) { /* sem sessão ou sem rede: fica só no navegador */ }
68    window.location.reload()
69  }
70
71  // Seletor PT | EN em pastilha, igual ao do simby.pt.
72  window.montarSeletorIdioma = function (alvo) {
73    if (!alvo || alvo.querySelector('.seletor-idioma')) return
74    const l = lingua()
75    const caixa = document.createElement('div')
76    caixa.className = 'seletor-idioma'
77    caixa.setAttribute('role', 'group')
78    caixa.setAttribute('aria-label', l === 'en' ? 'Language' : 'Idioma')
79    caixa.setAttribute('data-sem-traducao', '')
80    for (const c of ['pt', 'en']) {
81      const b = document.createElement('button')
82      b.type = 'button'
83      b.textContent = c.toUpperCase()
84      b.className = c === l ? 'ativo' : ''
85      b.setAttribute('aria-pressed', String(c === l))
86      if (c !== l) b.addEventListener('click', () => simbyMudarIdioma(c))
87      caixa.appendChild(b)
88    }
89    alvo.appendChild(caixa)
90  }
91
92  /* ---- Mensagens de erro do servidor (js/i18n/en-servidor.js) ---- */
93  const escRe = x => x.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
94  const PADROES = (window.SIMBY_ERROS_PADROES || []).map(([pt, en]) => {
95    // Cada % da função é um valor variável: vira grupo de captura e volta a
96    // entrar, pela mesma ordem, no lugar do % correspondente em inglês.
97    const re = new RegExp('^' + pt.split('%').map(escRe).join('(.+?)') + '$')
98    return [re, en]
99  })
100  window.traduzirErroServidor = function (msg) {
101    if (lingua() !== 'en' || typeof msg !== 'string') return msg
102    const k = msg.trim()
103    if (EN[k]) return EN[k]
104    for (const [re, en] of PADROES) {
105      const m = k.match(re)
106      if (m) { let i = 1; return en.replace(/%(\.2f)?/g, () => m[i++] ?? '') }
107    }
108    return msg
109  }
110  // As chamadas db.rpc() passam a devolver error.message já na língua escolhida.
111  function traduzirErrosDoCliente() {
112    if (!window.db || db.__errosTraduzidos || lingua() !== 'en') return
113    db.__errosTraduzidos = true
114    const rpc = db.rpc.bind(db)
115    db.rpc = function (...args) {
116      const pedido = rpc(...args)
117      const then = pedido.then.bind(pedido)
118      pedido.then = (ok, falha) => then(r => {
119        if (r && r.error && r.error.message) r.error.message = traduzirErroServidor(r.error.message)
120        return r
121      }).then(ok, falha)
122      return pedido
123    }
124  }
125  traduzirErrosDoCliente()
126
127  function arrancar() {
128    document.documentElement.lang = lingua() === 'en' ? 'en' : 'pt'
129    if (lingua() !== 'en') return
130    // Títulos com espaços que não quebram à volta da barra ("simby | …"): assim o
131    // Safari não esconde o "simby |" do separador. O dicionário usa espaços normais.
132    const titulo = document.title.replace(/\u00a0/g, ' ')
133    if (EN[titulo]) document.title = EN[titulo].replace(/ \| /g, '\u00a0|\u00a0')
134    simbyTraduzir(document.body)
135    new MutationObserver(lista => {
136      for (const m of lista) {
137        if (m.type === 'characterData') traduzirTexto(m.target)
138        else if (m.type === 'attributes') traduzirElemento(m.target)
139        else m.addedNodes.forEach(simbyTraduzir)
140      }
141    }).observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ATRIBUTOS })
142  }
143  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', arrancar)
144  else arrancar()
145})()

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.