diagcalc 3.2.4 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -8
- package/bin/diagcalc.js +142 -41
- package/docs/PROVENANCE.md +17 -0
- package/index.html +430 -118
- package/lib/README.md +98 -0
- package/lib/diagcalc-case.js +148 -0
- package/lib/diagcalc-core.js +554 -89
- package/lib/diagcalc-datasets.js +140 -1
- package/lib/diagcalc-geometry.js +27 -0
- package/lib/diagcalc-i18n.js +584 -0
- package/lib/diagcalc-meta.js +9 -0
- package/lib/diagcalc-presentation.js +278 -0
- package/lib/diagcalc-storage.js +45 -0
- package/lib/diagcalc-types.d.ts +59 -0
- package/package.json +18 -4
- package/script.js +1269 -277
- package/styles.css +2231 -325
- package/tui/index.js +75 -272
- package/tui/input.js +24 -0
- package/tui/presentation.js +168 -0
- package/tui/report.js +130 -0
- package/web/charts.js +339 -0
- package/web/results.js +235 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === "object" && module.exports) {
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
root.DiagcalcI18n = factory();
|
|
7
|
+
}(/** @type {any} */ (typeof globalThis !== "undefined" ? globalThis : this), () => {
|
|
8
|
+
// Browser copy uses explicit keys; calculation modules remain language-neutral.
|
|
9
|
+
const translations = {
|
|
10
|
+
en: {
|
|
11
|
+
"page.title": "DIAG-CALC",
|
|
12
|
+
"intro.tag": "Educational tool",
|
|
13
|
+
"intro.lead": "Interactive calculator for exploring diagnostic test performance measures and applying Bayesian reasoning in clinical practice.",
|
|
14
|
+
"intro.highlight1.h": "Key measures",
|
|
15
|
+
"intro.highlight1.p": "Sensitivity, specificity, PPV, NPV, and likelihood ratios with 95% CI.",
|
|
16
|
+
"intro.highlight2.h": "Post-test probability",
|
|
17
|
+
"intro.highlight2.p": "Convert pre-test probability into positive and negative post-test probability.",
|
|
18
|
+
"intro.highlight3.h": "Guided interpretation",
|
|
19
|
+
"intro.highlight3.p": "Results explained to support self-directed learning.",
|
|
20
|
+
"library.kicker": "Library",
|
|
21
|
+
"library.h": "Load a study scenario",
|
|
22
|
+
"library.help": "Pick a preset to load values, or fill in the matrix manually below.",
|
|
23
|
+
"editor.kicker": "Editor",
|
|
24
|
+
"editor.h": "Build the case",
|
|
25
|
+
"editor.help": "Pick a preset above or enter the 2×2 below. Non-negative integers; pre-test probability between 0% and 100%.",
|
|
26
|
+
"theme.toggle.label": "Toggle dark mode",
|
|
27
|
+
"lang.label": "Language",
|
|
28
|
+
"app.case.empty": "No case yet",
|
|
29
|
+
"app.case.prefix": "Case:",
|
|
30
|
+
"app.case.adhoc": "Ad-hoc case",
|
|
31
|
+
"about.h": "About DIAGCALC",
|
|
32
|
+
"form.kicker": "Setup",
|
|
33
|
+
"form.h": "Define the case",
|
|
34
|
+
"form.scenario": "Scenario",
|
|
35
|
+
"form.scenario.empty": "— Select —",
|
|
36
|
+
"form.scenario.group.generic": "Generic scenarios",
|
|
37
|
+
"form.scenario.group.literature": "Illustrative medical scenarios",
|
|
38
|
+
"form.scenario.help": "Pick a preset to load its values, or fill in the matrix manually below.",
|
|
39
|
+
"form.matrix.legend": "Confusion matrix",
|
|
40
|
+
"form.matrix.dpos": "Disease +",
|
|
41
|
+
"form.matrix.dneg": "Disease −",
|
|
42
|
+
"form.matrix.tpos": "Test +",
|
|
43
|
+
"form.matrix.tneg": "Test −",
|
|
44
|
+
"form.pre.label": "Pre-test probability (%)",
|
|
45
|
+
"form.pre.help": "Estimate the disease prevalence in the study population or the clinical probability before the test.",
|
|
46
|
+
"form.advanced.summary": "Advanced settings",
|
|
47
|
+
"form.advanced.continuity": "Continuity correction for LR / DOR CIs",
|
|
48
|
+
"form.advanced.continuity.auto": "Auto — apply +0.5 only when a 2×2 cell is zero",
|
|
49
|
+
"form.advanced.continuity.always": "Always — apply +0.5 to every cell",
|
|
50
|
+
"form.advanced.continuity.never": "Never — leave undefined intervals unavailable",
|
|
51
|
+
"form.advanced.continuity.help": "When any cell of the confusion matrix is zero the log-normal CI for LR+ / LR− / DOR is undefined. The default adds 0.5 to every cell (Cox 1970) when this happens so the maths still produces a (wide) interval.",
|
|
52
|
+
"form.calculate": "Calculate results",
|
|
53
|
+
"form.clear": "Clear fields",
|
|
54
|
+
"results.kicker": "Output",
|
|
55
|
+
"results.h": "Results",
|
|
56
|
+
"results.help": "Values are shown with 95% confidence intervals where applicable.",
|
|
57
|
+
"results.print": "Print report",
|
|
58
|
+
"panel.tag.tool": "Tool",
|
|
59
|
+
"panel.tag.ref": "Reference",
|
|
60
|
+
"fagan.h": "Fagan Nomogram",
|
|
61
|
+
"fagan.help": "Calibrated logarithmic axes: pre-test odds × LR = post-test odds. Off-scale or undefined values are shown in the summary.",
|
|
62
|
+
"fagan.pre": "Pre-test",
|
|
63
|
+
"fagan.lrpos": "LR+",
|
|
64
|
+
"fagan.lrneg": "LR−",
|
|
65
|
+
"fagan.postpos": "Post-test (+)",
|
|
66
|
+
"fagan.postneg": "Post-test (−)",
|
|
67
|
+
"feedback.calc.ok": "Calculation complete. Explore the interpretation guide below.",
|
|
68
|
+
"feedback.cleared": "Fields cleared.",
|
|
69
|
+
"feedback.scenario.loaded": "Scenario loaded. Adjust the values as needed and recalculate.",
|
|
70
|
+
"feedback.history.reloaded": "Reloaded \"{label}\" from history.",
|
|
71
|
+
"thresholdDemo.summary": "Threshold explorer",
|
|
72
|
+
"thresholdDemo.summary.sub": "drag the slicer to see how TP, FP, FN, TN and all metrics shift in real time",
|
|
73
|
+
"thresholdDemo.help": "An illustrative binormal model produces scores for 200 individuals, with group sizes adapted to the current case prevalence. Move the threshold slider to see how the confusion matrix and all performance metrics change in real time. There is no perfect threshold — only trade-offs.",
|
|
74
|
+
"thresholdDemo.threshold": "Threshold",
|
|
75
|
+
"thresholdDemo.sens": "Sensitivity",
|
|
76
|
+
"thresholdDemo.spec": "Specificity",
|
|
77
|
+
"thresholdDemo.ppv": "PPV",
|
|
78
|
+
"thresholdDemo.npv": "NPV",
|
|
79
|
+
"thresholdDemo.disease": "Disease +",
|
|
80
|
+
"thresholdDemo.healthy": "Disease −",
|
|
81
|
+
"thresholdDemo.testpos": "Test +",
|
|
82
|
+
"thresholdDemo.testneg": "Test −",
|
|
83
|
+
"thresholdDemo.tp": "True positives",
|
|
84
|
+
"thresholdDemo.fp": "False positives",
|
|
85
|
+
"thresholdDemo.fn": "False negatives",
|
|
86
|
+
"thresholdDemo.tn": "True negatives",
|
|
87
|
+
"thresholdDemo.note.low": "Threshold very low: catches nearly all diseased but flags many healthy. Useful for screening.",
|
|
88
|
+
"thresholdDemo.note.high": "Threshold very high: confirms nearly only diseased but misses many. Useful when treatment is risky.",
|
|
89
|
+
"thresholdDemo.note.balanced": "Reasonable balance. The final choice depends on the clinical cost of missing a case vs. flagging a healthy one.",
|
|
90
|
+
"thresholdDemo.note.sens": "Tilted toward sensitivity: few diseased escape ({sens}), but more healthy are flagged (specificity {spec}).",
|
|
91
|
+
"thresholdDemo.note.spec": "Tilted toward specificity: few healthy flagged ({spec}), but more diseased escape (sensitivity {sens}).",
|
|
92
|
+
},
|
|
93
|
+
"pt-PT": {
|
|
94
|
+
"page.title": "DIAG-CALC",
|
|
95
|
+
"intro.tag": "Ferramenta educativa",
|
|
96
|
+
"intro.lead": "Calculadora interactiva para explorar medidas de desempenho de testes de diagnóstico e aplicar raciocínio Bayesiano na prática clínica.",
|
|
97
|
+
"intro.highlight1.h": "Medidas principais",
|
|
98
|
+
"intro.highlight1.p": "Sensibilidade, especificidade, VPP, VPN e razões de verosimilhança com IC 95%.",
|
|
99
|
+
"intro.highlight2.h": "Probabilidade pós-teste",
|
|
100
|
+
"intro.highlight2.p": "Conversão da probabilidade pré-teste em probabilidades pós-teste positiva e negativa.",
|
|
101
|
+
"intro.highlight3.h": "Interpretação guiada",
|
|
102
|
+
"intro.highlight3.p": "Resultados explicados para apoiar a aprendizagem autónoma.",
|
|
103
|
+
"library.kicker": "Biblioteca",
|
|
104
|
+
"library.h": "Carregar um cenário de estudo",
|
|
105
|
+
"library.help": "Escolha um cenário para carregar valores, ou preencha a matriz manualmente em baixo.",
|
|
106
|
+
"editor.kicker": "Editor",
|
|
107
|
+
"editor.h": "Construir o caso",
|
|
108
|
+
"editor.help": "Escolha um cenário acima ou preencha a matriz 2×2 abaixo. Inteiros não-negativos; probabilidade pré-teste entre 0% e 100%.",
|
|
109
|
+
"theme.toggle.label": "Alternar modo escuro",
|
|
110
|
+
"lang.label": "Idioma",
|
|
111
|
+
"app.case.empty": "Sem caso ainda",
|
|
112
|
+
"app.case.prefix": "Caso:",
|
|
113
|
+
"app.case.adhoc": "Caso ad-hoc",
|
|
114
|
+
"about.h": "Sobre o DIAGCALC",
|
|
115
|
+
"form.kicker": "Configuração",
|
|
116
|
+
"form.h": "Definir o caso",
|
|
117
|
+
"form.scenario": "Cenário",
|
|
118
|
+
"form.scenario.empty": "— Seleccionar —",
|
|
119
|
+
"form.scenario.group.generic": "Cenários genéricos",
|
|
120
|
+
"form.scenario.group.literature": "Cenários médicos ilustrativos",
|
|
121
|
+
"form.scenario.help": "Escolha um cenário para carregar valores, ou preencha a matriz manualmente em baixo.",
|
|
122
|
+
"form.matrix.legend": "Matriz de confusão",
|
|
123
|
+
"form.matrix.dpos": "Doença +",
|
|
124
|
+
"form.matrix.dneg": "Doença −",
|
|
125
|
+
"form.matrix.tpos": "Teste +",
|
|
126
|
+
"form.matrix.tneg": "Teste −",
|
|
127
|
+
"form.pre.label": "Probabilidade pré-teste (%)",
|
|
128
|
+
"form.pre.help": "Estime a prevalência da doença na população do estudo ou a probabilidade clínica antes do teste.",
|
|
129
|
+
"form.advanced.summary": "Definições avançadas",
|
|
130
|
+
"form.advanced.continuity": "Correcção de continuidade para os IC de LR / DOR",
|
|
131
|
+
"form.advanced.continuity.auto": "Automática — aplicar +0,5 apenas quando alguma célula 2×2 é zero",
|
|
132
|
+
"form.advanced.continuity.always": "Sempre — aplicar +0,5 a todas as células",
|
|
133
|
+
"form.advanced.continuity.never": "Nunca — deixar os intervalos indefinidos indisponíveis",
|
|
134
|
+
"form.advanced.continuity.help": "Quando alguma célula da matriz é zero o IC log-normal para LR+ / LR− / DOR fica indefinido. A opção automática adiciona 0,5 a todas as células (Cox 1970) nesses casos, mantendo um intervalo (largo, mas finito).",
|
|
135
|
+
"form.calculate": "Calcular resultados",
|
|
136
|
+
"form.clear": "Limpar campos",
|
|
137
|
+
"results.kicker": "Resultado",
|
|
138
|
+
"results.h": "Resultados",
|
|
139
|
+
"results.help": "Os valores são apresentados com IC 95% sempre que aplicável.",
|
|
140
|
+
"results.print": "Imprimir relatório",
|
|
141
|
+
"panel.tag.tool": "Ferramenta",
|
|
142
|
+
"panel.tag.ref": "Referência",
|
|
143
|
+
"fagan.h": "Nomograma de Fagan",
|
|
144
|
+
"fagan.help": "Eixos logarítmicos calibrados: odds pré-teste × RV = odds pós-teste. Os valores fora da escala ou indefinidos constam do resumo.",
|
|
145
|
+
"fagan.pre": "Pré-teste",
|
|
146
|
+
"fagan.lrpos": "LR+",
|
|
147
|
+
"fagan.lrneg": "LR−",
|
|
148
|
+
"fagan.postpos": "Pós-teste (+)",
|
|
149
|
+
"fagan.postneg": "Pós-teste (−)",
|
|
150
|
+
"feedback.calc.ok": "Cálculo concluído. Explore o guia de interpretação em baixo.",
|
|
151
|
+
"feedback.cleared": "Campos limpos.",
|
|
152
|
+
"feedback.scenario.loaded": "Cenário carregado. Ajuste os valores e recalcule se necessário.",
|
|
153
|
+
"feedback.history.reloaded": "Recarregado \"{label}\" do histórico.",
|
|
154
|
+
"thresholdDemo.summary": "Explorador de limiar",
|
|
155
|
+
"thresholdDemo.summary.sub": "arraste o cursor para ver como VP, FP, FN, VN e todas as métricas mudam em tempo real",
|
|
156
|
+
"thresholdDemo.help": "Um modelo binormal ilustrativo produz valores para 200 indivíduos, com grupos adaptados à prevalência do caso actual. Mova o cursor do limiar para ver como a matriz de confusão e todas as métricas de desempenho mudam em tempo real. Não há limiar perfeito — apenas compromissos.",
|
|
157
|
+
"thresholdDemo.threshold": "Limiar",
|
|
158
|
+
"thresholdDemo.sens": "Sensibilidade",
|
|
159
|
+
"thresholdDemo.spec": "Especificidade",
|
|
160
|
+
"thresholdDemo.ppv": "VPP",
|
|
161
|
+
"thresholdDemo.npv": "VPN",
|
|
162
|
+
"thresholdDemo.disease": "Doença +",
|
|
163
|
+
"thresholdDemo.healthy": "Doença −",
|
|
164
|
+
"thresholdDemo.testpos": "Teste +",
|
|
165
|
+
"thresholdDemo.testneg": "Teste −",
|
|
166
|
+
"thresholdDemo.tp": "Verdadeiros positivos",
|
|
167
|
+
"thresholdDemo.fp": "Falsos positivos",
|
|
168
|
+
"thresholdDemo.fn": "Falsos negativos",
|
|
169
|
+
"thresholdDemo.tn": "Verdadeiros negativos",
|
|
170
|
+
"thresholdDemo.note.low": "Limiar muito baixo: apanha quase todos os doentes mas sinaliza muitos saudáveis. Útil em rastreio.",
|
|
171
|
+
"thresholdDemo.note.high": "Limiar muito alto: confirma quase só doentes mas perde muitos. Útil quando o tratamento é arriscado.",
|
|
172
|
+
"thresholdDemo.note.balanced": "Equilíbrio razoável. A escolha final depende do custo clínico de falhar um doente vs. sinalizar um saudável.",
|
|
173
|
+
"thresholdDemo.note.sens": "Inclinado para a sensibilidade: poucos doentes escapam ({sens}), mas mais saudáveis ficam sinalizados (especificidade {spec}).",
|
|
174
|
+
"thresholdDemo.note.spec": "Inclinado para a especificidade: poucos saudáveis sinalizados ({spec}), mas mais doentes escapam (sensibilidade {sens}).",
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const browserCopy = {
|
|
179
|
+
"feedback.load.failed": ["Calculator failed to load. Refresh the page or check the network tab for missing scripts.", "Não foi possível carregar a calculadora. Actualize a página ou verifique os ficheiros em falta no separador de rede."],
|
|
180
|
+
"dataset.screening.name": ["Screening programme (low prevalence)", "Programa de rastreio (baixa prevalência)"],
|
|
181
|
+
"dataset.screening.description": ["Generic population screening scenario with low prevalence.", "Cenário genérico de rastreio populacional com baixa prevalência."],
|
|
182
|
+
"dataset.caseControl.name": ["Case-control study", "Estudo caso-controlo"],
|
|
183
|
+
"dataset.caseControl.description": ["Typical case-control design with artificially balanced prevalence.", "Desenho típico de caso-controlo com prevalência artificialmente equilibrada."],
|
|
184
|
+
"dataset.clinic.name": ["Specialist clinic", "Consulta de especialidade"],
|
|
185
|
+
"dataset.clinic.description": ["Specialist clinic setting with intermediate prevalence.", "Consulta de especialidade com prevalência intermédia."],
|
|
186
|
+
"dataset.ddimer.name": ["D-dimer for pulmonary embolism", "D-dímero na embolia pulmonar"],
|
|
187
|
+
"dataset.ddimer.description": ["Low Wells score + D-dimer to rule out pulmonary embolism in the emergency department.", "Pontuação de Wells baixa e D-dímero para excluir embolia pulmonar no serviço de urgência."],
|
|
188
|
+
"chain.title": ["Chain a second test", "Encadear um segundo teste"],
|
|
189
|
+
"chain.help": ["Real diagnostic workflows often chain tests (e.g. D-dimer → CT-PA, rapid antigen → PCR, screen → confirm). Choose which result of the first test you are following up on, then enter the second test's confusion matrix. The second test's pre-test probability is set automatically.", "Os percursos de diagnóstico combinam frequentemente testes (por exemplo, D-dímero → angio-TC, antigénio → PCR, rastreio → confirmação). Escolha o resultado do primeiro teste e preencha a matriz do segundo. A probabilidade pré-teste do segundo teste é definida automaticamente. Esta combinação pressupõe independência condicional dos testes."],
|
|
190
|
+
"chain.follow": ["Follow up on test 1 result", "Resultado do teste 1 a seguir"],
|
|
191
|
+
"chain.positive": ["Positive (use post-test (+) as pre-test for test 2)", "Positivo (usar a probabilidade pós-teste (+) no teste 2)"],
|
|
192
|
+
"chain.negative": ["Negative (use post-test (−) as pre-test for test 2)", "Negativo (usar a probabilidade pós-teste (−) no teste 2)"],
|
|
193
|
+
"chain.matrix": ["Test 2 — confusion matrix", "Teste 2 — matriz de confusão"],
|
|
194
|
+
"chain.calculate": ["Calculate test 2 result", "Calcular o resultado do teste 2"],
|
|
195
|
+
"chain.clear": ["Clear test 2", "Limpar o teste 2"],
|
|
196
|
+
"threshold.title": ["Decision thresholds", "Limiares de decisão"],
|
|
197
|
+
"threshold.help1": ["Pauker–Kassirer (1980) framework. Set the treatment threshold (the post-test probability at which you would act). Below the", "Modelo de Pauker–Kassirer (1980). Defina o limiar de tratamento, a probabilidade pós-teste a partir da qual interviria. Abaixo do limiar de"],
|
|
198
|
+
"threshold.help2": ["testing", "teste"],
|
|
199
|
+
"threshold.help3": ["threshold even a positive result keeps you below the action level — testing changes nothing. Above the", "mesmo um resultado positivo fica abaixo do nível de intervenção. Acima do limiar de"],
|
|
200
|
+
"threshold.help4": ["test-treatment", "teste-tratamento"],
|
|
201
|
+
"threshold.help5": ["threshold even a negative result keeps you above the action level — just treat. The \"test is useful\" zone is the interval between them.", "mesmo um resultado negativo fica acima desse nível. O teste é útil no intervalo entre os dois limiares."],
|
|
202
|
+
"threshold.treatment": ["Treatment threshold", "Limiar de tratamento"],
|
|
203
|
+
"threshold.no": ["Don't test", "Não testar"],
|
|
204
|
+
"threshold.useful": ["Test useful", "Teste útil"],
|
|
205
|
+
"threshold.treat": ["Treat without testing", "Tratar sem testar"],
|
|
206
|
+
"threshold.pt": ["Pt (treatment threshold)", "Pt (limiar de tratamento)"],
|
|
207
|
+
"threshold.current": ["Current pre-test", "Probabilidade pré-teste actual"],
|
|
208
|
+
"roc.title": ["Build an ROC curve", "Curva ROC"],
|
|
209
|
+
"roc.help": ["For a continuous test (e.g. troponin, D-dimer, score), each candidate cutoff defines its own 2×2 table. Add one row per cutoff and the plot draws sensitivity vs (1 − specificity), the trapezoidal AUC, and highlights the cutoff with the largest Youden's J (sens + spec − 1).", "Num teste contínuo (por exemplo, troponina, D-dímero ou pontuação), cada ponto de corte define uma matriz 2×2. Adicione uma linha por ponto de corte para representar a sensibilidade em função de 1 − especificidade, calcular a área sob a curva (AUC) pelo método dos trapézios e identificar o maior índice J de Youden (sensibilidade + especificidade − 1)."],
|
|
210
|
+
"roc.direction": ["Positive result direction", "Sentido do resultado positivo"],
|
|
211
|
+
"roc.higher": ["Higher scores are positive", "Valores mais altos são positivos"],
|
|
212
|
+
"roc.lower": ["Lower scores are positive", "Valores mais baixos são positivos"],
|
|
213
|
+
"roc.cutoff": ["Cutoff", "Ponto de corte"],
|
|
214
|
+
"roc.add": ["+ Add cutoff row", "Adicionar ponto de corte"],
|
|
215
|
+
"roc.fill": ["+ Fill synthetic cutoffs", "Gerar pontos de corte simulados"],
|
|
216
|
+
"roc.reset": ["Reset rows", "Repor linhas"],
|
|
217
|
+
"history.title": ["History", "Histórico"],
|
|
218
|
+
"history.help1": ["Recent Calculate runs are saved in your browser. Click", "Os últimos dez cálculos são guardados neste navegador. Seleccione"],
|
|
219
|
+
"history.reload": ["Reload", "Carregar"],
|
|
220
|
+
"history.help2": ["to drop a case back into the form, tick two or more boxes to compare them side by side, or", "para recuperar um caso, marque dois ou mais casos para os comparar ou seleccione"],
|
|
221
|
+
"history.clear": ["Clear all", "Limpar tudo"],
|
|
222
|
+
"history.help3": ["to wipe local storage. Nothing leaves this device.", "para apagar o histórico local. Os dados permanecem neste dispositivo."],
|
|
223
|
+
"history.clearall": ["Clear all history", "Limpar todo o histórico"],
|
|
224
|
+
"prevalence.title": ["Explore prevalence sensitivity", "Explorar a prevalência"],
|
|
225
|
+
"prevalence.help": ["Sensitivity and specificity are intrinsic to the test and stay fixed. Drag the slider to see how the predictive values and post-test probabilities respond if the disease were more or less common in the population you are testing.", "Nesta simulação, a sensibilidade e a especificidade mantêm-se fixas. Mova o cursor para ver como os valores preditivos e as probabilidades pós-teste variam com a prevalência. Na prática, a sensibilidade e a especificidade também podem variar entre populações."],
|
|
226
|
+
"prevalence.label": ["Prevalence", "Prevalência"],
|
|
227
|
+
"kappa.kicker": ["Companion tool", "Concordância"],
|
|
228
|
+
"kappa.title": ["Inter-rater agreement (Cohen's κ)", "Concordância entre avaliadores (κ de Cohen)"],
|
|
229
|
+
"kappa.help": ["Two raters classify the same items as positive / negative. Enter the four counts to get Cohen's κ with 95% asymptotic CI adjusted for estimated rater marginals and a Landis–Koch qualitative label. Useful for imaging or pathology cases where the \"test\" is a human judgement.", "Dois avaliadores classificam os mesmos elementos como positivos ou negativos. Introduza as quatro contagens para obter o κ de Cohen, o IC 95% assintótico ajustado às marginais estimadas e a classificação de Landis–Koch. Útil em imagiologia ou anatomia patológica, quando o teste depende da avaliação humana."],
|
|
230
|
+
"kappa.matrix": ["Rater 1 × Rater 2", "Avaliador 1 × Avaliador 2"],
|
|
231
|
+
"kappa.r2pos": ["Rater 2 +", "Avaliador 2 +"],
|
|
232
|
+
"kappa.r2neg": ["Rater 2 −", "Avaliador 2 −"],
|
|
233
|
+
"kappa.r1pos": ["Rater 1 +", "Avaliador 1 +"],
|
|
234
|
+
"kappa.r1neg": ["Rater 1 −", "Avaliador 1 −"],
|
|
235
|
+
"kappa.calculate": ["Compute κ", "Calcular κ"],
|
|
236
|
+
"clear": ["Clear", "Limpar"],
|
|
237
|
+
"guide.title": ["Interpretation guide", "Guia de interpretação"],
|
|
238
|
+
"guide.sens": ["Sensitivity and specificity", "Sensibilidade e especificidade"],
|
|
239
|
+
"guide.sens.help": ["Quantify the test's ability to correctly identify people with and without the condition. Useful for understanding the intrinsic performance of the test. Learn more at", "Quantificam a capacidade do teste para identificar correctamente pessoas com e sem a condição. Ajudam a compreender o desempenho do teste. Saiba mais na"],
|
|
240
|
+
"guide.predictive": ["Predictive values", "Valores preditivos"],
|
|
241
|
+
"guide.predictive.help": ["Depend on prevalence (pre-test probability). Use them to estimate the probability that a person has or does not have the disease after a positive or negative result. See", "Dependem da prevalência na população estudada. Estimam a probabilidade de ter ou não ter a doença após um resultado positivo ou negativo. Consulte a"],
|
|
242
|
+
"guide.readmore": ["for further reading.", "para saber mais."],
|
|
243
|
+
"guide.lr": ["Likelihood ratios", "Razões de verosimilhança"],
|
|
244
|
+
"guide.lr.help": ["Relate the probability of a result in diseased vs. non-diseased individuals and can be applied directly in Bayes' formula to update the probability of disease. Further reference at", "Relacionam a probabilidade de um resultado em pessoas com e sem a doença. Aplicam-se à fórmula de Bayes para actualizar a probabilidade de doença. Consulte a"],
|
|
245
|
+
"guide.post": ["Post-test probabilities", "Probabilidades pós-teste"],
|
|
246
|
+
"guide.post.help": ["Result from applying the LR to pre-test odds. They are crucial for clinical decisions such as initiating treatment or ordering additional tests. See also", "Resultam da aplicação da razão de verosimilhança às odds pré-teste. Apoiam decisões como iniciar tratamento ou pedir testes adicionais. Consulte também a"],
|
|
247
|
+
"stard.kicker": ["Critical appraisal", "Avaliação crítica"],
|
|
248
|
+
"stard.title": ["STARD essentials", "Princípios STARD"],
|
|
249
|
+
"stard.intro1": ["Numbers from a 2×2 only mean what the underlying study allows them to mean. The", "A interpretação de uma matriz 2×2 depende do estudo que lhe deu origem. A"],
|
|
250
|
+
"stard.link": ["STARD 2015 checklist", "lista STARD 2015"],
|
|
251
|
+
"stard.intro2": ["has 30 items; six of them dominate the interpretation of sens / spec / PPV / NPV.", "tem 30 itens; estes seis são essenciais para interpretar a sensibilidade, a especificidade, o VPP e o VPN."],
|
|
252
|
+
"stard.1": ["1. Reference standard — what counted as \"truth\"?", "1. Padrão de referência"],
|
|
253
|
+
"stard.1a": ["If the gold standard is imperfect (e.g. expert consensus instead of biopsy), sensitivity and specificity are biased. Worse: if the reference standard incorporates the index test result, agreement is inflated (", "Se o padrão de referência for imperfeito (por exemplo, consenso de especialistas em vez de biópsia), a sensibilidade e a especificidade podem estar enviesadas. Se incorporar o resultado do teste em avaliação, a concordância aumenta artificialmente ("],
|
|
254
|
+
"stard.1b": ["incorporation bias", "viés de incorporação"],
|
|
255
|
+
"stard.1c": ["). Ask which standard was used and whether the index test fed into it.", "). Verifique qual foi o padrão usado e se incluiu o teste em avaliação."],
|
|
256
|
+
"stard.2": ["2. Spectrum of patients — who got tested?", "2. Espectro de participantes"],
|
|
257
|
+
"stard.2a": ["Performance estimated in obvious cases (\"sick\" vs healthy volunteers) almost always overestimates real-world accuracy. Check whether enrolment was", "Comparar casos evidentes com voluntários saudáveis tende a sobrestimar a exactidão na prática. Verifique se a inclusão foi"],
|
|
258
|
+
"stard.2b": ["consecutive", "consecutiva"],
|
|
259
|
+
"stard.or": ["or", "ou"],
|
|
260
|
+
"stard.2c": ["random", "aleatória"],
|
|
261
|
+
"stard.2d": [", and whether it spanned the clinical spectrum you'll apply the test to (mild, atypical, comorbid).", "e se abrangeu o espectro clínico em que irá aplicar o teste: casos ligeiros, atípicos e com comorbilidades."],
|
|
262
|
+
"stard.3": ["3. Verification — was the reference standard applied to everyone?", "3. Verificação"],
|
|
263
|
+
"stard.3a": ["Partial verification bias", "O viés de verificação parcial"],
|
|
264
|
+
"stard.3b": ["arises when positive-index-test patients are more likely to receive the gold standard than negative ones — inflates sensitivity, deflates specificity. Look for differential or post-hoc verification protocols.", "surge quando o padrão de referência é aplicado mais frequentemente a participantes com teste positivo. Pode aumentar a sensibilidade e reduzir a especificidade estimadas. Verifique se houve protocolos de verificação diferentes ou definidos após os resultados."],
|
|
265
|
+
"stard.4": ["4. Blinding — were readers independent?", "4. Ocultação dos resultados"],
|
|
266
|
+
"stard.4a": ["If the person interpreting the index test also knew the reference result (or vice versa), expect", "Se quem interpretou o teste conhecia o resultado do padrão de referência (ou vice-versa), pode existir"],
|
|
267
|
+
"stard.4b": ["review bias", "viés de revisão"],
|
|
268
|
+
"stard.4c": [": estimates drift toward perfect agreement. Imaging and pathology studies should explicitly report blinding between readers and from clinical context.", ": a concordância pode ser sobrestimada. Estudos de imagiologia e anatomia patológica devem esclarecer que informação estava disponível para cada avaliador."],
|
|
269
|
+
"stard.5": ["5. Indeterminate results — where did the \"?\"s go?", "5. Resultados indeterminados"],
|
|
270
|
+
"stard.5a": ["If indeterminate, missing, or excluded results were silently dropped, the 2×2 misrepresents real practice. Ask how many results fell outside the binary scheme and how they were handled (re-tested, excluded, counted as negative…).", "Excluir sem explicação resultados indeterminados, em falta ou inválidos afasta a matriz 2×2 da prática real. Verifique quantos resultados ficaram fora da classificação binária e como foram tratados: repetidos, excluídos ou classificados como negativos."],
|
|
271
|
+
"stard.6": ["6. Subgroups — does one number describe everyone?", "6. Subgrupos"],
|
|
272
|
+
"stard.6a": ["Sensitivity / specificity often vary by age, severity, prior testing, or setting. A single 2×2 hides those differences. Check whether the authors pre-specified clinically meaningful subgroups and reported them separately.", "A sensibilidade e a especificidade podem variar com a idade, a gravidade, os testes prévios ou o contexto. Uma única matriz oculta essas diferenças. Verifique se foram definidos previamente subgrupos clinicamente relevantes e apresentados os seus resultados."],
|
|
273
|
+
"references.title": ["Resources and references", "Recursos e referências"],
|
|
274
|
+
"references.literature": ["Supporting literature", "Bibliografia"],
|
|
275
|
+
"references.online": ["Online resources", "Recursos online"],
|
|
276
|
+
"references.calculators": ["Complementary calculators", "Calculadoras complementares"],
|
|
277
|
+
"references.ebm": ["EBM Tools and Resources", "Ferramentas e recursos de medicina baseada na evidência"],
|
|
278
|
+
"references.sens": ["Sensitivity and Specificity", "Sensibilidade e especificidade"],
|
|
279
|
+
"references.open": ["Open access", "Acesso livre"],
|
|
280
|
+
"references.medical": ["Medical calculators", "Calculadoras médicas"],
|
|
281
|
+
"references.diagnostic": ["Diagnostic Test Calculator", "Calculadora de testes de diagnóstico"],
|
|
282
|
+
"footer.education": ["DIAGCALC is a diagnostic test performance calculator created for educational purposes and training of healthcare students.", "O DIAGCALC é uma calculadora de desempenho de testes de diagnóstico para ensino e formação de estudantes da área da saúde."],
|
|
283
|
+
"footer.scope": ["Any analyses, results and conclusions may not be used, interpreted or generalised beyond this specific context.", "As análises, os resultados e as conclusões destinam-se a este contexto educativo."],
|
|
284
|
+
"footer.privacy": ["🔒 All calculations run locally in your browser. No inputs, datasets, or results leave this device.", "Os cálculos são efectuados neste navegador. Os valores introduzidos, os cenários e os resultados permanecem neste dispositivo."],
|
|
285
|
+
"footer.credit": ["© 2025 — MIT Licence — Developed by", "© 2025 — Licença MIT — Desenvolvido por"],
|
|
286
|
+
"footer.contact": ["Contact", "Contacto"],
|
|
287
|
+
"history.legacy": ["Legacy: original settings unknown; recomputed with auto", "Histórico antigo: definições desconhecidas; recalculado com correcção automática"],
|
|
288
|
+
"history.matrix": ["TP {tp} · FP {fp} · FN {fn} · TN {tn} · pre {pre}%", "VP {tp} · FP {fp} · FN {fn} · VN {tn} · pré {pre}%"],
|
|
289
|
+
"history.stats": ["sens {sens} · spec {spec} · post+ {pos} · post− {neg}", "sens {sens} · esp {spec} · pós+ {pos} · pós− {neg}"],
|
|
290
|
+
"roc.row": ["Row {row}: ", "Linha {row}: "],
|
|
291
|
+
"roc.rowAria": ["ROC row {field}", "Linha ROC: {field}"],
|
|
292
|
+
"roc.optimum": ["cutoff {cutoff} (sens {sens}, spec {spec}, J={j})", "ponto de corte {cutoff} (sens {sens}, esp {spec}, J={j})"],
|
|
293
|
+
"roc.added": ["Added {count} synthetic cutoffs from a binormal fit (seed: {seed}). Editing a row preserves its simulated origin.", "Adicionados {count} pontos de corte simulados por ajuste binormal (origem: {seed}). Editar uma linha mantém a sua origem simulada."],
|
|
294
|
+
"threshold.decision": ["Test useful — result can move you across the Pt={pt}% line.", "Teste útil: o resultado pode atravessar o limiar Pt={pt}%."],
|
|
295
|
+
"chain.pre": ["Test 2 pre-test probability: {pre} (from test 1's post-test after a {result} result).", "Probabilidade pré-teste do teste 2: {pre} (probabilidade pós-teste após resultado {result} no teste 1)."],
|
|
296
|
+
"chain.result.positive": ["positive", "positivo"],
|
|
297
|
+
"chain.result.negative": ["negative", "negativo"],
|
|
298
|
+
"chain.done": ["Test 2 complete. Post-test (+) {positive}, post-test (−) {negative}.", "Teste 2 calculado. Pós-teste (+) {positive}, pós-teste (−) {negative}."],
|
|
299
|
+
"bar.aria": ["{percent} percent of 100", "{percent} por cento de 100"],
|
|
300
|
+
"band.aria": ["{metric}: {tier} tier", "{metric}: nível {tier}"],
|
|
301
|
+
"bayes.aria": ["{label}: pre-test {pre}, post-test {post}, likelihood ratio {lr}.", "{label}: pré-teste {pre}, pós-teste {post}, razão de verosimilhança {lr}."],
|
|
302
|
+
"bayes.pre": ["Pre-test {value}", "Pré-teste {value}"],
|
|
303
|
+
"bayes.post": ["Post-test {value}", "Pós-teste {value}"],
|
|
304
|
+
"ci": ["95% CI: {lower} – {upper}", "IC 95%: {lower} – {upper}"],
|
|
305
|
+
"warnings.diseased": ["Small diseased group (n={n}). Sensitivity and likelihood-ratio estimates are imprecise — interpret 95% CIs carefully.", "Grupo com doença pequeno (n={n}). As estimativas de sensibilidade e de razões de verosimilhança são imprecisas; interprete os IC 95% com cuidado."],
|
|
306
|
+
"warnings.nondiseased": ["Small non-diseased group (n={n}). Specificity and likelihood-ratio estimates are imprecise — interpret 95% CIs carefully.", "Grupo sem doença pequeno (n={n}). As estimativas de especificidade e de razões de verosimilhança são imprecisas; interprete os IC 95% com cuidado."],
|
|
307
|
+
"warnings.prevalence": ["Study prevalence ({study}%) differs substantially from your pre-test probability ({pre}%). The PPV and NPV cards reflect the 2x2's prevalence; use the post-test probabilities for your patient.", "A prevalência do estudo ({study}%) difere substancialmente da probabilidade pré-teste ({pre}%). O VPP e o VPN reflectem a prevalência da matriz 2×2; use as probabilidades pós-teste para o caso clínico."],
|
|
308
|
+
"case.modified": ["Customised: {name}", "Personalizado: {name}"],
|
|
309
|
+
"dataset.troponin.name": ["High-sensitivity troponin for AMI", "Troponina de alta sensibilidade no enfarte agudo do miocárdio"],
|
|
310
|
+
"dataset.troponin.description": ["High-sensitivity troponin for diagnosis of acute myocardial infarction.", "Troponina de alta sensibilidade no diagnóstico de enfarte agudo do miocárdio."],
|
|
311
|
+
"dataset.mammography.name": ["Screening mammography", "Mamografia de rastreio"],
|
|
312
|
+
"dataset.mammography.description": ["Mammographic screening in women aged 50-69 (aggregated data).", "Rastreio mamográfico em mulheres dos 50 aos 69 anos (dados agregados)."],
|
|
313
|
+
"dataset.covid_antigen.name": ["Rapid COVID-19 antigen test", "Teste rápido de antigénio para COVID-19"],
|
|
314
|
+
"dataset.covid_antigen.description": ["Rapid antigen test for SARS-CoV-2 in symptomatic patients (first 7 days).", "Teste rápido de antigénio para SARS-CoV-2 em pessoas sintomáticas (primeiros 7 dias)."],
|
|
315
|
+
"dataset.hiv_elisa.name": ["HIV ELISA", "ELISA para VIH"],
|
|
316
|
+
"dataset.hiv_elisa.description": ["4th-generation ELISA for HIV screening in the general population.", "ELISA de quarta geração no rastreio de VIH na população geral."],
|
|
317
|
+
"dataset.strep_throat.name": ["Rapid Streptococcus test", "Teste rápido para estreptococo"],
|
|
318
|
+
"dataset.strep_throat.description": ["Rapid antigen test for streptococcal pharyngitis in children with fever and sore throat.", "Teste rápido de antigénio para faringite estreptocócica em crianças com febre e dor de garganta."],
|
|
319
|
+
"dataset.xray_pneumonia.name": ["Chest X-ray for pneumonia", "Radiografia torácica na pneumonia"],
|
|
320
|
+
"dataset.xray_pneumonia.description": ["Chest X-ray for community-acquired pneumonia in symptomatic adults.", "Radiografia torácica no diagnóstico de pneumonia adquirida na comunidade em adultos sintomáticos."],
|
|
321
|
+
"dataset.wells_dvt.name": ["Wells score (≥2) for DVT", "Pontuação de Wells (≥2) na TVP"],
|
|
322
|
+
"dataset.wells_dvt.description": ["Wells clinical prediction rule (cut-off ≥2 points = 'DVT likely') applied to outpatients with suspected lower-limb deep vein thrombosis.", "Regra clínica de Wells (≥2 pontos: TVP provável) em pessoas com suspeita de trombose venosa profunda dos membros inferiores em ambulatório."],
|
|
323
|
+
"dataset.heart_acs.name": ["HEART score (≥4) for MACE in chest pain", "Pontuação HEART (≥4) para eventos cardíacos na dor torácica"],
|
|
324
|
+
"dataset.heart_acs.description": ["HEART score (History, ECG, Age, Risk factors, Troponin) ≥4 to predict 6-week major adverse cardiac events in undifferentiated ED chest pain.", "Pontuação HEART (história clínica, ECG, idade, factores de risco e troponina) ≥4 para prever eventos cardíacos adversos major às seis semanas em pessoas com dor torácica no serviço de urgência."],
|
|
325
|
+
"dataset.curb65.name": ["CURB-65 (≥2) for severe community-acquired pneumonia", "CURB-65 (≥2) na pneumonia adquirida na comunidade grave"],
|
|
326
|
+
"dataset.curb65.description": ["CURB-65 score ≥2 as a marker of severe community-acquired pneumonia / 30-day mortality, derived/validated across UK, NZ and Netherlands cohorts.", "CURB-65 ≥2 como indicador de pneumonia adquirida na comunidade grave ou mortalidade aos 30 dias, desenvolvido e validado em grupos do Reino Unido, Nova Zelândia e Países Baixos."],
|
|
327
|
+
"dataset.qsofa.name": ["qSOFA (≥2) for sepsis screening", "qSOFA (≥2) no rastreio de sépsis"],
|
|
328
|
+
"dataset.qsofa.description": ["Quick SOFA score ≥2 (SBP ≤100, RR ≥22, altered mentation) as a bedside screen for in-hospital mortality among patients with suspected infection outside the ICU.", "qSOFA ≥2 (pressão sistólica ≤100, frequência respiratória ≥22, alteração do estado mental) para rastrear mortalidade hospitalar em pessoas com suspeita de infecção fora dos cuidados intensivos."],
|
|
329
|
+
"dataset.apgar_5min.name": ["APGAR < 7 at 5 min for severe neonatal outcome", "APGAR < 7 aos 5 min e complicações neonatais graves"],
|
|
330
|
+
"dataset.apgar_5min.description": ["Five-minute APGAR score below 7 as a predictor of neonatal death in term infants. Illustrates a highly specific but low-sensitivity test at very low disease prevalence.", "APGAR aos cinco minutos inferior a 7 como preditor de morte neonatal em recém-nascidos de termo. Ilustra um teste muito específico, mas pouco sensível, numa condição de prevalência muito baixa."],
|
|
331
|
+
"dataset.fast_trauma.name": ["FAST ultrasound for blunt abdominal trauma", "Ecografia FAST no traumatismo abdominal fechado"],
|
|
332
|
+
"dataset.fast_trauma.description": ["Focused Assessment with Sonography for Trauma (surgeon-performed) for intra-abdominal injury in blunt trauma patients.", "Ecografia FAST realizada por cirurgião para identificar lesões intra-abdominais em pessoas com traumatismo fechado."],
|
|
333
|
+
"attr.version": ["version", "versão"],
|
|
334
|
+
"attr.language": ["Language", "Idioma"],
|
|
335
|
+
"attr.diagnostic.test.results": ["Diagnostic test results", "Resultados do teste de diagnóstico"],
|
|
336
|
+
"attr.tp": ["TP", "VP"],
|
|
337
|
+
"attr.true.positives.tp.disease.present.test.positive": ["True positives (TP): disease present, test positive", "Verdadeiros positivos (VP): doença presente, teste positivo"],
|
|
338
|
+
"attr.fp": ["FP", "FP"],
|
|
339
|
+
"attr.false.positives.fp.disease.absent.test.positive": ["False positives (FP): disease absent, test positive", "Falsos positivos (FP): doença ausente, teste positivo"],
|
|
340
|
+
"attr.fn": ["FN", "FN"],
|
|
341
|
+
"attr.false.negatives.fn.disease.present.test.negative": ["False negatives (FN): disease present, test negative", "Falsos negativos (FN): doença presente, teste negativo"],
|
|
342
|
+
"attr.tn": ["TN", "VN"],
|
|
343
|
+
"attr.true.negatives.tn.disease.absent.test.negative": ["True negatives (TN): disease absent, test negative", "Verdadeiros negativos (VN): doença ausente, teste negativo"],
|
|
344
|
+
"attr.e.g.12.5": ["e.g. 12.5", "por exemplo, 12,5"],
|
|
345
|
+
"attr.fagan.nomogram": ["Fagan nomogram", "Nomograma de Fagan"],
|
|
346
|
+
"attr.diagnostic.test.2.results": ["Diagnostic test 2 results", "Resultados do segundo teste de diagnóstico"],
|
|
347
|
+
"attr.test.2.true.positives": ["Test 2 true positives", "Teste 2: verdadeiros positivos"],
|
|
348
|
+
"attr.test.2.false.positives": ["Test 2 false positives", "Teste 2: falsos positivos"],
|
|
349
|
+
"attr.test.2.false.negatives": ["Test 2 false negatives", "Teste 2: falsos negativos"],
|
|
350
|
+
"attr.test.2.true.negatives": ["Test 2 true negatives", "Teste 2: verdadeiros negativos"],
|
|
351
|
+
"attr.remove.row": ["Remove row", "Remover linha"],
|
|
352
|
+
"attr.fit.an.illustrative.equal.variance.binormal.model.and": ["Fit an illustrative equal-variance binormal model and generate seven simulated cutoffs.", "Ajustar um modelo binormal ilustrativo de variância igual e gerar sete pontos de corte simulados."],
|
|
353
|
+
"attr.receiver.operating.characteristic.curve": ["Receiver operating characteristic curve", "Curva ROC"],
|
|
354
|
+
"attr.saved.calculations": ["Saved calculations", "Cálculos guardados"],
|
|
355
|
+
"attr.ppv.and.npv.as.a.function.of.prevalence": ["PPV and NPV as a function of prevalence", "VPP e VPN em função da prevalência"],
|
|
356
|
+
"attr.score.distribution.with.threshold.line": ["Score distribution with threshold line", "Distribuição de valores com o limiar"],
|
|
357
|
+
"attr.confusion.matrix.at.current.threshold": ["Confusion matrix at current threshold", "Matriz de confusão no limiar actual"],
|
|
358
|
+
"attr.inter.rater.agreement.matrix": ["Inter-rater agreement matrix", "Matriz de concordância entre avaliadores"],
|
|
359
|
+
"attr.": ["++", "++"],
|
|
360
|
+
"attr.both.raters.positive": ["Both raters positive", "Ambos os avaliadores positivos"],
|
|
361
|
+
"attr..18": ["+−", "+−"],
|
|
362
|
+
"attr.only.rater.1.positive": ["Only rater 1 positive", "Só o avaliador 1 positivo"],
|
|
363
|
+
"attr..19": ["−+", "−+"],
|
|
364
|
+
"attr.only.rater.2.positive": ["Only rater 2 positive", "Só o avaliador 2 positivo"],
|
|
365
|
+
"attr..17": ["−−", "−−"],
|
|
366
|
+
"attr.both.raters.negative": ["Both raters negative", "Ambos os avaliadores negativos"],
|
|
367
|
+
"matrix.tp": ["TP", "VP"],
|
|
368
|
+
"matrix.tn": ["TN", "VN"],
|
|
369
|
+
"matrix.tp.tp.fn": ["TP / (TP + FN)", "VP / (VP + FN)"],
|
|
370
|
+
"matrix.tn.tn.fp": ["TN / (TN + FP)", "VN / (VN + FP)"],
|
|
371
|
+
"matrix.tp.tp.fp": ["TP / (TP + FP)", "VP / (VP + FP)"],
|
|
372
|
+
"matrix.tn.tn.fn": ["TN / (TN + FN)", "VN / (VN + FN)"],
|
|
373
|
+
"matrix.disease": ["Disease +", "Doença +"],
|
|
374
|
+
"matrix.disease.150": ["Disease −", "Doença −"],
|
|
375
|
+
"matrix.test": ["Test +", "Teste +"],
|
|
376
|
+
"matrix.test.152": ["Test −", "Teste −"],
|
|
377
|
+
"copy.custom.case": ["Custom case", "Caso personalizado"],
|
|
378
|
+
"copy.customised": ["Customised:", "Personalizado:"],
|
|
379
|
+
"copy.reload": ["Reload", "Carregar"],
|
|
380
|
+
"copy.delete.this.entry": ["Delete this entry", "Apagar este cálculo"],
|
|
381
|
+
"copy.delete.history.entry": ["Delete history entry", "Apagar cálculo do histórico"],
|
|
382
|
+
"copy.pre.test": ["Pre-test", "Pré-teste"],
|
|
383
|
+
"copy.engine.continuity": ["Engine / continuity", "Versão / correcção"],
|
|
384
|
+
"copy.sensitivity": ["Sensitivity", "Sensibilidade"],
|
|
385
|
+
"copy.specificity": ["Specificity", "Especificidade"],
|
|
386
|
+
"copy.ppv": ["PPV", "VPP"],
|
|
387
|
+
"copy.npv": ["NPV", "VPN"],
|
|
388
|
+
"copy.post.test": ["Post-test (+)", "Pós-teste (+)"],
|
|
389
|
+
"copy.post.test.12": ["Post-test (−)", "Pós-teste (−)"],
|
|
390
|
+
"copy.tp.fp.fn.tn": ["TP/FP/FN/TN", "VP/FP/FN/VN"],
|
|
391
|
+
"copy.clear.all.saved.history.this.cannot.be.undone": ["Clear all saved history? This cannot be undone.", "Apagar todos os cálculos guardados? Esta acção é irreversível."],
|
|
392
|
+
"copy.synthetic.cutoff.generated.from.a.binormal.fit.of": ["Synthetic cutoff — generated from a binormal fit of the first row.", "Ponto de corte simulado — gerado por ajuste binormal à primeira linha."],
|
|
393
|
+
"copy.edited.simulated.cutoff.still.derived.from.a.binormal": ["Edited simulated cutoff — still derived from a binormal model.", "Ponto de corte simulado editado — continua a derivar de um modelo binormal."],
|
|
394
|
+
"copy.remove.this.cutoff.row": ["Remove this cutoff row", "Remover este ponto de corte"],
|
|
395
|
+
"copy.includes.simulated.or.edited.simulated.cutoffs.auc.and": ["Includes simulated or edited simulated cutoffs. AUC and optimum are illustrative, not empirical estimates.", "Inclui pontos de corte simulados ou simulados editados. A AUC e o ponto óptimo são ilustrativos, não estimativas empíricas."],
|
|
396
|
+
"copy.illustrative.auc.includes.simulated.rows": ["Illustrative AUC (includes simulated rows)", "AUC ilustrativa (inclui linhas simuladas)"],
|
|
397
|
+
"copy.auc.trapezoid.estimate": ["AUC (trapezoid estimate)", "AUC (método dos trapézios)"],
|
|
398
|
+
"copy.cutoffs": ["Cutoffs", "Pontos de corte"],
|
|
399
|
+
"copy.illustrative.youden.optimum": ["Illustrative Youden optimum", "Óptimo ilustrativo de Youden"],
|
|
400
|
+
"copy.youden.optimum": ["Youden optimum", "Óptimo de Youden"],
|
|
401
|
+
"copy.roc.row.1": ["ROC row 1", "Linha ROC 1"],
|
|
402
|
+
"copy.need.a.valid.2.2.tp.fp.fn": ["Need a valid 2×2 (TP / FP / FN / TN) before synthetic cutoffs can be generated — fill the main form or row 1 first.", "É necessária uma matriz 2×2 válida (VP / FP / FN / VN) para gerar pontos de corte simulados. Preencha primeiro o formulário principal ou a primeira linha."],
|
|
403
|
+
"copy.could.not.fit.a.binormal.model.to.that": ["Could not fit a binormal model to that 2×2 — the synthetic scaffold needs at least one diseased and one non-diseased case.", "Não foi possível ajustar um modelo binormal a esta matriz. É necessário pelo menos um caso com doença e um sem doença."],
|
|
404
|
+
"copy.enter.non.negative.integers.in.all.four.cells": ["Enter non-negative integers in all four cells.", "Introduza números inteiros não negativos nas quatro células."],
|
|
405
|
+
"copy.total.cannot.be.zero": ["Total cannot be zero.", "O total deve ser superior a zero."],
|
|
406
|
+
"copy.95.ci": ["95% CI", "IC 95%"],
|
|
407
|
+
"copy.observed.agreement.p.o": ["Observed agreement (p_o)", "Concordância observada (p_o)"],
|
|
408
|
+
"copy.expected.agreement.p.e": ["Expected agreement (p_e)", "Concordância esperada (p_e)"],
|
|
409
|
+
"copy.interpretation": ["Interpretation", "Interpretação"],
|
|
410
|
+
"copy.heads.up": ["Heads up", "Notas de interpretação"],
|
|
411
|
+
"copy.thresholds.unavailable.this.display.requires.lr.1.and": ["Thresholds unavailable: this display requires LR+ > 1 and 0 ≤ LR− < 1. Review test direction.", "Limiares indisponíveis: esta representação exige RV+ > 1 e 0 ≤ RV− < 1. Verifique o sentido do teste."],
|
|
412
|
+
"copy.testing.threshold.don.t.test.below": ["Testing threshold (don't test below)", "Limiar de teste (não testar abaixo)"],
|
|
413
|
+
"copy.treatment.threshold.pt": ["Treatment threshold (Pt)", "Limiar de tratamento (Pt)"],
|
|
414
|
+
"copy.test.treatment.threshold.just.treat.above": ["Test-treatment threshold (just treat above)", "Limiar de teste-tratamento (tratar acima)"],
|
|
415
|
+
"copy.decision.at.current.pre.test": ["Decision at current pre-test", "Decisão na probabilidade pré-teste actual"],
|
|
416
|
+
"copy.don.t.test.even.a.positive.result.wouldn": ["Don't test — even a positive result wouldn't reach Pt.", "Não testar: mesmo um resultado positivo não atingiria Pt."],
|
|
417
|
+
"copy.treat.without.testing.even.a.negative.result.stays": ["Treat without testing — even a negative result stays above Pt.", "Tratar sem testar: mesmo um resultado negativo fica acima de Pt."],
|
|
418
|
+
"copy.calculate.test.1.first": ["Calculate test 1 first.", "Calcule primeiro o teste 1."],
|
|
419
|
+
"copy.test.2.fields.cleared": ["Test 2 fields cleared.", "Campos do teste 2 limpos."],
|
|
420
|
+
"copy.inputs.changed.calculate.to.update.results": ["Inputs changed. Calculate to update results.", "Os valores foram alterados. Calcule para actualizar os resultados."],
|
|
421
|
+
"copy.illustrative.teaching.scenario": ["Illustrative teaching scenario", "Cenário didáctico ilustrativo"],
|
|
422
|
+
"copy.positive.test": ["Positive test", "Teste positivo"],
|
|
423
|
+
"copy.negative.test": ["Negative test", "Teste negativo"],
|
|
424
|
+
"copy.weak": ["weak", "fraco"],
|
|
425
|
+
"copy.limited": ["limited", "limitado"],
|
|
426
|
+
"copy.useful": ["useful", "útil"],
|
|
427
|
+
"copy.rule.in": ["rule-in", "confirmar"],
|
|
428
|
+
"copy.rule.out": ["rule-out", "excluir"],
|
|
429
|
+
"copy.poor": ["poor", "fraco"],
|
|
430
|
+
"copy.very.strong": ["very strong", "muito forte"],
|
|
431
|
+
"copy.best": ["best", "óptimo"],
|
|
432
|
+
"copy.1.specificity.fpr": ["1 − specificity (FPR, %)", "1 − especificidade (FP, %)"],
|
|
433
|
+
"copy.sensitivity.tpr": ["Sensitivity (TPR, %)", "Sensibilidade (VP, %)"],
|
|
434
|
+
"copy.prevalence": ["Prevalence (%)", "Prevalência (%)"],
|
|
435
|
+
"copy.predictive.value": ["Predictive value (%)", "Valor preditivo (%)"],
|
|
436
|
+
"copy.post.test.59": ["Post-test", "Pós-teste"],
|
|
437
|
+
"copy.pre.test.odds.lr.post.test.odds": ["Pre-test odds × LR = post-test odds", "Odds pré-teste × RV = odds pós-teste"],
|
|
438
|
+
"copy.off.scale.undefined.see.values.above": ["Off-scale / undefined: see values above", "Fora da escala / indefinido: consultar os valores acima"],
|
|
439
|
+
"copy.diseased": ["diseased", "com doença"],
|
|
440
|
+
"copy.healthy": ["healthy", "sem doença"],
|
|
441
|
+
"copy.expected.agreement.is.1.kappa.is.undefined": ["Expected agreement is 1; kappa is undefined.", "Concordância esperada igual a 1; κ indefinido."],
|
|
442
|
+
"copy.enter.a.confusion.matrix.and.pre.test.probability": ["Enter a confusion matrix and pre-test probability.", "Introduza a matriz de confusão e a probabilidade pré-teste."],
|
|
443
|
+
"copy.enter.non.negative.integers.for.all.confusion.matrix": ["Enter non-negative integers for all confusion matrix cells.", "Introduza números inteiros não negativos em todas as células da matriz."],
|
|
444
|
+
"copy.use.whole.numbers.for.tp.fp.fn.and": ["Use whole numbers for TP, FP, FN, and TN.", "Use números inteiros para VP, FP, FN e VN."],
|
|
445
|
+
"copy.the.total.must.be.a.safe.whole.number": ["The total must be a safe whole number (at most 9007199254740991).", "O total deve ser um número inteiro até 9007199254740991."],
|
|
446
|
+
"copy.enter.a.valid.pre.test.probability": ["Enter a valid pre-test probability.", "Introduza uma probabilidade pré-teste válida."],
|
|
447
|
+
"copy.pre.test.probability.must.be.between.0.and": ["Pre-test probability must be between 0% and 100%, inclusive.", "A probabilidade pré-teste deve estar entre 0% e 100%, inclusive."],
|
|
448
|
+
"copy.sensitivity.is.indeterminate.add.cases.with.disease.tp": ["Sensitivity is indeterminate: add cases with disease (TP or FN).", "Sensibilidade indeterminada: adicione casos com doença (VP ou FN)."],
|
|
449
|
+
"copy.specificity.is.indeterminate.add.cases.without.disease.tn": ["Specificity is indeterminate: add cases without disease (TN or FP).", "Especificidade indeterminada: adicione casos sem doença (VN ou FP)."],
|
|
450
|
+
"copy.use.between.one.and.500.complete.cutoff.rows": ["Use between one and 500 complete cutoff rows.", "Use entre uma e 500 linhas completas de pontos de corte."],
|
|
451
|
+
"copy.all.cutoffs.must.use.the.same.diseased.and": ["All cutoffs must use the same diseased and non-diseased cohorts.", "Todos os pontos de corte devem usar os mesmos grupos com e sem doença."],
|
|
452
|
+
"copy.cutoff.must.be.a.finite.number.or.left": ["Cutoff must be a finite number or left blank.", "O ponto de corte deve ser um número finito ou ficar em branco."],
|
|
453
|
+
"copy.sensitivity.must.not.decrease.as.the.false.positive": ["Sensitivity must not decrease as the false-positive rate increases.", "A sensibilidade não deve diminuir quando a taxa de falsos positivos aumenta."],
|
|
454
|
+
"copy.cutoffs.must.follow.the.selected.higher.score.positive": ["Cutoffs must follow the selected higher-score-positive direction.", "Os pontos de corte devem respeitar o sentido escolhido: valores mais altos positivos."],
|
|
455
|
+
"copy.cutoffs.must.follow.the.selected.lower.score.positive": ["Cutoffs must follow the selected lower-score-positive direction.", "Os pontos de corte devem respeitar o sentido escolhido: valores mais baixos positivos."],
|
|
456
|
+
"copy.illustrative.teaching.counts.the.citation.is.background.reading": ["Illustrative teaching counts. The citation is background reading; these counts have not been verified against a published table.", "Contagens ilustrativas para ensino. A referência serve de leitura complementar; estas contagens não foram verificadas numa tabela publicada."],
|
|
457
|
+
"copy.generic.illustrative.teaching.scenario.not.an.empirical.study": ["Generic illustrative teaching scenario; not an empirical study.", "Cenário didáctico genérico e ilustrativo; não corresponde a um estudo empírico."],
|
|
458
|
+
"copy.case.recalculated.with.the.current.engine.legacy.history": ["Case recalculated with the current engine. Legacy history used auto because its original settings were not recorded.", "Caso recalculado com a versão actual. Foi usada a correcção automática porque o histórico antigo não registava as definições originais."],
|
|
459
|
+
"copy.auto": ["auto", "automática"],
|
|
460
|
+
"copy.always": ["always", "sempre"],
|
|
461
|
+
"copy.never": ["never", "nunca"],
|
|
462
|
+
"copy.less.than.chance": ["Less than chance", "Inferior ao acaso"],
|
|
463
|
+
"copy.slight": ["Slight", "Ligeira"],
|
|
464
|
+
"copy.fair": ["Fair", "Razoável"],
|
|
465
|
+
"copy.moderate": ["Moderate", "Moderada"],
|
|
466
|
+
"copy.substantial": ["Substantial", "Substancial"],
|
|
467
|
+
"copy.almost.perfect": ["Almost perfect", "Quase perfeita"],
|
|
468
|
+
};
|
|
469
|
+
for (const [key, [en, pt]] of Object.entries(browserCopy)) {
|
|
470
|
+
translations.en[key] = en;
|
|
471
|
+
translations["pt-PT"][key] = pt;
|
|
472
|
+
}
|
|
473
|
+
const sourceKeys = new Map(Object.entries(browserCopy).map(([key, [en]]) => [en, key]));
|
|
474
|
+
|
|
475
|
+
function text(source) {
|
|
476
|
+
const key = sourceKeys.get(source);
|
|
477
|
+
return key ? t(key) : source;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function relocalise(source) {
|
|
481
|
+
for (const key of Object.keys(translations.en)) {
|
|
482
|
+
for (const candidate of [translations.en[key], translations["pt-PT"][key]]) {
|
|
483
|
+
if (candidate === source) return t(key);
|
|
484
|
+
if (!candidate.includes("{")) continue;
|
|
485
|
+
const fields = [...candidate.matchAll(/\{(\w+)\}/g)].map((match) => match[1]);
|
|
486
|
+
const escaped = candidate.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\{\w+\\\}/g, "(.+?)");
|
|
487
|
+
const match = source.match(new RegExp("^" + escaped + "$"));
|
|
488
|
+
if (match) return t(key, Object.fromEntries(fields.map((field, index) => [field, match[index + 1]])));
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return source;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function message(source) {
|
|
495
|
+
for (const key of ["warnings.diseased", "warnings.nondiseased", "warnings.prevalence"]) {
|
|
496
|
+
const template = translations.en[key];
|
|
497
|
+
const fields = [...template.matchAll(/\{(\w+)\}/g)].map((match) => match[1]);
|
|
498
|
+
const escaped = template.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\{\w+\\\}/g, "(.+?)");
|
|
499
|
+
const match = source.match(new RegExp("^" + escaped + "$"));
|
|
500
|
+
if (match) return t(key, Object.fromEntries(fields.map((field, index) => [field, match[index + 1]])));
|
|
501
|
+
}
|
|
502
|
+
return text(source);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const FALLBACK = "en";
|
|
506
|
+
const STORAGE_KEY = "diagcalc-lang";
|
|
507
|
+
let activeLocale = FALLBACK;
|
|
508
|
+
|
|
509
|
+
function getLocale() {
|
|
510
|
+
return activeLocale;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function setLocale(locale) {
|
|
514
|
+
activeLocale = translations[locale] ? locale : FALLBACK;
|
|
515
|
+
try {
|
|
516
|
+
if (typeof window !== "undefined" && (/** @type {any} */ (window)).DiagcalcStorage) {
|
|
517
|
+
(/** @type {any} */ (window)).DiagcalcStorage.createStorage(() => window.localStorage).setItem(STORAGE_KEY, activeLocale);
|
|
518
|
+
}
|
|
519
|
+
} catch (_) {}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function detectLocale() {
|
|
523
|
+
try {
|
|
524
|
+
if (typeof window !== "undefined" && (/** @type {any} */ (window)).DiagcalcStorage) {
|
|
525
|
+
const stored = (/** @type {any} */ (window)).DiagcalcStorage.createStorage(() => window.localStorage).getItem(STORAGE_KEY);
|
|
526
|
+
if (stored && translations[stored]) return stored;
|
|
527
|
+
}
|
|
528
|
+
} catch (_) {}
|
|
529
|
+
if (typeof navigator !== "undefined" && navigator.language) {
|
|
530
|
+
const tag = navigator.language;
|
|
531
|
+
if (translations[tag]) return tag;
|
|
532
|
+
// Match a base language (e.g., "pt" → "pt-PT")
|
|
533
|
+
const base = tag.split("-")[0];
|
|
534
|
+
const match = Object.keys(translations).find((k) => k === base || k.startsWith(base + "-"));
|
|
535
|
+
if (match) return match;
|
|
536
|
+
}
|
|
537
|
+
return FALLBACK;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function t(key, vars) {
|
|
541
|
+
const table = translations[activeLocale] || translations[FALLBACK];
|
|
542
|
+
let value = table[key];
|
|
543
|
+
if (value === undefined) value = translations[FALLBACK][key];
|
|
544
|
+
if (value === undefined) return key;
|
|
545
|
+
if (vars && typeof value === "string") {
|
|
546
|
+
return value.replace(/\{(\w+)\}/g, (_, k) => (vars[k] !== undefined ? String(vars[k]) : `{${k}}`));
|
|
547
|
+
}
|
|
548
|
+
return value;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function applyToDom(root) {
|
|
552
|
+
if (typeof document === "undefined") return;
|
|
553
|
+
const scope = root || document;
|
|
554
|
+
scope.querySelectorAll("[data-i18n]").forEach((el) => {
|
|
555
|
+
const key = el.getAttribute("data-i18n");
|
|
556
|
+
if (key) el.textContent = t(key);
|
|
557
|
+
});
|
|
558
|
+
scope.querySelectorAll("[data-i18n-attr]").forEach((el) => {
|
|
559
|
+
const spec = el.getAttribute("data-i18n-attr");
|
|
560
|
+
// Format: "aria-label=some.key;title=other.key"
|
|
561
|
+
spec.split(";").forEach((pair) => {
|
|
562
|
+
const [attr, key] = pair.split("=").map((s) => s.trim());
|
|
563
|
+
if (attr && key) el.setAttribute(attr, t(key));
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function availableLocales() {
|
|
569
|
+
return Object.keys(translations);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return {
|
|
573
|
+
translations,
|
|
574
|
+
text,
|
|
575
|
+
message,
|
|
576
|
+
relocalise,
|
|
577
|
+
getLocale,
|
|
578
|
+
setLocale,
|
|
579
|
+
detectLocale,
|
|
580
|
+
t,
|
|
581
|
+
applyToDom,
|
|
582
|
+
availableLocales,
|
|
583
|
+
};
|
|
584
|
+
}));
|