diff --git a/messages/de.json b/messages/de.json index bdc16bfd4..070101ba0 100644 --- a/messages/de.json +++ b/messages/de.json @@ -5373,7 +5373,9 @@ "rowsImported": "Bisher {count} Einträge importiert", "doneSummary": "Fertig — {imported} von {read} Datensätzen importiert, {skipped} klinische Datensätze übersprungen.", "doneNothing": "Aus diesem Archiv wurde nichts importiert: 0 von {read} Datensätzen sind angekommen.", - "writtenByHealthLog": "{count} Einträge, die HealthLog selbst in Apple Health geschrieben hat, wurden ausgelassen, weil sie schon in deinen Daten sind.", + "writtenByHealthLogOne": "{count} Eintrag, den HealthLog selbst in Apple Health geschrieben hat, wurde ausgelassen, weil er schon in deinen Daten ist.", + "writtenByHealthLogOther": "{count} Einträge, die HealthLog selbst in Apple Health geschrieben hat, wurden ausgelassen, weil sie schon in deinen Daten sind.", + "writtenByHealthLogFew": "{count} Einträge, die HealthLog selbst in Apple Health geschrieben hat, wurden ausgelassen, weil sie schon in deinen Daten sind.", "ecgResult": "EKGs: {discovered} gefunden, {imported} importiert, {updated} aktualisiert, {skipped} übersprungen, {failed} fehlgeschlagen. Fehler bei EKGs verwerfen keine unterstützten Datensätze, die aus export.xml importiert wurden.", "estimateWarning": "Kumulative Summen für {count} Tag(e) wurden anhand der größten Exportquelle geschätzt. Native Apple-Health-Statistiken bleiben maßgeblich.", "phase": { diff --git a/messages/en.json b/messages/en.json index 979ec1a04..41f1dbcb3 100644 --- a/messages/en.json +++ b/messages/en.json @@ -5373,7 +5373,9 @@ "rowsImported": "{count} rows imported so far", "doneSummary": "Done — {imported} of {read} records imported, {skipped} clinical records skipped.", "doneNothing": "Nothing was imported from that archive: 0 of {read} records landed.", - "writtenByHealthLog": "{count} records that HealthLog wrote into Apple Health itself were left out, because they are already in your data.", + "writtenByHealthLogOne": "{count} record that HealthLog wrote into Apple Health itself was left out, because it is already in your data.", + "writtenByHealthLogOther": "{count} records that HealthLog wrote into Apple Health itself were left out, because they are already in your data.", + "writtenByHealthLogFew": "{count} records that HealthLog wrote into Apple Health itself were left out, because they are already in your data.", "ecgResult": "ECGs: {discovered} discovered, {imported} imported, {updated} updated, {skipped} skipped, {failed} failed. ECG failures do not discard supported records imported from export.xml.", "estimateWarning": "Cumulative totals for {count} day(s) were estimated from the largest export source. Native Apple Health statistics remain authoritative.", "phase": { diff --git a/messages/es.json b/messages/es.json index 50198919b..1a1167cf8 100644 --- a/messages/es.json +++ b/messages/es.json @@ -5373,7 +5373,9 @@ "rowsImported": "{count} filas importadas hasta ahora", "doneSummary": "Listo: {imported} de {read} registros importados, {skipped} registros clínicos omitidos.", "doneNothing": "No se importó nada de ese archivo: llegaron 0 de {read} registros.", - "writtenByHealthLog": "Se omitieron {count} registros que HealthLog escribió en Apple Health, porque ya están en tus datos.", + "writtenByHealthLogOne": "Se omitió {count} registro que HealthLog escribió en Apple Health, porque ya está en tus datos.", + "writtenByHealthLogOther": "Se omitieron {count} registros que HealthLog escribió en Apple Health, porque ya están en tus datos.", + "writtenByHealthLogFew": "Se omitieron {count} registros que HealthLog escribió en Apple Health, porque ya están en tus datos.", "ecgResult": "ECG: {discovered} detectados, {imported} importados, {updated} actualizados, {skipped} omitidos y {failed} fallidos. Los errores de ECG no descartan los registros compatibles importados desde export.xml.", "estimateWarning": "Los totales acumulados de {count} día(s) se estimaron a partir de la mayor fuente de la exportación. Las estadísticas nativas de Apple Health siguen siendo la referencia.", "phase": { diff --git a/messages/fr.json b/messages/fr.json index 19cd12f96..721c5befe 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -5373,7 +5373,9 @@ "rowsImported": "{count} lignes importées jusqu'ici", "doneSummary": "Terminé — {imported} enregistrements sur {read} importés, {skipped} enregistrements cliniques ignorés.", "doneNothing": "Rien n'a été importé de cette archive : 0 enregistrement sur {read} est arrivé.", - "writtenByHealthLog": "{count} enregistrements que HealthLog a écrits lui-même dans Santé ont été ignorés, car ils figurent déjà dans vos données.", + "writtenByHealthLogOne": "{count} enregistrement que HealthLog a écrit lui-même dans Santé a été ignoré, car il figure déjà dans vos données.", + "writtenByHealthLogOther": "{count} enregistrements que HealthLog a écrits lui-même dans Santé ont été ignorés, car ils figurent déjà dans vos données.", + "writtenByHealthLogFew": "{count} enregistrements que HealthLog a écrits lui-même dans Santé ont été ignorés, car ils figurent déjà dans vos données.", "ecgResult": "ECG : {discovered} détectés, {imported} importés, {updated} mis à jour, {skipped} ignorés et {failed} échoués. Les erreurs d’ECG n’annulent pas les enregistrements compatibles importés depuis export.xml.", "estimateWarning": "Les totaux cumulés de {count} jour(s) ont été estimés à partir de la source d’export la plus élevée. Les statistiques Apple Santé natives restent la référence.", "phase": { diff --git a/messages/it.json b/messages/it.json index 9c77b4ab9..d6b34faa5 100644 --- a/messages/it.json +++ b/messages/it.json @@ -5373,7 +5373,9 @@ "rowsImported": "{count} righe importate finora", "doneSummary": "Fatto: {imported} di {read} record importati, {skipped} record clinici saltati.", "doneNothing": "Da questo archivio non è stato importato nulla: sono arrivati 0 record su {read}.", - "writtenByHealthLog": "{count} registrazioni che HealthLog ha scritto in Salute sono state escluse, perché sono già nei tuoi dati.", + "writtenByHealthLogOne": "{count} registrazione che HealthLog ha scritto in Salute è stata esclusa, perché è già nei tuoi dati.", + "writtenByHealthLogOther": "{count} registrazioni che HealthLog ha scritto in Salute sono state escluse, perché sono già nei tuoi dati.", + "writtenByHealthLogFew": "{count} registrazioni che HealthLog ha scritto in Salute sono state escluse, perché sono già nei tuoi dati.", "ecgResult": "ECG: {discovered} rilevati, {imported} importati, {updated} aggiornati, {skipped} ignorati e {failed} non riusciti. Gli errori ECG non eliminano i record supportati importati da export.xml.", "estimateWarning": "I totali cumulativi di {count} giorno/i sono stati stimati dalla fonte di esportazione più alta. Le statistiche native di Apple Salute restano autorevoli.", "phase": { diff --git a/messages/ko.json b/messages/ko.json index 2c0721ccb..a22a21b1b 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -5373,7 +5373,9 @@ "rowsImported": "지금까지 {count}개 행을 불러왔어요", "doneSummary": "완료 — {read}개 중 {imported}개 기록을 불러왔고, 진료 기록 {skipped}개는 건너뛰었어요.", "doneNothing": "그 보관 파일에서 불러온 게 없어요: {read}개 중 0개만 들어왔어요.", - "writtenByHealthLog": "HealthLog가 직접 건강 앱에 기록한 {count}개 항목은 이미 데이터에 있으므로 제외했습니다.", + "writtenByHealthLogOne": "HealthLog가 직접 건강 앱에 기록한 {count}개 항목은 이미 데이터에 있으므로 제외했습니다.", + "writtenByHealthLogOther": "HealthLog가 직접 건강 앱에 기록한 {count}개 항목은 이미 데이터에 있으므로 제외했습니다.", + "writtenByHealthLogFew": "HealthLog가 직접 건강 앱에 기록한 {count}개 항목은 이미 데이터에 있으므로 제외했습니다.", "ecgResult": "심전도: 발견 {discovered}개, 불러옴 {imported}개, 갱신 {updated}개, 건너뜀 {skipped}개, 실패 {failed}개. 심전도가 실패해도 export.xml에서 불러온 지원 기록은 그대로 남아요.", "estimateWarning": "{count}일치 누적 합계는 가장 큰 내보내기 소스에서 추정했어요. Apple Health 자체 통계가 여전히 기준이에요.", "phase": { diff --git a/messages/pl.json b/messages/pl.json index c8091d1c0..229ade97a 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -5373,7 +5373,9 @@ "rowsImported": "Zaimportowano dotąd {count} wierszy", "doneSummary": "Gotowe — zaimportowano {imported} z {read} rekordów, pominięto {skipped} rekordów klinicznych.", "doneNothing": "Z tego archiwum nic nie zaimportowano: dotarło 0 z {read} rekordów.", - "writtenByHealthLog": "Pominięto {count} wpisów, które HealthLog sam zapisał w Apple Health, ponieważ są już w Twoich danych.", + "writtenByHealthLogOne": "Pominięto {count} wpis, który HealthLog sam zapisał w Apple Health, ponieważ jest już w Twoich danych.", + "writtenByHealthLogOther": "Pominięto {count} wpisów, które HealthLog sam zapisał w Apple Health, ponieważ są już w Twoich danych.", + "writtenByHealthLogFew": "Pominięto {count} wpisy, które HealthLog sam zapisał w Apple Health, ponieważ są już w Twoich danych.", "ecgResult": "EKG: wykryto {discovered}, zaimportowano {imported}, zaktualizowano {updated}, pominięto {skipped}, nie powiodło się {failed}. Błędy EKG nie usuwają obsługiwanych rekordów zaimportowanych z pliku export.xml.", "estimateWarning": "Łączne wartości dla {count} dni oszacowano na podstawie największego źródła w eksporcie. Natywne statystyki Apple Health pozostają nadrzędne.", "phase": { diff --git a/src/components/settings/import-panel/__tests__/apple-health-own-records-note.test.tsx b/src/components/settings/import-panel/__tests__/apple-health-own-records-note.test.tsx new file mode 100644 index 000000000..9d18bd756 --- /dev/null +++ b/src/components/settings/import-panel/__tests__/apple-health-own-records-note.test.tsx @@ -0,0 +1,62 @@ +/** + * The note under a finished Apple Health import that says how many records + * were left out because HealthLog wrote them into Apple Health itself. + * + * The count is often 1, and a single string rendered "1 Einträge". The note + * reads the plural tier of the active locale, so each locale's own singular + * (and the Polish few form) reaches the screen. + */ +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; +import { renderToStaticMarkup } from "react-dom/server"; + +import { I18nProvider } from "@/lib/i18n/context"; +import type { MessageBundle } from "@/lib/i18n/load-locale"; +import { locales, type Locale } from "@/lib/i18n/config"; + +import { AppleHealthOwnRecordsNote } from "../apple-health-import-card"; + +function bundle(locale: Locale): MessageBundle { + return JSON.parse( + readFileSync(join(process.cwd(), "messages", `${locale}.json`), "utf8"), + ) as MessageBundle; +} + +function render(locale: Locale, count: number): string { + return renderToStaticMarkup( + + + , + ) + .replace(/<[^>]+>/g, "") + .trim(); +} + +describe("", () => { + it("uses the singular for one record", () => { + expect(render("de", 1)).toMatch(/^1 Eintrag,/); + expect(render("en", 1)).toMatch(/^1 record that/); + expect(render("es", 1)).toContain("1 registro que"); + expect(render("fr", 1)).toMatch(/^1 enregistrement que/); + expect(render("it", 1)).toMatch(/^1 registrazione che/); + expect(render("pl", 1)).toContain("1 wpis,"); + }); + + it("uses the plural for several, and the Polish few form for 2 to 4", () => { + expect(render("de", 5)).toMatch(/^5 Einträge,/); + expect(render("en", 5)).toMatch(/^5 records that/); + expect(render("pl", 3)).toContain("3 wpisy,"); + expect(render("pl", 5)).toContain("5 wpisów,"); + }); + + it("renders a sentence with the count in every locale", () => { + for (const locale of locales) { + for (const count of [1, 2, 5]) { + const text = render(locale, count); + expect(text, `${locale} ${count}`).toContain(String(count)); + expect(text, `${locale} ${count}`).not.toContain("writtenByHealthLog"); + } + } + }); +}); diff --git a/src/components/settings/import-panel/apple-health-import-card.tsx b/src/components/settings/import-panel/apple-health-import-card.tsx index dbe40953c..fa486dc3c 100644 --- a/src/components/settings/import-panel/apple-health-import-card.tsx +++ b/src/components/settings/import-panel/apple-health-import-card.tsx @@ -32,7 +32,11 @@ interface JobStatus { clinical?: { skipped?: number }; cumulativeEstimates?: { days?: number; rows?: number }; /** Records HealthLog wrote into Apple Health itself, left out here. */ - writtenByHealthLog?: { byMarker?: number; matchedManual?: number }; + writtenByHealthLog?: { + byMarker?: number; + byExternalId?: number; + matchedManual?: number; + }; /** HK types deferred by design — read but not (yet) imported. */ deferred?: Record; /** Per-key drop counters — plain HK types are unsupported-by-design, @@ -249,6 +253,22 @@ export function AppleHealthSkipLines({ ); } +/** How many records the import left out because HealthLog wrote them. */ +export function AppleHealthOwnRecordsNote({ count }: { count: number }) { + const { tCount } = useTranslations(); + return ( +

+ {tCount( + "settings.sections.export.import.appleHealth.writtenByHealthLog", + count, + )} +

+ ); +} + export function AppleHealthImportCard() { const { t } = useTranslations(); const fileInputRef = useRef(null); @@ -346,6 +366,7 @@ export function AppleHealthImportCard() { const isDone = status?.status === "done"; const ownRecordsSkipped = (status?.result?.writtenByHealthLog?.byMarker ?? 0) + + (status?.result?.writtenByHealthLog?.byExternalId ?? 0) + (status?.result?.writtenByHealthLog?.matchedManual ?? 0); const isFailed = status?.status === "failed"; const busy = uploading || isRunning; @@ -498,17 +519,7 @@ export function AppleHealthImportCard() {

)} {isDone && ownRecordsSkipped > 0 && ( -

- {t( - "settings.sections.export.import.appleHealth.writtenByHealthLog", - { - count: ownRecordsSkipped, - }, - )} -

+ )} {isDone && (status?.result?.cumulativeEstimates?.days ?? 0) > 0 && (