Skip to content

Commit e0c6e3f

Browse files
committed
chore:bunch of cleanups
1 parent 7fe6df0 commit e0c6e3f

26 files changed

Lines changed: 168 additions & 271 deletions

cmp/compiler/src/__test-utils__/mocks.ts

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import type { LingoConfig } from "../types";
77
import { createLingoConfig } from "../utils/config-factory";
8-
import type { DictionarySchema } from "../translators";
98

109
/**
1110
* Create a mock loader config for testing
@@ -30,62 +29,3 @@ export function createMockConfig(
3029
...overrides,
3130
});
3231
}
33-
34-
/**
35-
* Create a mock dictionary for testing
36-
*
37-
* @param entries - Hash-to-translation mapping
38-
* @param locale - Target locale (default: "en")
39-
* @param version - Schema version (default: 0.1)
40-
* @returns Complete DictionarySchema
41-
*
42-
* @example
43-
* ```typescript
44-
* const dict = createMockDictionary({
45-
* "abc123": "Hello",
46-
* "def456": "World"
47-
* }, "de");
48-
* ```
49-
*/
50-
export function createMockDictionary(
51-
entries: Record<string, string> = {},
52-
locale: string = "en",
53-
version: number = 0.1,
54-
): DictionarySchema {
55-
return {
56-
version,
57-
locale,
58-
entries,
59-
};
60-
}
61-
62-
/**
63-
* Create a mock translator for testing
64-
* Returns a simple translator that prefixes all translations with the locale
65-
*
66-
* @param locale - Default locale (unused, kept for backward compatibility)
67-
* @returns Mock translator object
68-
*
69-
* @example
70-
* ```typescript
71-
* const translator = createMockTranslator();
72-
* const result = await translator.translate("de", {
73-
* hash1: { text: "Hello", context: {} }
74-
* });
75-
* // -> { hash1: "[de] Hello" }
76-
* ```
77-
*/
78-
export function createMockTranslator(locale: string = "en") {
79-
return {
80-
translate: async (
81-
targetLocale: string,
82-
entries: Record<string, { text: string; context: Record<string, any> }>,
83-
) => {
84-
const result: Record<string, string> = {};
85-
for (const [hash, { text }] of Object.entries(entries)) {
86-
result[hash] = `[${targetLocale}] ${text}`;
87-
}
88-
return result;
89-
},
90-
};
91-
}

cmp/compiler/src/locale/client.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import type { LocaleCode } from "lingo.dev/spec";
2+
13
/**
24
* Get the current locale on the client
35
* Reads from cookie
46
* @returns Resolved locale code
57
*/
6-
export function getClientLocale(): string {
8+
export function getClientLocale(): LocaleCode {
79
return "en";
810
}
911

@@ -14,6 +16,6 @@ const __NOOP_PERSIST_LOCALE__ = () => {};
1416
* Writes to cookie
1517
* @param locale - Locale code to persist
1618
*/
17-
export function persistLocale(locale: string): void {
19+
export function persistLocale(locale: LocaleCode): void {
1820
return __NOOP_PERSIST_LOCALE__();
1921
}

cmp/compiler/src/locale/server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import type { LocaleCode } from "lingo.dev/spec";
2+
13
/**
24
* Get the current locale on the server
35
* Uses cookies, headers, or other server-side mechanisms
46
* @returns Resolved locale code
57
*/
6-
export async function getServerLocale(): Promise<string> {
8+
export async function getServerLocale(): Promise<LocaleCode> {
79
return "en";
810
}

cmp/compiler/src/plugin/build-translator.ts

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
} from "../translation-server";
1818
import { loadMetadata } from "../metadata/manager";
1919
import { createCache, type TranslationCache } from "../translators";
20+
import { dictionaryFrom } from "../translators/api";
21+
import type { LocaleCode } from "lingo.dev/spec";
2022

2123
export interface BuildTranslationOptions {
2224
config: LingoConfig;
@@ -125,7 +127,7 @@ export async function processBuildTranslations(
125127
);
126128

127129
const stats: BuildTranslationResult["stats"] = {};
128-
const errors: Array<{ locale: string; error: string }> = [];
130+
const errors: Array<{ locale: LocaleCode; error: string }> = [];
129131

130132
// Translate all locales in parallel
131133
const localePromises = allLocales.map(async (locale) => {
@@ -195,7 +197,7 @@ async function validateCache(
195197
const allHashes = Object.keys(metadata.entries);
196198
const missingLocales: string[] = [];
197199
const incompleteLocales: Array<{
198-
locale: string;
200+
locale: LocaleCode;
199201
missing: number;
200202
total: number;
201203
}> = [];
@@ -295,36 +297,8 @@ async function copyStaticFiles(
295297
const publicFilePath = path.join(publicOutputPath, `${locale}.json`);
296298

297299
try {
298-
// TODO (AleksandrSl 14/12/2025): Probably make it required?
299-
// Use getDictionary if available (for LocalTranslationCache), otherwise use get
300-
const dictionary = cache.getDictionary
301-
? await cache.getDictionary(locale)
302-
: null;
303-
304-
if (!dictionary) {
305-
logger.error(`❌ Failed to read cache for ${locale}`);
306-
process.exit(1);
307-
}
308-
309-
const filteredEntries: Record<string, string> = {};
310-
let includedCount = 0;
311-
let skippedCount = 0;
312-
313-
for (const [hash, translation] of Object.entries(dictionary.entries)) {
314-
if (usedHashes.has(hash)) {
315-
filteredEntries[hash] = translation;
316-
includedCount++;
317-
} else {
318-
skippedCount++;
319-
}
320-
}
321-
322-
// Write filtered translations
323-
const outputData = {
324-
locale,
325-
entries: filteredEntries,
326-
version: dictionary.version,
327-
};
300+
const entries = await cache.get(locale, Array.from(usedHashes));
301+
const outputData = dictionaryFrom(locale, entries);
328302

329303
await fs.writeFile(
330304
publicFilePath,
@@ -333,7 +307,7 @@ async function copyStaticFiles(
333307
);
334308

335309
logger.info(
336-
`✓ Generated ${locale}.json (${includedCount} translations, ${skippedCount} unused skipped)`,
310+
`✓ Generated ${locale}.json (${Object.keys(entries).length} translations)`,
337311
);
338312
} catch (error) {
339313
logger.error(`❌ Failed to generate ${locale}.json:`, error);
@@ -344,7 +318,11 @@ async function copyStaticFiles(
344318

345319
function formatCacheValidationError(
346320
missingLocales: string[],
347-
incompleteLocales: Array<{ locale: string; missing: number; total: number }>,
321+
incompleteLocales: Array<{
322+
locale: LocaleCode;
323+
missing: number;
324+
total: number;
325+
}>,
348326
): string {
349327
let msg = "❌ Cache validation failed in cache-only mode:\n\n";
350328

@@ -374,7 +352,7 @@ function formatCacheValidationError(
374352
}
375353

376354
function formatTranslationErrors(
377-
errors: Array<{ locale: string; error: string }>,
355+
errors: Array<{ locale: LocaleCode; error: string }>,
378356
): string {
379357
let msg = "❌ Translation generation failed:\n\n";
380358

cmp/compiler/src/plugin/next-compiler-loader.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ export default async function nextCompilerLoader(
1515
this: any,
1616
source: string,
1717
): Promise<void> {
18-
// TODO (AleksandrSl 14/12/2025): Webpack doesn't like callback usage in async function. But asycn function can return only code, so we have to use promises which is sad.
18+
// TODO (AleksandrSl 14/12/2025): Webpack doesn't like callback usage in async function.
19+
// But async function can return only code, so we have to use promises which is sad. It actually errors only when we catch an error, so it's not an urgent problem
20+
1921
// Ensure we're running in loader context
2022
if (typeof this.async !== "function") {
2123
throw new Error("This module must be run as a loader");

cmp/compiler/src/plugin/transform/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ export function createTranslationEntry<T extends keyof TranslationEntryByType>(
133133
},
134134
overrides:
135135
overrides && Object.keys(overrides).length > 0 ? overrides : undefined,
136-
// TODO (AleksandrSl 08/12/2025): Figure out why it doesn't work without the cast
136+
// Seems like the only approach without the cast is function overloads, which are noisy. The type cast is not that bad since it's inside the function.
137137
} as TranslationEntryByType[T];
138138
}
139139

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type { ReactNode } from "react";
1717
import { logger } from "../../utils/logger";
1818
// Keep this import full for replacement during build.
1919
import { getServerLocale } from "@lingo.dev/compiler/locale/server";
20+
import type { LocaleCode } from "lingo.dev/spec";
2021

2122
/**
2223
* Get server-side translations function
@@ -31,7 +32,7 @@ export async function getServerTranslations(options: {
3132
/**
3233
* Target locale for translations (required)
3334
*/
34-
locale?: string;
35+
locale?: LocaleCode;
3536

3637
/**
3738
* List of translation hashes needed for this component
@@ -50,7 +51,7 @@ export async function getServerTranslations(options: {
5051
sourceText: string,
5152
params?: RichTextParams,
5253
) => string | ReactNode;
53-
locale: string;
54+
locale: LocaleCode;
5455
translations: Record<string, string>;
5556
}> {
5657
const locale = options.locale || (await getServerLocale());

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { join } from "path";
1313
import { logger } from "../../utils/logger";
1414
import { fetchTranslations as fetchFromDevServer } from "../shared/utils";
1515
import { cacheDir, serverUrl } from "@lingo.dev/compiler/dev-config";
16+
import type { LocaleCode } from "lingo.dev/spec";
1617

1718
/**
1819
* Configuration for translation fetching
@@ -37,7 +38,7 @@ export interface TranslationFetchConfig {
3738
* @returns Translation dictionary (hash -> translated text)
3839
*/
3940
async function readFromFilesystem(
40-
locale: string,
41+
locale: LocaleCode,
4142
basePath: string = process.cwd(),
4243
): Promise<Record<string, string>> {
4344
// TODO (AleksandrSl 02/12/2025): Sanity check. We need to try loading the most up to date translations first. Fo the dev mode they are in lingo. Gor build they are in next.
@@ -101,7 +102,7 @@ async function readFromFilesystem(
101102
* ```
102103
*/
103104
export async function fetchTranslationsOnServer(
104-
locale: string,
105+
locale: LocaleCode,
105106
hashes: string[],
106107
config: TranslationFetchConfig = {},
107108
): Promise<Record<string, string>> {

cmp/compiler/src/react/shared/LingoContext.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
import { createContext, useContext } from "react";
2+
import type { LocaleCode } from "lingo.dev/spec";
23

34
interface LingoContextType {
5+
/**
6+
* Source locale (default language)
7+
*/
8+
sourceLocale: LocaleCode;
9+
410
/**
511
* Current locale (e.g., 'en', 'de', 'fr')
612
*/
7-
locale: string;
13+
locale: LocaleCode;
814

915
/**
1016
* Change the current locale and dynamically load translations
1117
*/
12-
setLocale: (locale: string) => Promise<void>;
18+
setLocale: (locale: LocaleCode) => Promise<void>;
1319

1420
/**
1521
* Translation dictionary: hash -> translated text
@@ -27,11 +33,6 @@ interface LingoContextType {
2733
*/
2834
isLoading: boolean;
2935

30-
/**
31-
* Source locale (default language)
32-
*/
33-
sourceLocale: string;
34-
3536
/**
3637
* Development statistics (only in dev mode)
3738
*/

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
persistLocale,
1717
} from "@lingo.dev/compiler/locale/client";
1818
import { LingoContext } from "./LingoContext";
19+
import type { LocaleCode } from "lingo.dev/spec";
1920

2021
const noop = () => {};
2122

@@ -26,12 +27,12 @@ export interface LingoProviderProps {
2627
/**
2728
* Initial locale to use
2829
*/
29-
initialLocale?: string;
30+
initialLocale?: LocaleCode;
3031

3132
/**
3233
* Source locale (default language)
3334
*/
34-
sourceLocale?: string;
35+
sourceLocale?: LocaleCode;
3536

3637
/**
3738
* Initial translations (pre-loaded)
@@ -97,13 +98,14 @@ export const LingoProvider = IS_DEV ? LingoProvider__Dev : LingoProvider__Prod;
9798

9899
function LingoProvider__Prod({
99100
initialLocale,
101+
// TODO (AleksandrSl 14/12/2025): Remove sourceLocale here.
100102
sourceLocale = "en",
101103
initialTranslations = {},
102104
router,
103105
children,
104106
}: LingoProviderProps) {
105107
// Use client locale detection if no initialLocale provided
106-
const [locale, setLocaleState] = useState(() => {
108+
const [locale, setLocaleState] = useState<LocaleCode>(() => {
107109
if (initialLocale) return initialLocale;
108110
// Only detect on client-side (not during SSR)
109111
if (typeof window !== "undefined") {
@@ -135,7 +137,7 @@ function LingoProvider__Prod({
135137
* Lazy loads on-demand for SPAs
136138
*/
137139
const loadTranslations = useCallback(
138-
async (targetLocale: string) => {
140+
async (targetLocale: LocaleCode) => {
139141
// If we already have initialTranslations (Next.js SSR), don't fetch
140142
if (Object.keys(initialTranslations).length > 0) {
141143
return;
@@ -189,7 +191,7 @@ function LingoProvider__Prod({
189191
* - For SPAs: lazy loads translations from /translations/{locale}.json
190192
*/
191193
const setLocale = useCallback(
192-
async (newLocale: string) => {
194+
async (newLocale: LocaleCode) => {
193195
// 1. Persist to cookie so server can read it on next render
194196
persistLocale(newLocale);
195197

@@ -391,7 +393,7 @@ function LingoProvider__Dev({
391393
* Change locale and load translations dynamically
392394
*/
393395
const setLocale = useCallback(
394-
async (newLocale: string) => {
396+
async (newLocale: LocaleCode) => {
395397
// 1. Persist to cookie (unless disabled)
396398
persistLocale(newLocale);
397399

0 commit comments

Comments
 (0)