Skip to content

Commit 67a10cd

Browse files
committed
feat: add locale code validation
1 parent 24ef708 commit 67a10cd

File tree

4 files changed

+44
-17
lines changed

4 files changed

+44
-17
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
*/
44
import { describe, expect, it } from "vitest";
55
import IntlMessageFormat from "intl-messageformat";
6-
import { escapeTextForICU } from "./utils";
6+
import { parse } from "@babel/parser";
7+
import traverse from "@babel/traverse";
8+
import * as t from "@babel/types";
9+
import { escapeTextForICU, isReactComponent } from "./utils";
710

811
describe("escapeTextForICU", () => {
912
it.each([

cmp/compiler/src/translation-server/cli.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { logger } from "../utils/logger";
5151
import { startOrGetTranslationServer } from "./translation-server";
5252
import { createLingoConfig } from "../utils/config-factory";
5353
import { type LocaleCode } from "lingo.dev/spec";
54+
import { parseLocaleOrThrow } from "../utils/is-valid-locale";
5455

5556
interface CLIOptions {
5657
port?: number;
@@ -87,13 +88,15 @@ function parseCliArgs(): CLIOptions {
8788
allowPositionals: false,
8889
});
8990

91+
const parsedSourceLocale = parseLocaleOrThrow(values["source-locale"]);
92+
const parsedTargetLocales = values["target-locales"]
93+
?.split(",")
94+
.map((s) => parseLocaleOrThrow(s));
95+
9096
return {
9197
port: values.port ? parseInt(values.port, 10) : undefined,
92-
// TODO (AleksandrSl 04/12/2025): Validation for LocaleCode is needed
93-
sourceLocale: values["source-locale"] as LocaleCode,
94-
targetLocales: values["target-locales"]
95-
?.split(",")
96-
.map((s) => s.trim()) as LocaleCode[],
98+
sourceLocale: parsedSourceLocale,
99+
targetLocales: parsedTargetLocales,
97100
lingoDir: values["lingo-dir"],
98101
sourceRoot: values["source-root"],
99102
models: values.models ? parseModelsString(values.models) : undefined,

cmp/compiler/src/translation-server/translation-server.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import type { TranslationServerEvent } from "./ws-events";
3131
import { createEvent } from "./ws-events";
3232
import type { LocaleCode } from "lingo.dev/spec";
33+
import { parseLocaleOrThrow } from "../utils/is-valid-locale";
3334

3435
export interface TranslationServerOptions {
3536
/**
@@ -607,20 +608,16 @@ export class TranslationServer {
607608
const postMatch = url.pathname.match(/^\/translations\/([^/]+)$/);
608609
if (postMatch && req.method === "POST") {
609610
const [, locale] = postMatch;
610-
// TODO (AleksandrSl 14/12/2025): Validate localeCode
611-
await this.handleBatchTranslationRequest(
612-
locale as LocaleCode,
613-
req,
614-
res,
615-
);
611+
612+
await this.handleBatchTranslationRequest(locale, req, res);
616613
return;
617614
}
618615

619616
// Translation dictionary endpoint: GET /translations/:locale
620617
const dictMatch = url.pathname.match(/^\/translations\/([^/]+)$/);
621618
if (dictMatch && req.method === "GET") {
622619
const [, locale] = dictMatch;
623-
await this.handleDictionaryRequest(locale as LocaleCode, res);
620+
await this.handleDictionaryRequest(locale, res);
624621
return;
625622
}
626623

@@ -653,11 +650,13 @@ export class TranslationServer {
653650
* Handle batch translation request
654651
*/
655652
private async handleBatchTranslationRequest(
656-
locale: LocaleCode,
653+
locale: string,
657654
req: http.IncomingMessage,
658655
res: http.ServerResponse,
659656
): Promise<void> {
660657
try {
658+
const parsedLocale = parseLocaleOrThrow(locale);
659+
661660
// Read request body
662661
let body = "";
663662
this.logger.debug("Reading request body...");
@@ -703,7 +702,7 @@ export class TranslationServer {
703702
try {
704703
// Translate using the stored service
705704
const result = await this.translationService.translate(
706-
locale,
705+
parsedLocale,
707706
this.metadata,
708707
hashes,
709708
);
@@ -743,10 +742,12 @@ export class TranslationServer {
743742
* Handle request for full translation dictionary
744743
*/
745744
private async handleDictionaryRequest(
746-
locale: LocaleCode,
745+
locale: string,
747746
res: http.ServerResponse,
748747
): Promise<void> {
749748
try {
749+
const parsedLocale = parseLocaleOrThrow(locale);
750+
750751
if (!this.translationService) {
751752
throw new Error("Translation service not initialized");
752753
}
@@ -765,7 +766,7 @@ export class TranslationServer {
765766

766767
// Translate all hashes
767768
const result = await this.translationService.translate(
768-
locale,
769+
parsedLocale,
769770
this.metadata,
770771
allHashes,
771772
);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { isValidLocale as _isValidLocale } from "lingo.dev/locale-codes";
2+
import type { LocaleCode } from "lingo.dev/spec";
3+
4+
export function isValidLocale(locale: string): locale is LocaleCode {
5+
return _isValidLocale(locale);
6+
}
7+
8+
export function parseLocale(
9+
locale: string | undefined,
10+
): LocaleCode | undefined {
11+
return locale && isValidLocale(locale) ? locale : undefined;
12+
}
13+
14+
export function parseLocaleOrThrow(locale: string | undefined): LocaleCode {
15+
if (locale && isValidLocale(locale)) {
16+
return locale;
17+
} else {
18+
throw new Error(`Invalid locale: ${locale}`);
19+
}
20+
}

0 commit comments

Comments
 (0)