mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
Merge pull request #1246 from robdegraaf/feat/i18n-nl
This commit is contained in:
commit
7be3714ac2
83 files changed
+13832
-74
No files matched your search
@@ -202,7 +202,7 @@ describe("dashboard.*Short keys exist across all six locales", () => {
|
||||
|
||||
// Resolved at test time so the assertion runs against the current
|
||||
// bundle, not a stale cached snapshot.
|
||||
const locales = ["en", "de", "fr", "es", "it", "pl", "ko"] as const;
|
||||
const locales = ["en", "de", "fr", "es", "it", "pl", "ko", "nl"] as const;
|
||||
|
||||
it.each(locales)("%s carries every dashboard.*Short key", async (locale) => {
|
||||
const messages = (await import(`../../../../messages/${locale}.json`))
|
||||
|
||||
@@ -223,9 +223,9 @@ const FULL_RECORD = {
|
||||
const DOTTED_KEY = /\b[a-z][A-Za-z0-9]*(\.[A-Za-z0-9_]+){2,}\b/g;
|
||||
|
||||
describe("<ClinicianView> resolves every label in every locale", () => {
|
||||
it("covers all seven shipped locales", () => {
|
||||
it("covers all eight shipped locales", () => {
|
||||
// A floor: an empty locale list would make the loop below assert nothing.
|
||||
expect(locales.length).toBe(7);
|
||||
expect(locales.length).toBe(8);
|
||||
});
|
||||
|
||||
for (const locale of locales) {
|
||||
|
||||
@@ -89,6 +89,7 @@ describe("CoachReadStrip — the latest reading's day", () => {
|
||||
es: "1 de octubre",
|
||||
fr: "1 octobre",
|
||||
it: "1 ottobre",
|
||||
nl: "1 oktober",
|
||||
ko: "10월 1일",
|
||||
pl: "1 października",
|
||||
};
|
||||
|
||||
@@ -103,7 +103,7 @@ describe("<KeyboardShortcutsDialog>", () => {
|
||||
expect(kbd).not.toMatch(/#[0-9a-f]{3,6}|dracula|\[/i);
|
||||
});
|
||||
|
||||
it.each(["de", "es", "fr", "it", "pl", "ko"])(
|
||||
it.each(["de", "es", "fr", "it", "pl", "ko", "nl"])(
|
||||
"is translated in %s",
|
||||
(locale) => {
|
||||
const html = render({}, locale);
|
||||
|
||||
@@ -66,7 +66,16 @@ describe("<OfflineBanner>", () => {
|
||||
});
|
||||
|
||||
it("ships the message key in every supported locale", () => {
|
||||
for (const locale of ["en", "de", "fr", "es", "it", "pl", "ko"] as const) {
|
||||
for (const locale of [
|
||||
"en",
|
||||
"de",
|
||||
"fr",
|
||||
"es",
|
||||
"it",
|
||||
"pl",
|
||||
"ko",
|
||||
"nl",
|
||||
] as const) {
|
||||
const m = loadMessages(locale);
|
||||
expect(m.offlineBanner.message.length, locale).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { formatPeriod, parsePeriodQuery } from "../period-query";
|
||||
|
||||
const NOW = new Date("2026-10-15T12:00:00.000Z");
|
||||
const LOCALES = ["de", "en", "es", "fr", "it", "pl", "ko"] as const;
|
||||
const LOCALES = ["de", "en", "es", "fr", "it", "pl", "ko", "nl"] as const;
|
||||
|
||||
function window(term: string, locale: string) {
|
||||
const parsed = parsePeriodQuery(term, locale, NOW);
|
||||
|
||||
@@ -41,7 +41,7 @@ const REQUIRED_KEYS = [
|
||||
];
|
||||
|
||||
describe("measurement-reminder + bulk-share dynamic i18n keys", () => {
|
||||
it("discovers all seven shipped locales", () => {
|
||||
it("discovers all eight shipped locales", () => {
|
||||
expect(LOCALES.map((l) => l.locale).sort()).toEqual([
|
||||
"de",
|
||||
"en",
|
||||
@@ -49,6 +49,7 @@ describe("measurement-reminder + bulk-share dynamic i18n keys", () => {
|
||||
"fr",
|
||||
"it",
|
||||
"ko",
|
||||
"nl",
|
||||
"pl",
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -72,7 +72,7 @@ export interface NaturalLanguageExtractorProps {
|
||||
* UI locale forwarded to the route as a hint. The route accepts an
|
||||
* undefined value and falls back to the resolver chain.
|
||||
*/
|
||||
locale?: "en" | "de" | "es" | "fr" | "it" | "pl" | "ko";
|
||||
locale?: "en" | "de" | "es" | "fr" | "it" | "pl" | "ko" | "nl";
|
||||
/**
|
||||
* Optional override of the API path so the wizard test harness can
|
||||
* point at a mock server. Defaults to the production route.
|
||||
|
||||
@@ -108,7 +108,7 @@ describe("<ImportPanel> — SSR smoke", () => {
|
||||
});
|
||||
|
||||
describe("<AppleHealthEstimateWarning>", () => {
|
||||
it.each(["en", "de", "es", "fr", "it", "pl", "ko"] as const)(
|
||||
it.each(["en", "de", "es", "fr", "it", "pl", "ko", "nl"] as const)(
|
||||
"renders localized estimate disclosure for %s",
|
||||
(locale) => {
|
||||
const html = render(<AppleHealthEstimateWarning days={3} />, locale);
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
ManagedRecordSettingsForm,
|
||||
} from "../managed-record-settings-section";
|
||||
|
||||
const LOCALES = ["de", "en", "es", "fr", "it", "pl", "ko"] as const;
|
||||
const LOCALES = ["de", "en", "es", "fr", "it", "pl", "ko", "nl"] as const;
|
||||
|
||||
const render = (
|
||||
family: Parameters<typeof ManagedRecordSettingsForm>[0]["family"],
|
||||
|
||||
@@ -285,12 +285,12 @@ describe("the managed-profile card, in the shared-access section", () => {
|
||||
}
|
||||
// The timezone control is the shared picker, which labels itself.
|
||||
expect(html).toContain('id="managed-profile-timezone"');
|
||||
// Six languages offered, which is the route's enum exactly.
|
||||
// Eight languages offered, which is the route's enum exactly.
|
||||
const options =
|
||||
/<select[^>]*data-slot="managed-profile-locale"[\s\S]*?<\/select>/
|
||||
.exec(html)?.[0]
|
||||
.match(/<option/g) ?? [];
|
||||
expect(options).toHaveLength(7);
|
||||
expect(options).toHaveLength(8);
|
||||
// Four sex options, and the first is the empty one: null is a real answer
|
||||
// ("not recorded") rather than an absent field, and a form offering only
|
||||
// the three enum members would make it unsayable.
|
||||
|
||||
@@ -215,7 +215,7 @@ describe("<AppleHealthCard>", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it.each(["en", "de", "es", "fr", "it", "pl", "ko"] as const)(
|
||||
it.each(["en", "de", "es", "fr", "it", "pl", "ko", "nl"] as const)(
|
||||
"renders localized Apple Health copy for %s without leaking keys",
|
||||
(locale) => {
|
||||
statusPayload = { lastSyncedAt: null, health: null };
|
||||
|
||||
@@ -13,6 +13,7 @@ import es from "../../../../../messages/es.json";
|
||||
import fr from "../../../../../messages/fr.json";
|
||||
import itMessages from "../../../../../messages/it.json";
|
||||
import ko from "../../../../../messages/ko.json";
|
||||
import nl from "../../../../../messages/nl.json";
|
||||
import pl from "../../../../../messages/pl.json";
|
||||
|
||||
import {
|
||||
@@ -31,6 +32,7 @@ const CATALOGS: Record<Locale, unknown> = {
|
||||
fr,
|
||||
it: itMessages,
|
||||
pl,
|
||||
nl,
|
||||
ko,
|
||||
};
|
||||
|
||||
|
||||
@@ -411,7 +411,7 @@ function ProfileSettingsForm({
|
||||
<option value="">
|
||||
{t("settings.sharedRecord.managedSettings.profile.systemDefault")}
|
||||
</option>
|
||||
{["de", "en", "es", "fr", "it", "pl", "ko"].map((locale) => (
|
||||
{["de", "en", "es", "fr", "it", "pl", "ko", "nl"].map((locale) => (
|
||||
<option key={locale} value={locale}>
|
||||
{locale}
|
||||
</option>
|
||||
|
||||
@@ -34,7 +34,7 @@ import { TimelineLegend } from "../timeline-view";
|
||||
import { useSeriesValueFormat } from "../use-series-value-format";
|
||||
import { item, wordsIn } from "./timeline-fixture";
|
||||
|
||||
const LOCALES: Locale[] = ["de", "en", "es", "fr", "it", "pl", "ko"];
|
||||
const LOCALES: Locale[] = ["de", "en", "es", "fr", "it", "pl", "ko", "nl"];
|
||||
|
||||
function render(node: React.ReactElement, locale: Locale = "en") {
|
||||
return renderToStaticMarkup(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { de, enUS, es, fr, it, ko, pl } from "date-fns/locale";
|
||||
import { de, enUS, es, fr, it, ko, nl, pl } from "date-fns/locale";
|
||||
import type { Locale as DateFnsLocale } from "date-fns";
|
||||
|
||||
import type { Locale } from "@/lib/i18n/config";
|
||||
@@ -6,7 +6,7 @@ import type { Locale } from "@/lib/i18n/config";
|
||||
/**
|
||||
* Map the app locale → the date-fns locale so the calendar's month / weekday
|
||||
* names match the UI language. Defaults to enUS for anything unmapped.
|
||||
* Imported only by the calendars, which load on demand, so the seven
|
||||
* Imported only by the calendars, which load on demand, so the eight
|
||||
* locales never ride a page's first load.
|
||||
*/
|
||||
export function resolveDateFnsLocale(locale: Locale): DateFnsLocale {
|
||||
@@ -23,6 +23,8 @@ export function resolveDateFnsLocale(locale: Locale): DateFnsLocale {
|
||||
return pl;
|
||||
case "ko":
|
||||
return ko;
|
||||
case "nl":
|
||||
return nl;
|
||||
default:
|
||||
return enUS;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user