Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/wicked-baboons-bake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"lingo.dev": patch
---

Fix parsing of comma-separated locale inputs with spaces and quotes during init
31 changes: 19 additions & 12 deletions packages/cli/src/cli/cmd/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,25 @@ const throwHelpError = (option: string, value: string) => {
if (value === "help") {
openUrl("/go/call");
}

// Better error message for invalid locale or bucket format inputs, with instructions to get help for supported formats
throw new Error(
`Invalid ${option}: ${value}\n\nDo you need support for ${value} ${option}? Type "help" and we will.`,
`Invalid ${option}: "${value}".\n\n` +
`Accepted formats:\n` +
` es,fr\n` +
` es fr\n` +
` 'es', 'fr'\n\n` +
`If you need support for "${value}" ${option}, type "help" and we will.`,
);
};

// Parses comma or space separated list of values, removing extra whitespace and quotes
export const parseListInput = (value: string): string[] =>
value
.split(/[,\s]+/)
.map((v) => v.replace(/^['"]|['"]$/g, ""))
.filter(Boolean);

export default new InteractiveCommand()
.command("init")
.description("Create i18n.json configuration file for a new project")
Expand Down Expand Up @@ -67,10 +81,8 @@ export default new InteractiveCommand()
"-t --targets <locale...>",
"Target languages to translate to. Accepts locale codes like 'es', 'fr', 'de-AT' separated by commas or spaces. Defaults to 'es'",
)
.argParser((value) => {
const values = (
value.includes(",") ? value.split(",") : value.split(" ")
) as LocaleCode[];
.argParser((value: string) => {
const values = parseListInput(value) as LocaleCode[];
values.forEach((value) => {
try {
resolveLocaleCode(value);
Expand Down Expand Up @@ -102,9 +114,7 @@ export default new InteractiveCommand()
)
.argParser((value) => {
if (!value || value.length === 0) return [];
const values = value.includes(",")
? value.split(",")
: value.split(" ");
const values = parseListInput(value);

for (const p of values) {
try {
Expand All @@ -118,7 +128,6 @@ export default new InteractiveCommand()
}
}
return values;

})
.prompt(undefined) // make non-interactive
.default([]),
Expand Down Expand Up @@ -191,9 +200,7 @@ export default new InteractiveCommand()
const customPaths = await input({
message: "Enter paths to use",
});
selectedPatterns = customPaths.includes(",")
? customPaths.split(",")
: customPaths.split(" ");
selectedPatterns = parseListInput(customPaths);
}

newConfig.buckets = {
Expand Down
40 changes: 40 additions & 0 deletions packages/cli/src/cli/cmd/parseListInput.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { describe, it, expect } from "vitest";
import { parseListInput } from "./init";

describe("parseListInput", () => {
it("parses comma-separated values", () => {
expect(parseListInput("es,fr")).toEqual(["es", "fr"]);
});

it("parses comma-separated values with whitespace", () => {
expect(parseListInput("es, fr")).toEqual(["es", "fr"]);
});

it("parses space-separated values", () => {
expect(parseListInput("es fr")).toEqual(["es", "fr"]);
});

it("parses single-quoted values", () => {
expect(parseListInput("'es', 'fr'")).toEqual(["es", "fr"]);
});

it("parses double-quoted values", () => {
expect(parseListInput('"es", "fr"')).toEqual(["es", "fr"]);
});

it("returns empty array for empty input", () => {
expect(parseListInput("")).toEqual([]);
});

it("returns empty array for whitespace-only input", () => {
expect(parseListInput(" ")).toEqual([]);
});

it("filters duplicate separators", () => {
expect(parseListInput("es,,fr")).toEqual(["es", "fr"]);
});

it("parses outer-quoted comma-separated lists", () => {
expect(parseListInput("'es,fr'")).toEqual(["es", "fr"]);
});
});
Loading