/* Key register — translations.
Add a language by copying the `pt` block. Set UI_LANG in .env to choose. */
"use strict";
var I18N = {
pt: {
_months: ["jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez"],
brand: "Registo de Chaves", appTitle: "Armários de chaves", admin: "Administração",
loginSub: "Introduza o código partilhado.",
loginPlaceholder: "Código de acesso", loginBtn: "Entrar",
loginWrong: "Código incorreto.", loginMany: "Demasiadas tentativas. Aguarde um minuto.",
loginShow: "Mostrar código", loginHide: "Ocultar código",
loginFoot: "Acesso restrito à rede privada da igreja.",
loading: "A carregar…", noServer: "Não foi possível contactar o servidor.",
back: "Voltar ao registo",
tabBoard: "Quadro", tabDoors: "Portas", tabPeople: "Pessoas",
tabLog: "Movimentos", tabJournal: "Diário",
cabinetsN: (n) => `${n} ${n === 1 ? "armário" : "armários"}`,
keysIn: (a, b) => `${a} de ${b} chaves no armário`,
issuedN: "emprestadas", overdueN: "em atraso", reserveN: "de reserva",
searchPerson: "Procurar pessoa…",
/* pedido de acesso */
reqTitle: "Pedido de acesso",
reqIntro: "Preencha este formulário para pedir acesso a uma zona da igreja. O pedido é analisado pelo responsável pelas chaves — não recebe nenhuma chave por aqui.",
reqName: "Nome completo", reqDept: "Departamento ou grupo",
reqContact: "Telefone ou email", reqAreas: "Zonas a que precisa de aceder",
reqDetail: "Para quê, e durante quanto tempo",
reqDetailPh: "Ex.: ensaios do coro às quartas, a partir de setembro.",
reqPin: "Código da igreja", reqPinHelp: "Peça o código ao responsável pelas chaves.",
reqSend: "Enviar pedido", reqSending: "A enviar…",
reqOk: "Pedido enviado.",
reqOkMore: "O responsável pelas chaves vai analisá-lo. Não guarde este separador aberto.",
reqAnother: "Enviar outro pedido",
reqNeedName: "Indique o seu nome.", reqNeedArea: "Escolha pelo menos uma zona.",
reqWrongPin: "Código incorreto.", reqTooMany: "Demasiados pedidos. Tente mais tarde.",
reqFoot: "Nenhuma chave é entregue através deste formulário.",
/* caixa de pedidos */
reqHandPhone: "Passar o telemóvel para alguém preencher",
reqKioskDone: "Pode devolver o telemóvel.",
reqKioskNext: "Novo pedido",
reqNoPinStaff: "Sem código — o pedido está a ser feito a partir da aplicação.",
reqFormPublic: "Formulário público, protegido por código.",
reqFormOpen: "FORMULÁRIO PÚBLICO SEM CÓDIGO — qualquer pessoa que descubra o endereço pode enviar pedidos.",
reqFormPrivate: "Formulário só acessível a quem entra na aplicação.",
reqFormOpenIt: "Abrir formulário",
reqInbox: "Pedidos de acesso", reqPending: (n) => `${n} por tratar`,
reqNone: "Nenhum pedido pendente.",
reqOn: (d) => `Recebido em ${d}`, reqMarkDone: "Marcar como tratado",
reqNotePh: "O que foi decidido?", reqHandled: "Tratado",
reqHandledOn: (d) => `Tratado em ${d}`,
/* reports */
reports: "Relatórios", print: "Imprimir", printPdf: "Imprimir / PDF",
repOut: "Chaves emprestadas", repOverdue: "Chaves em atraso",
repPerson: "Recibo por pessoa", repInventory: "Inventário completo",
repAudit: "Folha de auditoria",
repGenerated: (d, h) => `Gerado em ${d} às ${h}`,
repNothing: "Nada a mostrar.",
repChoosePerson: "Escolha uma pessoa",
stKeysOut: "emprestadas", stPeople: "pessoas", stOverdue: "em atraso",
stTotal: "total", stInCab: "no armário", stReserve: "reserva",
cTag: "Nº", cDoor: "Porta", cBuilding: "Edifício", cCabinet: "Armário",
cPerson: "Pessoa", cDept: "Departamento", cOut: "Entregue", cDue: "Devolver até",
cDays: "Dias", cStatus: "Estado", cHook: "Gancho", cCheck: "Conf.",
repSignedOn: (d) => `Assinado em ${d}`, repNoSig: "Sem assinatura registada",
repReceiptTitle: "Recibo de chaves",
repReceiptIntro: (n) => `Declaro ter em minha posse ${n === 1 ? "a chave" : "as"} ${n === 1 ? "" : n + " chaves"} abaixo indicadas, e comprometo-me a devolvê-${n === 1 ? "la" : "las"} quando solicitado.`,
repHolder: "Titular", repResponsible: "Responsável pelas chaves", repDate: "Data",
repAuditIntro: "Confirme cada chave presente no armário e assinale a caixa. As chaves emprestadas estão indicadas.",
repAuditBy: "Auditoria feita por", repNotes: "Observações",
repAllCabinets: "Todos os armários",
allTile: "Todos",
statuses: { in: "no armário", out: "emprestada", reserve: "reserva", lost: "perdida", retired: "retirada" },
fEverything: "Tudo", fKeys: "Só chaves", fTags: "Etiquetas",
fOut: "Emprestadas", fLate: "Em atraso",
lgIn: "No armário", lgOut: "Emprestada", lgRes: "Reserva",
lgLate: "Em atraso", lgTag: "Etiqueta de alarme",
outCount: (n) => `${n} fora`,
boardNote: "Um gancho por cada chave física, pela ordem do armário.
Para saber quem abre uma porta, use Portas.",
noMatch: "Nada corresponde a esse filtro.",
noKeysYet: "Ainda não há chaves. Adicione-as na",
onHook: "No seu gancho", reserveCopy: "Cópia de reserva",
lost: "Perdida", retired: "Retirada", allIn: "todas no armário",
copies: (n) => `${n} ${n === 1 ? "cópia" : "cópias"}`,
doorsN: (n) => `${n} ${n === 1 ? "porta" : "portas"}`,
searchPlaceholder: "Porta, número de chave, pessoa ou nota",
nothingFound: (q) => `Nada encontrado para «${q}».`,
doorsNote: "Uma linha por porta. Os pontos são as cópias — cheio está no armário,
vazio está com alguém, cinzento é a reserva.",
tagsSection: "Etiquetas de alarme",
holdingNow: "Com chaves neste momento", nothingOut: "Sem chaves",
inactive: "inativo",
keysWord: (n) => `${n} ${n === 1 ? "chave" : "chaves"}`,
tagsWord: (n) => `${n} ${n === 1 ? "etiqueta" : "etiquetas"}`,
noPeopleYet: "Ainda não há pessoas. Adicione-as na",
logTitle: "Todas as entregas, mais recentes primeiro",
toWord: "para", backFrom: "devolvida por", signedMark: "assinado",
noHandovers: "Ainda não há entregas registadas.",
logNote: "As linhas só são acrescentadas, nunca alteradas.",
dirOut: "saída", dirIn: "entrada",
newEntry: "Nova entrada",
journalPlaceholder: "Auditoria feita, fechadura mudada, selo de reserva quebrado…",
addJournal: "Adicionar ao diário", journalTitle: "Diário dos armários",
journalEmpty: "Ainda não há registos.",
journalNote: "Para tudo o que pertence a um armário e não a uma chave.",
withSince: (who, d) => `Com ${who} desde ${d}.`,
dueWas: (d, n) => `
Devolução prevista ${d} — há ${n} dias.`,
issuedToOn: (who, d) => `Entregue a ${who} em ${d}`,
dueBack: (d) => `
Devolução prevista ${d}.`,
noDueDate: "
Sem data de devolução — posse permanente.",
reserveLine: "Cópia de reserva, fora das instalações. A entrega fica automaticamente no diário.",
lostLine: "Dada como perdida. Considere mudar a fechadura desta porta.",
retiredLine: "Retirada. Mantida apenas pelo histórico.",
inCabinet: (c) => `No ${c}`, atHook: (h) => `, gancho ${h}`,
fType: "Tipo", vKey: "Chave de porta", vTag: "Etiqueta NFC de alarme",
fKeptIn: "Guardada em", fHook: "Gancho", fLockGroup: "Grupo de fechadura",
fChipUid: "UID do chip", fMaster: "Chave-mestra", yes: "Sim", no: "Não",
fCopiesLiveIn: "Cópias guardadas em", fHoldingNow: "Tem agora",
fReturnedPast: "Devolvidas no passado", fPhone: "Telefone",
seeAllCopies: (n) => `Ver todas as ${n} cópias`,
noteOnHandover: "Nota desta entrega",
noteOnKey: "Nota sobre esta chave — permanente",
notePlaceholder: "Algo que a próxima pessoa deva saber.",
saveNote: "Guardar nota",
noteOnDoor: "Nota sobre esta porta",
issueTo: "Entregar a alguém", breakSeal: "Quebrar o selo e entregar",
takeBack: "Receber de volta", close: "Fechar", cancel: "Cancelar",
history: "Histórico", neverIssued: "Nunca foi entregue.",
returnedOn: (d) => `devolvida em ${d}`, stillHolding: "ainda a tem",
stClosed: "fechado", stOpen: "aberto", stLate: "atraso",
currentlyHolds: "Tem neste momento", holdsNothing: "Não tem nada de momento.",
returnedSection: "Devolvidas", heldFromTo: (a, b) => `esteve de ${a} a ${b}`,
itemsN: (n) => `${n} ${n === 1 ? "item" : "itens"}`,
fromCabinets: (s) => `de ${s}`,
issueTitle: (t) => `Entregar ${t}`, fromThe: (c) => `do ${c}`,
whoTakes: "Quem a vai levar?",
reserveWarn: "Esta é uma cópia de último recurso. Ao entregá-la, a porta fica sem reserva.",
noActivePeople: "Não há pessoas ativas. Adicione-as na",
handover: "Entrega", backBy: "Devolver até",
dNone: "Sem data — permanente", d7: "Uma semana", d30: "Um mês", d90: "Três meses",
whyNote: "Motivo — nota desta entrega",
whyPlaceholder: "Concerto de Natal, revisão da caldeira, novo sacristão…",
signature: "Assinatura", signHint: "Dê o telemóvel para assinar",
clearSig: "Limpar", confirmHandover: "Confirmar entrega",
everyCopy: "Todas as cópias desta chave",
sameLock: "Mesmo grupo de fechadura — estas também abrem",
doorSummary: (total, out, inC, res, lst) => {
const parts = [];
if (out) parts.push(`${out} com pessoas`);
parts.push(`${inC} no armário`);
if (res) parts.push(`${res} de reserva`);
if (lst) parts.push(`${lst} perdidas`);
return `Existem ${total} ${total === 1 ? "cópia" : "cópias"}. ${parts.join(", ")}.`;
},
tHandedOver: "Entregue", tBackIn: "De volta ao armário",
tNoteSaved: "Nota guardada", tJournalAdded: "Entrada adicionada",
tSaved: "Guardado", tRemoved: "Removido",
errors: {
"already issued": "Esta chave já está emprestada.",
"not currently issued": "Esta chave não está emprestada.",
"no such item": "Chave não encontrada.",
"no such person": "Pessoa não encontrada.",
"not issuable": "Esta chave não pode ser entregue.",
"tag required": "O número da chave é obrigatório.",
"name required": "O nome é obrigatório.",
"that tag is already used": "Esse número de chave já existe.",
"door number already used": "Esse número de porta já existe.",
"num, building and name required": "Número, edifício e nome são obrigatórios.",
"id and name required": "Código e nome são obrigatórios.",
"confirmation phrase required": "Tem de escrever a frase de confirmação.",
"empty": "Está vazio.",
"locked": "Sessão terminada. Recarregue a página.",
"wrong pin": "Código incorreto.",
"too many requests": "Demasiados pedidos. Tente mais tarde.",
},
/* admin */
aItems: "Chaves e etiquetas", aDoors: "Portas", aPeople: "Pessoas",
aCabinets: "Armários", aKinds: "Tipos", aData: "Importar / cópias", aAudit: "Alterações",
aKindsHelp: "Os tipos de objeto que guarda. A forma diz o que é, o preenchimento diz onde está.",
aAddKind: "+ Adicionar tipo",
fKindCode: "Código", fKindName: "Nome", fShape: "Forma", fColour: "Cor",
shCircle: "Círculo", shRounded: "Quadrado arredondado", shSquare: "Quadrado",
deleteKind: "Eliminar este tipo",
kindNote: "O código é usado nas importações (coluna kind) e não deve mudar depois de haver chaves com ele. O nome pode mudar à vontade.",
kindInUse: (n) => `${n} itens usam este tipo.`,
aItemsHelp: (n) => `Uma linha por cada chave física. ${n} no total.`,
aFilter: "Filtrar por número, porta ou nota",
aAddItem: "+ Adicionar chave ou etiqueta", aAddDoor: "+ Adicionar porta",
aAddPerson: "+ Adicionar pessoa", aAddCabinet: "+ Adicionar armário",
aTag: "Nº", aDoor: "Porta / zona", aCabinet: "Armário", aHook: "Gancho",
aStatus: "Estado", aEdit: "Editar", aNothing: "Ainda não há nada.",
aDoorsHelp: (n) => `As portas físicas. As chaves apontam para estas. ${n} no total.`,
aNo: "Nº", aLock: "Fechadura", aCopies: "Cópias",
aPeopleHelp: "Titulares de chaves. Nunca fazem login — isto é só quem pode receber uma chave.",
aName: "Nome", aDept: "Departamento", aHolding: "Tem",
aCabinetsHelp: "Onde as chaves ficam fisicamente. Um armário não é um edifício — o escritório pode guardar mestras da igreja.",
aOffSite: "fora das instalações", aItemsCount: (n) => `${n} itens`,
fNum: "Número", fBuilding: "Edifício", fDoorName: "Nome da porta", fNote: "Nota",
fShortCode: "Código curto", fShortName: "Nome curto", fFullName: "Nome completo",
fWhereIs: "Onde está e o que a abre", fSort: "Ordem no quadro",
fSortHelp: "Número menor aparece primeiro. Deixe a reserva com um número alto para ficar no fim.",
fOffSiteLabel: "Fora das instalações — depósito de reserva",
fZoneKeys: "Porta (chaves)", fZoneTags: "Zona de alarme (etiquetas)",
fNoneTag: "— nenhuma / etiqueta de alarme —", fNone: "— nenhum —",
fIsMaster: "É uma chave-mestra", fPermNote: "Nota permanente",
fFunction: "Função", fEmail: "Email",
fActiveLabel: "Ativo — pode receber chaves",
save: "Guardar", deleteRetire: "Eliminar ou retirar esta chave",
deleteDoor: "Eliminar esta porta", deactivate: "Desativar",
deleteCabinet: "Eliminar este armário",
retireNote: "Se tiver histórico de entregas é retirada, nunca eliminada — o histórico tem de sobreviver.",
peopleNote: "As pessoas nunca são eliminadas. Aparecem em entregas antigas, e eliminá-las tornaria esse histórico anónimo.",
lockGroupNote: "O grupo de fechadura liga portas que partilham o mesmo perfil de chave, para saber o que mais uma chave perdida poderá abrir.",
offSiteNote: "As chaves devolvidas a um depósito fora das instalações voltam a reserva e não a no armário, ficando fora da circulação normal.",
holdsWarn: (n, tags) => `Tem ${n} ${n === 1 ? "item" : "itens"}: ${tags}. Receba-os de volta antes de desativar.`,
impWhat: "O que quer importar?",
impItems: "Chaves e etiquetas", impDoors: "Portas", impPeople: "Pessoas", impCabinets: "Armários",
impResult2: (c, u) => `Criados ${c}, atualizados ${u}.`,
impTitle: "Importar de uma folha de cálculo",
impHelp: "Cole CSV. Colunas obrigatórias: tag, door, building. Opcionais: lock_group, cabinet, hook, master, status, kind, notes. As portas e armários que não existirem são criados.",
impBtn: "Importar",
impResult: (i, d, c) => `Importados ${i} itens, criadas ${d} portas e ${c} armários.`,
impSkipped: (n, list) => `
Ignorados ${n}: ${list}`,
impNothing: "Não há nada para importar",
expTitle: "Exportar",
expHelp: "Todo o registo como folha de cálculo. Útil para uma cópia impressa no armário, e prova de que nunca fica preso a esta aplicação.",
expBtn: "Descarregar CSV",
bkTitle: "Cópia de segurança",
bkHelp: "Um único ficheiro SQLite, seguro de descarregar com a aplicação a funcionar. Guarde uma cópia fora desta máquina.",
bkBtn: "Descarregar base de dados",
wipeTitle: "Apagar tudo",
wipeWarn: "Elimina todas as chaves, portas, pessoas, entregas e entradas do diário. Os armários são mantidos. Não há como desfazer — faça primeiro uma cópia de segurança.",
wipePlaceholder: "Escreva: DELETE EVERYTHING",
wipeBtn: "Apagar o registo", wipeDone: "Registo apagado",
auditTitle: "Registo de alterações",
auditHelp: "Alterações administrativas, mais recentes primeiro. Com duas pessoas a editar, «quem mudou a 12-B e quando» é uma pergunta que acaba por surgir.",
aWhen: "Quando", aWhat: "O quê", aDetail: "Detalhe",
imported: "Importado",
},
en: {
_months: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
brand: "Key Register", appTitle: "Key cabinets", admin: "Admin",
loginSub: "Enter the shared passcode.",
loginPlaceholder: "Passcode", loginBtn: "Unlock",
loginWrong: "Wrong passcode.", loginMany: "Too many attempts. Wait a minute.",
loginShow: "Show passcode", loginHide: "Hide passcode",
loginFoot: "Restricted to the church private network.",
loading: "Loading…", noServer: "Can't reach the server.", back: "Back to the register",
tabBoard: "Board", tabDoors: "Doors", tabPeople: "People", tabLog: "Log", tabJournal: "Journal",
cabinetsN: (n) => `${n} store${n === 1 ? "" : "s"}`,
keysIn: (a, b) => `${a} of ${b} keys in`,
issuedN: "issued", overdueN: "overdue", reserveN: "reserve",
searchPerson: "Search for a person…",
/* access request */
reqTitle: "Access request",
reqIntro: "Use this form to request access to an area of the church. The keyholder reviews every request — no key is issued here.",
reqName: "Full name", reqDept: "Department or group",
reqContact: "Phone or email", reqAreas: "Areas you need access to",
reqDetail: "What for, and for how long",
reqDetailPh: "e.g. choir rehearsals on Wednesdays, from September.",
reqPin: "Church code", reqPinHelp: "Ask the keyholder for the code.",
reqSend: "Send request", reqSending: "Sending…",
reqOk: "Request sent.",
reqOkMore: "The keyholder will review it. You can close this tab.",
reqAnother: "Send another request",
reqNeedName: "Please enter your name.", reqNeedArea: "Choose at least one area.",
reqWrongPin: "Wrong code.", reqTooMany: "Too many requests. Try again later.",
reqFoot: "No key is handed over through this form.",
reqHandPhone: "Hand the phone to someone to fill in",
reqKioskDone: "You can hand the phone back.",
reqKioskNext: "New request",
reqNoPinStaff: "No code needed — this request is being made from inside the app.",
reqFormPublic: "Form is public, protected by a code.",
reqFormOpen: "FORM IS PUBLIC WITH NO CODE — anyone who finds the address can submit.",
reqFormPrivate: "Form is only reachable by people signed in to the app.",
reqFormOpenIt: "Open the form",
reqInbox: "Access requests", reqPending: (n) => `${n} to handle`,
reqNone: "No pending requests.",
reqOn: (d) => `Received ${d}`, reqMarkDone: "Mark as handled",
reqNotePh: "What was decided?", reqHandled: "Handled",
reqHandledOn: (d) => `Handled ${d}`,
/* reports */
reports: "Reports", print: "Print", printPdf: "Print / PDF",
repOut: "Keys currently issued", repOverdue: "Overdue keys",
repPerson: "Receipt per person", repInventory: "Full inventory",
repAudit: "Audit sheet",
repGenerated: (d, h) => `Generated ${d} at ${h}`,
repNothing: "Nothing to show.",
repChoosePerson: "Choose a person",
stKeysOut: "issued", stPeople: "people", stOverdue: "overdue",
stTotal: "total", stInCab: "in cabinet", stReserve: "reserve",
cTag: "Tag", cDoor: "Door", cBuilding: "Building", cCabinet: "Cabinet",
cPerson: "Person", cDept: "Department", cOut: "Issued", cDue: "Due back",
cDays: "Days", cStatus: "Status", cHook: "Hook", cCheck: "Check",
repSignedOn: (d) => `Signed ${d}`, repNoSig: "No signature on record",
repReceiptTitle: "Key receipt",
repReceiptIntro: (n) => `I confirm that I hold the ${n === 1 ? "key" : n + " keys"} listed below, and undertake to return ${n === 1 ? "it" : "them"} on request.`,
repHolder: "Holder", repResponsible: "Keyholder in charge", repDate: "Date",
repAuditIntro: "Confirm each key present in the cabinet and tick the box. Issued keys are marked.",
repAuditBy: "Audit carried out by", repNotes: "Notes",
repAllCabinets: "All cabinets",
allTile: "All",
statuses: { in: "in cabinet", out: "issued", reserve: "reserve", lost: "lost", retired: "retired" },
fEverything: "Everything", fKeys: "Keys only", fTags: "Alarm tags",
fOut: "Currently out", fLate: "Overdue",
lgIn: "In cabinet", lgOut: "Issued", lgRes: "Reserve", lgLate: "Overdue", lgTag: "Alarm tag",
outCount: (n) => `${n} out`,
boardNote: "One peg per piece of metal, in hook order.
For \u201cwho can open this door\u201d, use Doors.",
noMatch: "Nothing matches that filter.", noKeysYet: "No keys yet. Add them in",
onHook: "On its hook", reserveCopy: "Reserve copy", lost: "Lost", retired: "Retired",
allIn: "all in",
copies: (n) => `${n} cop${n === 1 ? "y" : "ies"}`,
doorsN: (n) => `${n} door${n === 1 ? "" : "s"}`,
searchPlaceholder: "Door, key number, person, or note",
nothingFound: (q) => `Nothing found for \u201c${q}\u201d.`,
doorsNote: "One row per door. Dots are its copies — filled is in a cabinet,
hollow is out with someone, grey is reserve.",
tagsSection: "Alarm tags",
holdingNow: "Holding something now", nothingOut: "Nothing out", inactive: "inactive",
keysWord: (n) => `${n} key${n === 1 ? "" : "s"}`,
tagsWord: (n) => `${n} tag${n === 1 ? "" : "s"}`,
noPeopleYet: "No people yet. Add them in",
logTitle: "Every handover, newest first", toWord: "to", backFrom: "back from",
signedMark: "signed", noHandovers: "No handovers recorded yet.",
logNote: "Rows are only ever added, never edited.", dirOut: "out", dirIn: "in",
newEntry: "New entry", journalPlaceholder: "Audit done, lock changed, reserve seal broken…",
addJournal: "Add to journal", journalTitle: "Cabinet journal",
journalEmpty: "Nothing recorded yet.",
journalNote: "For everything that belongs to a cabinet rather than to one key.",
withSince: (w, d) => `With ${w} since ${d}.`,
dueWas: (d, n) => `
Due back ${d} — ${n} days ago.`,
issuedToOn: (w, d) => `Issued to ${w} on ${d}`,
dueBack: (d) => `
Due back ${d}.`,
noDueDate: "
No return date — held permanently.",
reserveLine: "Reserve copy, off site. Issuing it is logged in the journal automatically.",
lostLine: "Reported lost. Consider re-keying this door.",
retiredLine: "Retired. Kept for its history only.",
inCabinet: (c) => `In the ${c}`, atHook: (h) => `, hook ${h}`,
fType: "Type", vKey: "Door key", vTag: "NFC alarm tag",
fKeptIn: "Kept in", fHook: "Hook", fLockGroup: "Lock group", fChipUid: "Chip UID",
fMaster: "Master key", yes: "Yes", no: "No",
fCopiesLiveIn: "Copies live in", fHoldingNow: "Holding now",
fReturnedPast: "Returned in the past", fPhone: "Phone",
seeAllCopies: (n) => `See all ${n} copies`,
noteOnHandover: "Note on this handover", noteOnKey: "Note on this key — permanent",
notePlaceholder: "Anything the next person should know.", saveNote: "Save note",
noteOnDoor: "Note on this door",
issueTo: "Issue to someone", breakSeal: "Break seal and issue", takeBack: "Take it back",
close: "Close", cancel: "Cancel", history: "History", neverIssued: "Never been issued.",
returnedOn: (d) => `returned ${d}`, stillHolding: "still holding it",
stClosed: "closed", stOpen: "open", stLate: "late",
currentlyHolds: "Currently holds", holdsNothing: "Holds nothing right now.",
returnedSection: "Returned", heldFromTo: (a, b) => `held ${a} → ${b}`,
itemsN: (n) => `${n} item${n === 1 ? "" : "s"}`,
fromCabinets: (s) => `from ${s}`,
issueTitle: (t) => `Issue ${t}`, fromThe: (c) => `from the ${c}`,
whoTakes: "Who is taking it?",
reserveWarn: "This is a last-resort copy. Issuing it leaves the door without a reserve.",
noActivePeople: "No active people. Add them in",
handover: "Handover", backBy: "Back by",
dNone: "No date — permanent", d7: "One week", d30: "One month", d90: "Three months",
whyNote: "Why — note on this handover",
whyPlaceholder: "Christmas concert, boiler service, new caretaker…",
signature: "Signature", signHint: "Hand them the phone to sign",
clearSig: "Clear", confirmHandover: "Confirm handover",
everyCopy: "Every copy of this key", sameLock: "Same lock group — these open too",
doorSummary: (total, out, inC, res, lst) => {
const parts = [];
if (out) parts.push(`${out} with people`);
parts.push(`${inC} in cabinets`);
if (res) parts.push(`${res} reserve`);
if (lst) parts.push(`${lst} lost`);
return `${total} cop${total === 1 ? "y" : "ies"} exist. ${parts.join(", ")}.`;
},
tHandedOver: "Handed over", tBackIn: "Back in the cabinet", tNoteSaved: "Note saved",
tJournalAdded: "Journal entry added", tSaved: "Saved", tRemoved: "Removed",
errors: {},
aItems: "Keys & tags", aDoors: "Doors", aPeople: "People", aCabinets: "Cabinets",
aData: "Import / backup", aAudit: "Change log",
aItemsHelp: (n) => `One row per physical piece of metal. ${n} in total.`,
aFilter: "Filter by tag, door, or note",
aAddItem: "+ Add a key or tag", aAddDoor: "+ Add a door",
aAddPerson: "+ Add a person", aAddCabinet: "+ Add a cabinet",
aTag: "Tag", aDoor: "Door / zone", aCabinet: "Cabinet", aHook: "Hook",
aStatus: "Status", aEdit: "Edit", aNothing: "Nothing yet.",
aDoorsHelp: (n) => `The physical doors. Keys point at these. ${n} in total.`,
aNo: "No.", aLock: "Lock", aCopies: "Copies",
aPeopleHelp: "Key holders. They never log in — this is just who can be handed a key.",
aName: "Name", aDept: "Department", aHolding: "Holding",
aCabinetsHelp: "Where keys physically live. A cabinet is not a building — the office can hold church masters.",
aOffSite: "off site", aItemsCount: (n) => `${n} items`,
fNum: "Number", fBuilding: "Building", fDoorName: "Door name", fNote: "Note",
fShortCode: "Short code", fShortName: "Short name", fFullName: "Full name",
fWhereIs: "Where it is, and what opens it", fSort: "Board order",
fSortHelp: "Lower numbers come first. Give the reserve store a high number to keep it last.",
fOffSiteLabel: "Off site — reserve store",
fZoneKeys: "Door (keys)", fZoneTags: "Alarm zone (tags)",
fNoneTag: "— none / alarm tag —", fNone: "— none —",
fIsMaster: "This is a master key", fPermNote: "Permanent note",
fFunction: "Function", fEmail: "Email", fActiveLabel: "Active — can be handed keys",
save: "Save", deleteRetire: "Delete or retire this key", deleteDoor: "Delete this door",
deactivate: "Deactivate", deleteCabinet: "Delete this cabinet",
retireNote: "If it has handover history it is retired, never deleted — the history has to survive.",
peopleNote: "People are never deleted. They appear in old handovers, and deleting them would make that history anonymous.",
lockGroupNote: "Lock group ties doors that share a key profile, so you can see what else a lost key might open.",
offSiteNote: "Keys returned to an off-site store go back to reserve rather than in cabinet, so they stay out of everyday circulation.",
holdsWarn: (n, tags) => `Currently holds ${n} item${n === 1 ? "" : "s"}: ${tags}. Take those back before deactivating.`,
impWhat: "What do you want to import?",
impItems: "Keys & tags", impDoors: "Doors", impPeople: "People", impCabinets: "Cabinets",
impResult2: (c, u) => `Created ${c}, updated ${u}.`,
impTitle: "Import from a spreadsheet",
impHelp: "Paste CSV. Required columns: tag, door, building. Optional: lock_group, cabinet, hook, master, status, kind, notes. Doors and cabinets that don't exist are created.",
impBtn: "Import",
impResult: (i, d, c) => `Imported ${i} items, created ${d} doors and ${c} cabinets.`,
impSkipped: (n, list) => `
Skipped ${n}: ${list}`,
impNothing: "Nothing to import",
expTitle: "Export",
expHelp: "The whole register as a spreadsheet. Useful for a printed copy in the cabinet, and proof you are never locked into this app.",
expBtn: "Download CSV",
bkTitle: "Backup",
bkHelp: "One SQLite file, safe to download while the app is running. Keep a copy somewhere that is not this machine.",
bkBtn: "Download database",
wipeTitle: "Clear everything",
wipeWarn: "Deletes every key, door, person, handover and journal entry. Cabinets are kept. There is no undo — download a backup first.",
wipePlaceholder: "Type: DELETE EVERYTHING",
wipeBtn: "Clear the register", wipeDone: "Register cleared",
auditTitle: "Change log",
auditHelp: "Admin changes, newest first. With two of you editing, \u201cwho moved 12-B and when\u201d is a question that comes up.",
aWhen: "When", aWhat: "What", aDetail: "Detail",
imported: "Imported",
},
};
/* Chosen by the server from UI_LANG; falls back to Portuguese.
`var`, not `let`, so the binding is a real global shared with app.js and
admin.js rather than scoped to this script. */
var T = I18N.pt;
function setLang(code) {
T = I18N[code] || I18N.pt;
document.documentElement.lang = I18N[code] ? code : "pt";
}
/* Server errors arrive in English; show a translated version when we have one. */
function errMsg(m) { return (T.errors && T.errors[m]) || m; }