@@ -17,11 +17,7 @@ import { URL } from "url";
1717import { WebSocket , WebSocketServer } from "ws" ;
1818import type { MetadataSchema , TranslationMiddlewareConfig } from "../types" ;
1919import { getLogger } from "./logger" ;
20- import {
21- createCache ,
22- createTranslator ,
23- TranslationService ,
24- } from "../translators" ;
20+ import { TranslationService } from "../translators" ;
2521import {
2622 createEmptyMetadata ,
2723 getMetadataPath ,
@@ -33,38 +29,21 @@ import type { LocaleCode } from "lingo.dev/spec";
3329import { parseLocaleOrThrow } from "../utils/is-valid-locale" ;
3430
3531export interface TranslationServerOptions {
36- /**
37- * Starting port to try (will find next available if taken)
38- * @default 3456
39- */
40- startPort ?: number ;
41-
42- /**
43- * Configuration for translation generation
44- */
4532 config : TranslationMiddlewareConfig ;
46-
47- /**
48- * Callback when server is ready
49- */
33+ translationService ?: TranslationService ;
5034 onReady ?: ( port : number ) => void ;
51-
52- /**
53- * Callback on error
54- */
5535 onError ?: ( error : Error ) => void ;
5636}
5737
5838export class TranslationServer {
5939 private server : http . Server | null = null ;
6040 private url : string | undefined = undefined ;
6141 private logger ;
62- private config : TranslationMiddlewareConfig ;
63- private configHash : string ;
64- private startPort : number ;
65- private onReadyCallback ?: ( port : number ) => void ;
66- private onErrorCallback ?: ( error : Error ) => void ;
67- private translationService : TranslationService | null = null ;
42+ private readonly config : TranslationMiddlewareConfig ;
43+ private readonly configHash : string ;
44+ private readonly startPort : number ;
45+ private readonly onReadyCallback ?: ( port : number ) => void ;
46+ private readonly onErrorCallback ?: ( error : Error ) => void ;
6847 private metadata : MetadataSchema | null = null ;
6948 private connections : Set < Socket > = new Set ( ) ;
7049 private wss : WebSocketServer | null = null ;
@@ -75,11 +54,16 @@ export class TranslationServer {
7554 private isBusy = false ;
7655 private busyTimeout : NodeJS . Timeout | null = null ;
7756 private readonly BUSY_DEBOUNCE_MS = 500 ; // Time after last translation to send "idle" event
57+ private readonly translationService : TranslationService ;
7858
7959 constructor ( options : TranslationServerOptions ) {
8060 this . config = options . config ;
8161 this . configHash = hashConfig ( options . config ) ;
82- this . startPort = options . startPort || 60000 ;
62+ this . translationService =
63+ options . translationService ??
64+ // Fallback is for CLI start only.
65+ new TranslationService ( options . config , getLogger ( options . config ) ) ;
66+ this . startPort = options . config . dev . translationServerStartPort ;
8367 this . onReadyCallback = options . onReady ;
8468 this . onErrorCallback = options . onError ;
8569 this . logger = getLogger ( this . config ) ;
@@ -95,19 +79,6 @@ export class TranslationServer {
9579
9680 this . logger . info ( `🔧 Initializing translator...` ) ;
9781
98- const translator = createTranslator ( this . config , this . logger ) ;
99- const cache = createCache ( this . config ) ;
100-
101- this . translationService = new TranslationService (
102- translator ,
103- cache ,
104- {
105- sourceLocale : this . config . sourceLocale ,
106- pluralization : this . config . pluralization ,
107- } ,
108- this . logger ,
109- ) ;
110-
11182 const port = await this . findAvailablePort ( this . startPort ) ;
11283
11384 return new Promise ( ( resolve , reject ) => {
@@ -281,14 +252,13 @@ export class TranslationServer {
281252 * Start a new server or get the URL of an existing one on the preferred port.
282253 *
283254 * This method optimizes for the common case where a translation server is already
284- * running on port 60000 . If that port is taken, it checks if it's our service
255+ * running on a preferred port . If that port is taken, it checks if it's our service
285256 * by calling the health check endpoint. If it is, we reuse it instead of starting
286257 * a new server on a different port.
287258 *
288259 * @returns URL of the running server (new or existing)
289260 */
290261 async startOrGetUrl ( ) : Promise < string > {
291- // If this instance already has a server running, return its URL
292262 if ( this . server && this . url ) {
293263 this . logger . info ( `Using existing server instance at ${ this . url } ` ) ;
294264 return this . url ;
@@ -527,7 +497,6 @@ export class TranslationServer {
527497
528498 res . on ( "end" , ( ) => {
529499 try {
530- // Check if response is valid and has the expected structure
531500 if ( res . statusCode === 200 ) {
532501 const json = JSON . parse ( data ) ;
533502 // Our translation server returns { status: "ok", port: ..., configHash: ... }
@@ -680,11 +649,6 @@ export class TranslationServer {
680649 ) ;
681650 return ;
682651 }
683-
684- if ( ! this . translationService ) {
685- throw new Error ( "Translation service not initialized" ) ;
686- }
687-
688652 // Reload metadata to ensure we have the latest entries
689653 // (new entries may have been added since server started)
690654 await this . reloadMetadata ( ) ;
@@ -747,10 +711,6 @@ export class TranslationServer {
747711 try {
748712 const parsedLocale = parseLocaleOrThrow ( locale ) ;
749713
750- if ( ! this . translationService ) {
751- throw new Error ( "Translation service not initialized" ) ;
752- }
753-
754714 // Reload metadata to ensure we have the latest entries
755715 // (new entries may have been added since server started)
756716 await this . reloadMetadata ( ) ;
@@ -842,9 +802,6 @@ export function hashConfig(config: Record<string, SerializableValue>): string {
842802 return crypto . createHash ( "md5" ) . update ( serialized ) . digest ( "hex" ) . slice ( 0 , 12 ) ;
843803}
844804
845- /**
846- * Create and start a translation server
847- */
848805export async function startTranslationServer (
849806 options : TranslationServerOptions ,
850807) : Promise < TranslationServer > {
@@ -856,10 +813,10 @@ export async function startTranslationServer(
856813/**
857814 * Create a translation server and start it or reuse an existing one on the preferred port
858815 *
859- * Since we have little control over the dev server start in next, we can start the translation server only in the loader,
860- * and loaders could be started from multiple processes (it seems) or similar we need a way to avoid starting multiple servers.
816+ * Since we have little control over the dev server start in next, we can start the translation server only in the async config or in the loader,
817+ * they both could be run in different processes, and we need a way to avoid starting multiple servers.
861818 * This one will try to start a server on the preferred port (which seems to be an atomic operation), and if it fails,
862- * it checks if the server already started is ours and returns its url.
819+ * it checks if the server that is already started is ours and returns its url.
863820 *
864821 * @returns Object containing the server instance and its URL
865822 */
0 commit comments