Skip to content

Commit 3b29435

Browse files
committed
fix: do not copy extra translations to the build
1 parent fc1e7e3 commit 3b29435

4 files changed

Lines changed: 98 additions & 94 deletions

File tree

cmp/compiler/README.md

Lines changed: 56 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -28,75 +28,81 @@ pnpm add @lingo.dev/compiler
2828
yarn add @lingo.dev/compiler
2929
```
3030

31-
## Quick Start
31+
## Structure
3232

33-
## Usage with Turbopack (Next.js 16+)
33+
The compiler is organized into several key modules:
3434

35-
### 1. Configure Next.js
35+
### Core Directories
3636

37-
[//]: # ( TODO (AleksandrSl 12/12/2025):
37+
#### `src/plugin/` - Build-time transformation
3838

39-
### 2. (Optional) Use directive mode
39+
- **`transform/`** - Babel AST transformation logic for JSX text extraction
40+
- **`unplugin.ts`** - Universal plugin implementation (Vite, Webpack, Rollup, esbuild)
41+
- **`next.ts`** - Next.js-specific plugin with Turbopack and webpack support
42+
- **`build-translator.ts`** - Batch translation generation at build time
43+
- **`locale-code-generator.ts`** - Generates locale resolver modules
4044

41-
If you set `useDirective: true`, add the directive to files you want to translate:
45+
#### `src/metadata/` - Translation metadata management
4246

43-
```tsx
44-
"use i18n";
47+
- **`manager.ts`** - CRUD operations for `.lingo/metadata.json`
48+
- Thread-safe metadata file operations with locking
49+
- Manages translation entries with hash-based identifiers
4550

46-
export function MyComponent() {
47-
return <div>This text will be translated</div>;
48-
}
49-
```
51+
#### `src/translators/` - Translation provider abstraction
5052

51-
### 3. Write components normally
52-
53-
```tsx
54-
// src/components/Welcome.tsx
55-
export function Welcome() {
56-
return (
57-
<div>
58-
<h1>Welcome to our site</h1>
59-
<p>This text will be automatically translated</p>
60-
</div>
61-
);
62-
}
63-
```
53+
- **`lcp/`** - Lingo.dev Engine integration
54+
- **`pseudotranslator/`** - Development-mode fake translator
55+
- **`pluralization/`** - Automatic ICU MessageFormat detection
56+
- **`translator-factory.ts`** - Provider selection and initialization
6457

65-
### 4. Build and see transformations
58+
#### `src/translation-server/` - Development server
6659

67-
After build, the code will be transformed to:
60+
- **`translation-server.ts`** - HTTP server for on-demand translations
61+
- **`cli.ts`** - Standalone CLI for translation generation
62+
- WebSocket support for real-time dev widget updates
63+
- Port management (60000-60099 range)
6864

69-
```tsx
70-
import { useTranslation } from "@lingo.dev/runtime";
65+
#### `src/react/` - Runtime translation hooks
7166

72-
export function Welcome() {
73-
const t = useTranslation();
74-
return (
75-
<div>
76-
<h1>{t("a1b2c3d4e5f6")}</h1>
77-
<p>{t("f6e5d4c3b2a1")}</p>
78-
</div>
79-
);
80-
}
81-
```
67+
- **`client/`** - Client-side Context-based hooks
68+
- **`server/`** - Server component cache-based hooks (isomorphic)
69+
- **`server-only/`** - Async server-only API (`getServerTranslations`)
70+
- **`shared/`** - Shared utilities (RichText rendering, Context)
71+
- **`next/`** - Next.js-specific middleware and locale switcher
72+
73+
#### `src/utils/` - Shared utilities
74+
75+
- **`hash.ts`** - Stable hash generation for translation keys
76+
- **`config-factory.ts`** - Configuration defaults and merging
77+
- **`logger.ts`** - Structured logging utilities
78+
- **`path-helpers.ts`** - File path resolution
79+
80+
#### `src/widget/` - Development widget
81+
82+
- In-browser translation editor overlay for development mode
83+
84+
### Support Directories
85+
86+
#### `tests/` - End-to-end testing
8287

83-
## How It Works
88+
- **`e2e/`** - Playwright tests for full build workflows
89+
- **`fixtures/`** - Test applications (Vite, Next.js)
90+
- **`helpers/`** - Test utilities and assertions
8491

85-
[//]: # ( TODO (AleksandrSl 12/12/2025):
92+
#### `benchmarks/` - Performance benchmarks
8693

87-
## Limitations (Current Version)
94+
- Translation speed benchmarks
95+
- Metadata I/O performance tests
8896

89-
- Requires manual runtime setup (TranslationProvider, etc.)
97+
#### `old-docs/` - Legacy documentation
9098

91-
## Supported Bundlers
99+
- Historical design documents and notes
92100

93-
| Bundler | Status | Import Path |
94-
| ------- | --------------- | ----------------------------- |
95-
| Vite | ✅ Full Support | `@lingo.dev/compiler/vite` |
96-
| Webpack | ✅ Full Support | `@lingo.dev/compiler/webpack` |
97-
| Next.js | ✅ Full Support | `@lingo.dev/compiler/next` |
101+
### Entry Points
98102

99-
All bundler plugins share the same configuration API.
103+
- **`src/index.ts`** - Main package exports (plugins, types)
104+
- **`src/types.ts`** - Core TypeScript types
105+
- **`src/dev-config.ts`** - Development configuration utilities
100106

101107
## Contributing
102108

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

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export async function processBuildTranslations(
8989
logger.info("✅ Cache validation passed");
9090

9191
if (publicOutputPath) {
92-
await copyStaticFiles(config, publicOutputPath);
92+
await copyStaticFiles(config, publicOutputPath, metadata);
9393
}
9494

9595
return {
@@ -161,7 +161,7 @@ export async function processBuildTranslations(
161161

162162
// Copy cache to public directory if requested
163163
if (publicOutputPath) {
164-
await copyStaticFiles(config, publicOutputPath);
164+
await copyStaticFiles(config, publicOutputPath, metadata);
165165
}
166166

167167
logger.info("✅ Translation generation completed successfully");
@@ -269,11 +269,15 @@ function buildCacheStats(
269269
async function copyStaticFiles(
270270
config: LingoConfig,
271271
publicOutputPath: string,
272+
metadata: MetadataSchema,
272273
): Promise<void> {
273274
logger.info(`📦 Generating static translation files in ${publicOutputPath}`);
274275

275276
await fs.mkdir(publicOutputPath, { recursive: true });
276277

278+
const usedHashes = new Set(Object.keys(metadata.entries));
279+
logger.info(`📊 Filtering translations to ${usedHashes.size} used hash(es)`);
280+
277281
// Include source locale if pluralization is enabled
278282
const needsSourceLocale = config.pluralization?.enabled !== false;
279283
const allLocales = needsSourceLocale
@@ -285,10 +289,42 @@ async function copyStaticFiles(
285289
const publicFilePath = path.join(publicOutputPath, `${locale}.json`);
286290

287291
try {
288-
await fs.copyFile(cacheFilePath, publicFilePath);
289-
logger.info(`✓ Generated ${locale}.json`);
292+
const cacheContent = await fs.readFile(cacheFilePath, "utf-8");
293+
const cache = JSON.parse(cacheContent);
294+
295+
const filteredEntries: Record<string, string> = {};
296+
let includedCount = 0;
297+
let skippedCount = 0;
298+
299+
for (const [hash, translation] of Object.entries(
300+
cache.entries as Record<string, string>,
301+
)) {
302+
if (usedHashes.has(hash)) {
303+
filteredEntries[hash] = translation;
304+
includedCount++;
305+
} else {
306+
skippedCount++;
307+
}
308+
}
309+
310+
// Write filtered translations
311+
const outputData = {
312+
locale,
313+
entries: filteredEntries,
314+
version: cache.version,
315+
};
316+
317+
await fs.writeFile(
318+
publicFilePath,
319+
JSON.stringify(outputData, null, 2),
320+
"utf-8",
321+
);
322+
323+
logger.info(
324+
`✓ Generated ${locale}.json (${includedCount} translations, ${skippedCount} unused skipped)`,
325+
);
290326
} catch (error) {
291-
logger.error(`❌ Failed to copy ${locale}.json:`, error);
327+
logger.error(`❌ Failed to generate ${locale}.json:`, error);
292328
process.exit(1);
293329
}
294330
}

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

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,9 @@ export interface TransformResult {
1212
transformed: boolean;
1313
}
1414

15-
/**
16-
* Options for the Babel transformation
17-
*/
1815
export interface BabelTransformOptions {
19-
/**
20-
* Source code to transform
21-
*/
2216
code: string;
23-
24-
/**
25-
* File path being transformed
26-
*/
2717
filePath: string;
28-
29-
/**
30-
* Loader configuration
31-
*/
3218
config: LingoConfig;
3319
}
3420

@@ -86,27 +72,3 @@ export function transformComponent({
8672
};
8773
}
8874
}
89-
90-
/**
91-
* Check if a file should be transformed
92-
*/
93-
export function shouldTransformFile(
94-
filePath: string,
95-
config: LingoConfig,
96-
): boolean {
97-
// Only transform .tsx and .jsx files
98-
if (!filePath.match(/\.(tsx|jsx)$/)) {
99-
return false;
100-
}
101-
102-
// Check skip patterns
103-
if (config.skipPatterns) {
104-
for (const pattern of config.skipPatterns) {
105-
if (pattern.test(filePath)) {
106-
return false;
107-
}
108-
}
109-
}
110-
111-
return true;
112-
}

cmp/compiler/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface CookieConfig {
2828
export type LocalePersistenceConfig = { type: "cookie"; cookieName?: string };
2929

3030
/**
31-
* Field that we require users to fill in in the config. The rest could be taken from defaults.
31+
* Field that we require users to fill in the config. The rest could be taken from defaults.
3232
*/
3333
export type LingoConfigRequiredFields = "sourceLocale" | "targetLocales";
3434

0 commit comments

Comments
 (0)