Skip to content

Commit d1e444d

Browse files
committed
feat: warn on missing translations instead of errors in the dev mode
1 parent 01ee1f0 commit d1e444d

3 files changed

Lines changed: 27 additions & 18 deletions

File tree

cmp/compiler/src/plugin/dev-server-loader.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ export default async function devServerLoader(
1717
logger.debug("Running devServerLoader");
1818
const callback = this.async();
1919
const isDev = process.env.NODE_ENV === "development";
20+
if (!isDev) {
21+
return;
22+
}
2023

2124
const config: LingoConfig = this.getOptions();
2225
const startPort = config.dev.serverStartPort;
2326

24-
if (isDev && !serverPromise) {
27+
if (!serverPromise) {
2528
serverPromise = startOrGetTranslationServer({
2629
startPort,
2730
onError: (err) => {
@@ -36,7 +39,7 @@ export default async function devServerLoader(
3639

3740
// Wait for server with timeout to prevent compilation from hanging
3841
const server = await withTimeout(
39-
serverPromise!,
42+
serverPromise,
4043
DEFAULT_TIMEOUTS.SERVER_START,
4144
"Translation server startup",
4245
);

cmp/compiler/src/react/server-only/translations.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { readFile } from "fs/promises";
1212
import { join } from "path";
1313
import { logger } from "../../utils/logger";
1414
import { fetchTranslations as fetchFromDevServer } from "../shared/utils";
15-
import { serverUrl, cacheDir } from "@lingo.dev/compiler/dev-config";
15+
import { cacheDir, serverUrl } from "@lingo.dev/compiler/dev-config";
1616

1717
/**
1818
* Configuration for translation fetching
@@ -112,7 +112,14 @@ export async function fetchTranslationsOnServer(
112112
logger.debug(
113113
`Server. Fetching translations for ${locale} and ${hashes.join(", ")} from dev server (${serverUrl})`,
114114
);
115-
return await fetchFromDevServer(locale, hashes, serverUrl);
115+
try {
116+
return await fetchFromDevServer(locale, hashes, serverUrl);
117+
} catch (error) {
118+
logger.warn(
119+
`Failed to fetch translations from translation server: ${error}.`,
120+
);
121+
return {};
122+
}
116123
}
117124

118125
logger.debug(`Reading translations for ${locale} from filesystem`);

cmp/compiler/src/react/shared/TranslationContext.tsx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"use client";
22

3-
// TODO (AleksandrSl 21/11/2025): I think there should be a better type
4-
import type { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";
53
import {
64
createContext,
75
type ReactNode,
@@ -105,7 +103,7 @@ export interface TranslationProviderProps {
105103
* If provided, calls router.refresh() after locale change
106104
* This ensures Server Components re-render with new locale
107105
*/
108-
router?: AppRouterInstance;
106+
router?: { refresh: () => void };
109107

110108
/**
111109
* Development widget configuration
@@ -303,16 +301,10 @@ function TranslationProvider__Dev({
303301
useState<Record<string, string>>(initialTranslations);
304302
const [isLoading, setIsLoading] = useState(false);
305303

306-
// Track registered hashes from components (updated every render)
307-
const registeredHashesRef = useRef<Set<string>>(new Set());
308-
309-
// Track all hashes that have been seen (for hot reload detection)
310304
const [allSeenHashes, setAllSeenHashes] = useState<Set<string>>(new Set());
311-
312-
// Track which hashes are pending translation request
305+
const registeredHashesRef = useRef<Set<string>>(new Set());
313306
const pendingHashesRef = useRef<Set<string>>(new Set());
314-
315-
// Batch timer reference
307+
const erroredHashesRef = useRef<Set<string>>(new Set());
316308
const batchTimerRef = useRef<NodeJS.Timeout | null>(null);
317309

318310
// Use ref to track translations to avoid stale closures
@@ -379,9 +371,12 @@ function TranslationProvider__Dev({
379371
[...allSeenHashes.values()],
380372
[...pendingHashesRef.current.values()],
381373
);
382-
logger.debug("translationsRef.current: ", translations);
383374
for (const hash of allSeenHashes) {
384-
if (!translations[hash] && !pendingHashesRef.current.has(hash)) {
375+
if (
376+
!translations[hash] &&
377+
!pendingHashesRef.current.has(hash) &&
378+
!erroredHashesRef.current.has(hash)
379+
) {
385380
missingHashes.push(hash);
386381
pendingHashesRef.current.add(hash);
387382
}
@@ -425,10 +420,13 @@ function TranslationProvider__Dev({
425420
registeredHashesRef.current.add(hash);
426421
}
427422
} catch (error) {
428-
logger.error("Failed to fetch translations:", error);
423+
logger.warn(
424+
`Failed to fetch translations from translation server: ${error}.`,
425+
);
429426
// Remove from pending so they can be retried
430427
for (const hash of hashesToFetch) {
431428
pendingHashesRef.current.delete(hash);
429+
erroredHashesRef.current.add(hash);
432430
}
433431
} finally {
434432
setIsLoading(false);
@@ -470,6 +468,7 @@ function TranslationProvider__Dev({
470468
try {
471469
logger.info(`Fetching translations for locale: ${newLocale}`);
472470

471+
// TODO (AleksandrSl 08/12/2025): We should be fetching the existing cached translations here.
473472
const translatedDict = await fetchTranslations(
474473
newLocale,
475474
[],

0 commit comments

Comments
 (0)