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.
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"metadata": {
|
||||
"title": "FieldOps — Maintenance",
|
||||
"description": "Industrial maintenance backoffice."
|
||||
},
|
||||
"common": {
|
||||
"enter": "Sign in",
|
||||
"entering": "Signing in…",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Confirm",
|
||||
"loading": "Loading…",
|
||||
"allAreas": "All",
|
||||
"status": {
|
||||
"open": "Open",
|
||||
"claimed": "In progress",
|
||||
"resolved": "Resolved"
|
||||
},
|
||||
"timeAgo": {
|
||||
"now": "just now",
|
||||
"minutesAgo": "{mins}m ago",
|
||||
"hoursAgo": "{hours}h ago",
|
||||
"daysAgo": "{days}d ago"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"emailLabel": "Email",
|
||||
"emailPlaceholder": "admin@demo.local",
|
||||
"passwordLabel": "Password",
|
||||
"invalidCredentials": "Incorrect email or password. Please try again.",
|
||||
"unexpectedError": "Unexpected error. Please try again.",
|
||||
"title": "FieldOps",
|
||||
"subtitle": "Maintenance console access"
|
||||
},
|
||||
"maintenance": {
|
||||
"queueTitle": "Maintenance queue",
|
||||
"openRequestsTitle": "{count} open requests",
|
||||
"reportLink": "Shift report",
|
||||
"soundOn": "🔔 Sound on",
|
||||
"soundOff": "🔕 Sound off",
|
||||
"filterStatus": "Status:",
|
||||
"filterArea": "Area:",
|
||||
"updatesEvery": "Updates every 5s",
|
||||
"emptyQueue": "No requests match the current filters.",
|
||||
"photo": "Photo",
|
||||
"reportedBy": "Reported by {email} · {time}",
|
||||
"claimedBy": "Accepted by {email} · {time}",
|
||||
"resolvedBy": "Resolved by {email} · {time}",
|
||||
"accept": "Accept",
|
||||
"markResolved": "Mark resolved",
|
||||
"resolveDialogTitle": "Mark as resolved",
|
||||
"resolveNoteLabel": "Resolution note (optional)",
|
||||
"resolveNotePlaceholder": "Describe what was done…",
|
||||
"documentTitleWithCount": "({count}) FieldOps — Maintenance",
|
||||
"documentTitle": "FieldOps — Maintenance"
|
||||
},
|
||||
"report": {
|
||||
"pageTitle": "FieldOps — Shift report",
|
||||
"title": "Shift report",
|
||||
"print": "Print",
|
||||
"printHeader": "FieldOps — Maintenance report",
|
||||
"backToQueue": "Queue",
|
||||
"today": "Today",
|
||||
"custom": "Custom",
|
||||
"customUntil": "to",
|
||||
"customApply": "Apply",
|
||||
"loading": "Loading…",
|
||||
"emptyWindow": "No requests in this window.",
|
||||
"windowLabel": {
|
||||
"today": "Today — {range}",
|
||||
"manha": "Morning Shift — {range}",
|
||||
"tarde": "Afternoon Shift — {range}",
|
||||
"noite": "Night Shift — {range}",
|
||||
"custom": "Custom — {range}"
|
||||
},
|
||||
"shiftButton": {
|
||||
"manha": "Morning",
|
||||
"tarde": "Afternoon",
|
||||
"noite": "Night"
|
||||
},
|
||||
"sections": {
|
||||
"summary": "Summary",
|
||||
"byWorkstation": "By workstation",
|
||||
"byArea": "By area",
|
||||
"stillOpen": "Open at report time"
|
||||
},
|
||||
"metrics": {
|
||||
"created": "Requests",
|
||||
"resolved": "Resolved",
|
||||
"open": "Open",
|
||||
"responseAvg": "Avg response",
|
||||
"resolutionAvg": "Avg resolution",
|
||||
"responseMax": "Worst response",
|
||||
"openSub": "{open} open · {claimed} in progress",
|
||||
"requestsSub": "{count, plural, one {over # request} other {over # requests}}",
|
||||
"noData": "no data"
|
||||
},
|
||||
"table": {
|
||||
"code": "Code",
|
||||
"name": "Name",
|
||||
"area": "Area",
|
||||
"requests": "Requests"
|
||||
},
|
||||
"stillOpenReportedBy": "Reported by {email} · {date}",
|
||||
"allClear": "Nothing open in this shift. ✓",
|
||||
"duration": {
|
||||
"lessThan1Min": "< 1 min",
|
||||
"minutes": "{n} min",
|
||||
"hours": "{h} h",
|
||||
"hoursMinutes": "{h} h {m} min",
|
||||
"dash": "—"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"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": "—"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user