Pedro Gomes 35e7027881 localization support
O que mudou
Infra (por app):

i18n/locales.ts — lista de locales (pt, en), default pt, labels para o seletor
i18n/request.ts — lê o cookie NEXT_LOCALE, carrega as mensagens
messages/pt.json + messages/en.json — todas as strings extraídas
next.config.ts — envolvido com withNextIntl (operator-pwa: withPWA(withNextIntl(...)))
app/layout.tsx — <html lang={locale}> dinâmico, NextIntlClientProvider
app/language-switcher.tsx — seletor PT | EN (cookie + router.refresh())
23 ficheiros de UI atualizados — todos os textos visíveis agora usam t('...') ou getTranslations.

Datas no relatório passaram de toLocaleString('pt-PT') fixo para useFormatter() do next-intl — localizam-se automaticamente.

Plurais em ICU no sync-chip: {count, plural, one {# pedido...} other {# pedidos...}}.

Resultado dos testes:

pnpm test:e2e — 3/3 ✓
pnpm test:e2e:auth — 4/4 ✓
tsc --noEmit em ambas as apps — limpo ✓
Para adicionar uma língua futura: criar messages/<locale>.json + adicionar o locale a i18n/locales.ts em cada app. O seletor aparece automaticamente.
2026-05-30 16:46:07 +01:00

114 lines
3.5 KiB
JSON

{
"metadata": {
"title": "FieldOps — Manutenção",
"description": "Backoffice de manutenção industrial."
},
"common": {
"enter": "Entrar",
"entering": "A entrar…",
"cancel": "Cancelar",
"confirm": "Confirmar",
"loading": "A carregar…",
"allAreas": "Todas",
"status": {
"open": "Aberto",
"claimed": "Em curso",
"resolved": "Resolvido"
},
"timeAgo": {
"now": "agora",
"minutesAgo": "há {mins}m",
"hoursAgo": "há {hours}h",
"daysAgo": "há {days}d"
}
},
"auth": {
"emailLabel": "Email",
"emailPlaceholder": "admin@demo.local",
"passwordLabel": "Password",
"invalidCredentials": "Email ou password incorretos. Tente novamente.",
"unexpectedError": "Erro inesperado. Tente novamente.",
"title": "FieldOps",
"subtitle": "Acesso à consola de manutenção"
},
"maintenance": {
"queueTitle": "Fila de manutenção",
"openRequestsTitle": "{count} pedidos abertos",
"reportLink": "Relatório de turno",
"soundOn": "🔔 Som on",
"soundOff": "🔕 Som off",
"filterStatus": "Estado:",
"filterArea": "Área:",
"updatesEvery": "Atualiza a cada 5s",
"emptyQueue": "Nenhum pedido com os filtros actuais.",
"photo": "Foto",
"reportedBy": "Reportado por {email} · {time}",
"claimedBy": "Aceite por {email} · {time}",
"resolvedBy": "Resolvido por {email} · {time}",
"accept": "Aceitar",
"markResolved": "Marcar resolvido",
"resolveDialogTitle": "Marcar como resolvido",
"resolveNoteLabel": "Nota de resolução (opcional)",
"resolveNotePlaceholder": "Descreve o que foi feito…",
"documentTitleWithCount": "({count}) FieldOps — Manutenção",
"documentTitle": "FieldOps — Manutenção"
},
"report": {
"pageTitle": "FieldOps — Relatório de turno",
"title": "Relatório de turno",
"print": "Imprimir",
"printHeader": "FieldOps — Relatório de manutenção",
"backToQueue": "Fila",
"today": "Hoje",
"custom": "Personalizado",
"customUntil": "até",
"customApply": "Aplicar",
"loading": "A carregar…",
"emptyWindow": "Sem pedidos nesta janela.",
"windowLabel": {
"today": "Hoje — {range}",
"manha": "Turno da Manhã — {range}",
"tarde": "Turno da Tarde — {range}",
"noite": "Turno da Noite — {range}",
"custom": "Personalizado — {range}"
},
"shiftButton": {
"manha": "Manhã",
"tarde": "Tarde",
"noite": "Noite"
},
"sections": {
"summary": "Resumo",
"byWorkstation": "Por posto",
"byArea": "Por área",
"stillOpen": "Em aberto à hora do relatório"
},
"metrics": {
"created": "Pedidos",
"resolved": "Resolvidos",
"open": "Em aberto",
"responseAvg": "Resposta média",
"resolutionAvg": "Resolução média",
"responseMax": "Pior resposta",
"openSub": "{open} aberto · {claimed} em curso",
"requestsSub": "{count, plural, one {sobre # pedido} other {sobre # pedidos}}",
"noData": "sem dados"
},
"table": {
"code": "Código",
"name": "Nome",
"area": "Área",
"requests": "Pedidos"
},
"stillOpenReportedBy": "Reportado por {email} · {date}",
"allClear": "Nada em aberto neste turno. ✓",
"duration": {
"lessThan1Min": "< 1 min",
"minutes": "{n} min",
"hours": "{h} h",
"hoursMinutes": "{h} h {m} min",
"dash": "—"
}
}
}