Skip to content

Commit a16f982

Browse files
committed
chore: rename modules to have logic names and also lower name conflicts
1 parent 59bd084 commit a16f982

20 files changed

+127
-126
lines changed

cmp/compiler/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ The compiler is organized into several key modules:
102102

103103
- **`src/index.ts`** - Main package exports (plugins, types)
104104
- **`src/types.ts`** - Core TypeScript types
105-
- **`src/dev-config.ts`** - Development configuration utilities
106105

107106
## Contributing
108107

cmp/compiler/Requirements.md

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
1-
# Translation compiler requirements
1+
## Requirements
22

3-
## Overview of the process
3+
1. The core thing is DX. We want compiler to work as seamless as possible.
4+
2. It should work at least with turbopack (since it's next default bundler), webpack and vite.
5+
3. HMR should work, whenever we update the code the state of the app should be kept as it owuld be without the compiler.
6+
4. There should be a way to override the translations
7+
5. We should be able to translate the attributes, the most obvious ones are alt and aria-label, aria-description and
8+
such, but we may also add a way to translate other string attributes too.
9+
6. We should keep translations calls to the minimum.
10+
7. We start with react, but it would be nice to apply the same approach to other frameworks. Like vue and astro. So the
11+
system should be flexible.
12+
8. The translations should be possible to commit in the repo, so we can version them. Maybe we can also consider
13+
conforming to the existing standard?
414

5-
### Dev mode
15+
## Overall architecture
616

7-
- When the app is started we should extract translatable text from the code and inject our translation functions.
8-
- When the user changes the code, we should re-extract the text and update the translation functions.
9-
- If a non source language is used (e.g. sources are in english, and user switches to the german) we want to start translations and update the app when they are ready.
17+
Given that we still may want translations in the dev mode, but we want to keep them to minimum and do not interfere with
18+
the development flow, we may consider the following approach:
1019

11-
### Build mode
20+
1. Do not translate the text unless explicitly requested.
21+
2. Allow using pseudolocalization for even more simple checks. It shows which part of the UI can be translated by
22+
compiler, and also using varying width of the text highlights part of the interface not ready for varying languages.
1223

13-
- During build we need to extract all the translatable text from the code and inject our translation functions.
14-
- While the build is in progress, we should perform the translation and create files with translations.
15-
- When the build ends we should make sure that all the translations are in place.
16-
- Ideally split translations into multiple chunks, according to the js chunks maybe.
24+
This leads us to the question, how to lazily request the translations in the dev mode.
25+
Let's work with the SPA apps as an example (since SSR apps may have more options).
26+
Both currently rendered components and the locale are client side.
27+
Cache for translations is stored in the filesystem in the project.
28+
So the way to receive a request from the browser to the host that will get the translations and store the cache.
1729

18-
In the end we should have a built app with all the translations in place.
30+
The most straightforward way to do this is to use the usual web server. App will send a request for translations when needed,
31+
server components can do the same. This adds a bit of latency but keeps the code uniform.
1932

20-
## Problems of the current implementation
33+
With this approach we get couple more questions:
2134

22-
We want to avoid fetching translations if they are not needed. To achieve this, we need to ask for translations if they are missing.
23-
We also want to cache the translations to avoid unnecessary requests, by default, the cache is on the user machine.
24-
But client side components in the browser can't access the user machine to save the cache.
35+
- How to start the server
36+
- How to make the client aware about server's address
37+
- How to batch the calls, both client and server side.
2538

26-
This can be solved by adding separate route/middleware on the user machine that can be called from the client and will serve the translations.
27-
We can use Vite middleware and Next routeHandler to do this.
39+
How to start the server
40+
While it sounds simple, it's not that simple if you don't want to create any external scripts.
41+
We have at least three environments:
2842

29-
- The approaches differ for different bundlers
30-
- We do not want to ask the user to add the extra route handlers to their app.
43+
- Next turbopack: no startup hooks.
44+
- Next webpack: there is compiler.hooks.watchRun. Well, watchRun runs on every script compilation during hot reload.
45+
- Vite: buildStart.
3146

32-
## Possible solution
47+
So next with turbopack is the most complex case.
3348

34-
Use a separate server that will serve the translations. It can be started by the compiler. Then client components can fetch the translations from the server.
49+
It shows which part of the UI can be translated by
50+
compiler - can be done easier? Add some colors

cmp/compiler/package.json

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,20 @@
2929
"default": "./build/react/client/index.cjs"
3030
}
3131
},
32-
"./dev-config": {
33-
"types": "./build/dev-config.d.ts",
34-
"import": "./build/dev-config.mjs",
35-
"require": "./build/dev-config.cjs"
32+
"./virtual/config": {
33+
"types": "./build/virtual/config.d.ts",
34+
"import": "./build/virtual/config.mjs",
35+
"require": "./build/virtual/config.cjs"
3636
},
37-
"./locale/server": {
38-
"types": "./build/locale/server.d.ts",
39-
"import": "./build/locale/server.mjs",
40-
"require": "./build/locale/server.cjs"
37+
"./virtual/locale/server": {
38+
"types": "./build/virtual/locale/server.d.ts",
39+
"import": "./build/virtual/locale/server.mjs",
40+
"require": "./build/virtual/locale/server.cjs"
4141
},
42-
"./locale/client": {
43-
"types": "./build/locale/client.d.ts",
44-
"import": "./build/locale/client.mjs",
45-
"require": "./build/locale/client.cjs"
42+
"./virtual/locale/client": {
43+
"types": "./build/virtual/locale/client.d.ts",
44+
"import": "./build/virtual/locale/client.mjs",
45+
"require": "./build/virtual/locale/client.cjs"
4646
},
4747
"./react/client": {
4848
"types": "./build/react/index.d.ts",
@@ -91,10 +91,10 @@
9191
"import": "./build/plugin/next-compiler-loader.mjs",
9292
"require": "./build/plugin/next-compiler-loader.cjs"
9393
},
94-
"./next-dev-config-loader": {
95-
"types": "./build/plugin/next-dev-config-loader.d.ts",
96-
"import": "./build/plugin/next-dev-config-loader.mjs",
97-
"require": "./build/plugin/next-dev-config-loader.cjs"
94+
"./next-config-loader": {
95+
"types": "./build/plugin/next-config-loader.d.ts",
96+
"import": "./build/plugin/next-config-loader.mjs",
97+
"require": "./build/plugin/next-config-loader.cjs"
9898
},
9999
"./next-locale-server-loader": {
100100
"types": "./build/plugin/next-locale-server-loader.d.ts",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { LingoConfig } from "../types";
2+
import { generateConfigModule } from "../virtual/code-generator";
3+
4+
/**
5+
* Loader for config module
6+
* Generates full module code - ignores source file (template is only for types)
7+
*/
8+
export default function nextConfigLoader(this: any, _source: string): string {
9+
const config: LingoConfig = this.getOptions();
10+
return generateConfigModule(config);
11+
}

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

Lines changed: 0 additions & 14 deletions
This file was deleted.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import type { LingoConfig } from "../types";
7-
import { generateClientLocaleModule } from "./virtual-modules-code-generator";
7+
import { generateClientLocaleModule } from "../virtual/code-generator";
88

99
export default function nextLocaleClientLoader(
1010
this: any,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import type { LingoConfig } from "../types";
7-
import { generateServerLocaleModule } from "./virtual-modules-code-generator";
7+
import { generateServerLocaleModule } from "../virtual/code-generator";
88

99
export default function nextLocaleServerLoader(
1010
this: any,

cmp/compiler/src/plugin/next.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ export function loaders({
4242
},
4343
};
4444

45-
const devConfigLoader = {
46-
loader: "@lingo.dev/compiler/next-dev-config-loader",
45+
const configLoader = {
46+
loader: "@lingo.dev/compiler/next-config-loader",
4747
options: {
4848
...common,
4949
dev: {
@@ -90,43 +90,43 @@ export function loaders({
9090
devConfig: translationServerUrl
9191
? {
9292
turbopack: {
93-
pattern: "**/dev-config.mjs",
93+
pattern: "**/virtual/config.mjs",
9494
config: {
95-
loaders: [devConfigLoader],
95+
loaders: [configLoader],
9696
},
9797
},
9898
webpack: {
9999
enforce: "pre",
100-
test: /dev-config\.mjs$/i,
101-
use: [devConfigLoader],
100+
test: /virtual\/config\.mjs$/i,
101+
use: [configLoader],
102102
},
103103
}
104104
: {},
105105

106106
localeServer: {
107107
turbopack: {
108-
pattern: "**/locale/server.mjs",
108+
pattern: "**/virtual/locale/server.mjs",
109109
config: {
110110
loaders: [localeServerLoader],
111111
},
112112
},
113113
webpack: {
114114
enforce: "pre",
115-
test: /locale[\\/]server\.mjs$/i,
115+
test: /virtual\/locale[\\/]server\.mjs$/i,
116116
use: [localeServerLoader],
117117
},
118118
},
119119

120120
localeClient: {
121121
turbopack: {
122-
pattern: "**/locale/client.mjs",
122+
pattern: "**/virtual/locale/client.mjs",
123123
config: {
124124
loaders: [localeClientLoader],
125125
},
126126
},
127127
webpack: {
128128
enforce: "pre",
129-
test: /locale[\\/]client\.mjs$/i,
129+
test: /virtual\/locale[\\/]client\.mjs$/i,
130130
use: [localeClientLoader],
131131
},
132132
},

cmp/compiler/src/plugin/unplugin.ts

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ import { logger } from "../utils/logger";
1919
import { useI18nRegex } from "./transform/use-i18n";
2020
import {
2121
generateClientLocaleModule,
22-
generateDevConfigModule,
22+
generateConfigModule,
2323
generateServerLocaleModule,
24-
} from "./virtual-modules-code-generator";
24+
} from "../virtual/code-generator";
2525
import { processBuildTranslations } from "./build-translator";
2626
import { registerCleanupOnCurrentProcess } from "./cleanup";
2727
import path from "path";
@@ -52,17 +52,17 @@ function tryLocalOrReturnVirtual(
5252
* If customFileCheck is defined, the specified file will be first searched for, and if not found virtual module will be used.
5353
*/
5454
const virtualModules = {
55-
"@lingo.dev/compiler/dev-config": {
56-
virtualId: "\0virtual:lingo-dev-config",
57-
loader: (config: LingoConfig) => generateDevConfigModule(config),
55+
"@lingo.dev/compiler/virtual/config": {
56+
virtualId: "\0virtual:lingo-config",
57+
loader: (config: LingoConfig) => generateConfigModule(config),
5858
customFileCheck: undefined,
5959
},
60-
"@lingo.dev/compiler/locale/server": {
60+
"@lingo.dev/compiler/virtual/locale/server": {
6161
virtualId: "\0virtual:locale-resolver.server" as const,
6262
loader: (config: LingoConfig) => generateServerLocaleModule(config),
6363
customFileCheck: "locale-resolver.server.ts" as const,
6464
},
65-
"@lingo.dev/compiler/locale/client": {
65+
"@lingo.dev/compiler/virtual/locale/client": {
6666
virtualId: "\0virtual:locale-resolver.client" as const,
6767
loader: (config: LingoConfig) => generateClientLocaleModule(config),
6868
customFileCheck: "locale-resolver.client.ts" as const,
@@ -110,6 +110,27 @@ export const lingoUnplugin = createUnplugin<
110110
);
111111
};
112112

113+
async function startServer() {
114+
const server = await startTranslationServer({
115+
startPort,
116+
onError: (err) => {
117+
logger.error("Translation server error:", err);
118+
},
119+
onReady: (port) => {
120+
logger.info(`Translation server started successfully on port: ${port}`);
121+
},
122+
config,
123+
});
124+
// I don't like this quite a lot. But starting server inside the loader seems lame.
125+
config.dev.translationServerUrl = server.getUrl();
126+
registerCleanupOnCurrentProcess({
127+
asyncCleanup: async () => {
128+
await translationServer.stop();
129+
},
130+
});
131+
return server;
132+
}
133+
113134
return {
114135
name: PLUGIN_NAME,
115136
enforce: "pre", // Run before other plugins (especially before React plugin)
@@ -119,29 +140,12 @@ export const lingoUnplugin = createUnplugin<
119140
const metadataFilePath = getMetadataPath();
120141

121142
cleanupExistingMetadata(metadataFilePath);
122-
123143
registerCleanupOnCurrentProcess({
124144
cleanup: () => cleanupExistingMetadata(metadataFilePath),
125145
});
126-
if (isDev && !translationServer) {
127-
translationServer = await startTranslationServer({
128-
startPort,
129-
onError: (err) => {
130-
logger.error("Translation server error:", err);
131-
},
132-
onReady: (port) => {
133-
logger.info(
134-
`Translation server started successfully on port: ${port}`,
135-
);
136-
},
137-
config,
138-
});
139146

140-
registerCleanupOnCurrentProcess({
141-
asyncCleanup: async () => {
142-
await translationServer.stop();
143-
},
144-
});
147+
if (isDev && !translationServer) {
148+
translationServer = await startServer();
145149
}
146150
},
147151

@@ -177,23 +181,7 @@ export const lingoUnplugin = createUnplugin<
177181

178182
compiler.hooks.watchRun.tapPromise(PLUGIN_NAME, async () => {
179183
if (webpackMode === "development" && !translationServer) {
180-
translationServer = await startTranslationServer({
181-
startPort,
182-
onError: (err) => {
183-
logger.error("Translation server error:", err);
184-
},
185-
onReady: (port) => {
186-
logger.info(
187-
`Translation server started successfully on port: ${port}`,
188-
);
189-
},
190-
config,
191-
});
192-
registerCleanupOnCurrentProcess({
193-
asyncCleanup: async () => {
194-
await translationServer.stop();
195-
},
196-
});
184+
translationServer = await startServer();
197185
}
198186
});
199187

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
import type { ReactNode } from "react";
1717
import { logger } from "../../utils/logger";
1818
// Keep this import full for replacement during build.
19-
import { getServerLocale } from "@lingo.dev/compiler/locale/server";
19+
import { getServerLocale } from "@lingo.dev/compiler/virtual/locale/server";
2020
import type { LocaleCode } from "lingo.dev/spec";
2121

2222
/**

0 commit comments

Comments
 (0)