Skip to content
Open
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
80 changes: 76 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ npx i18n-scan <directory>
# If installed globally
i18n-scan <directory>

# Run locally during development
npm run dev <directory>
# Run locally during development (note the -- before flags)
npm run dev <directory> -- [options]

# Run built version locally
npm start <directory>
Expand All @@ -57,16 +57,31 @@ npm start <directory>
npm run dev ./src

# Scan with custom file extensions
npm run dev ./src --ext .tsx,.jsx,.ts,.js
npm run dev ./src -- --ext .tsx,.jsx,.ts,.js

# Scan with custom attributes
npm run dev ./src --attributes title,alt,placeholder,aria-label
npm run dev ./src -- --attributes title,alt,placeholder,aria-label

# Filter out non-alphabetic text (symbols, emojis, numbers only)
npm run dev ./src -- --filter-non-alpha

# Extract from objects, function calls, and schemas
npm run dev ./src -- --include-literals

# Combine options
npm run dev ./src -- --filter-non-alpha --truncate 50

# When using npx or installed globally, no -- needed:
npx i18n-scan ./src --filter-non-alpha --truncate 50
```

### Options

- `-e, --ext <extensions>`: Comma-separated list of file extensions (default: `.tsx,.jsx`)
- `-a, --attributes <names>`: Comma-separated list of attribute names to extract (default: `title,alt,placeholder`)
- `-t, --truncate <limit>`: Truncate text to this length, 0 to disable (default: `30`)
- `-f, --filter-non-alpha`: Filter out text containing only non-alphabetic characters (e.g., `$`, `123`, `🎉`)
- `-l, --include-literals`: Include string literals from objects, function calls, and validation schemas

## Example Output

Expand All @@ -77,6 +92,17 @@ npm run dev ./src --attributes title,alt,placeholder,aria-label
[src/components/Form.tsx:13] This field is required
```

**With `--filter-non-alpha` enabled:**

```
# Without filter:
[src/components/ProductCard.tsx:30] $
[src/components/ProductCard.tsx:33] In Stock

# With filter ($ is filtered out):
[src/components/ProductCard.tsx:33] In Stock
```

## Development

- `npm run dev <directory>`: Run from TypeScript source
Expand All @@ -85,6 +111,8 @@ npm run dev ./src --attributes title,alt,placeholder,aria-label

## What it extracts

### By default (JSX only):

1. **JSX Text Content**: Any text between JSX tags

```jsx
Expand All @@ -99,6 +127,50 @@ npm run dev ./src --attributes title,alt,placeholder,aria-label
<button title="Close dialog" /> // Extracts: "Close dialog"
```

### With `--include-literals` flag:

Extracts additional string literals that are commonly user-facing:

3. **Object Properties**: Strings from objects with keys like `message`, `error`, `label`, `title`, `description`, etc.

```typescript
const errors = {
required: "This field is required", // Extracts
invalid: "Invalid email address", // Extracts
};

const config = {
className: "btn-primary", // Skips (technical)
title: "Welcome back", // Extracts
apiUrl: "/api/users", // Skips (technical)
};
```

4. **Zod Schema Messages**: Validation messages and descriptions

```typescript
z.string().email("Invalid email")
z.string().min(5, "Too short")
z.string().max(100, { message: "Too long"
z.string().describe("Your username")
```

5. **Function Calls**: Common user-facing function calls

```typescript
toast.success("Saved successfully!");
toast.error("Failed to save");
console.error("Something went wrong");
throw new Error("Invalid input");
```

**Smart Filtering**: The tool automatically skips technical strings like:

- Import paths, URLs, API endpoints
- CSS class names, IDs
- camelCase identifiers, CONSTANTS
- Single characters and code patterns

## Github Repo

https://github.com/hexxt-git/i18n-scan
File renamed without changes.
68 changes: 68 additions & 0 deletions example/src/test-literals.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { z } from "zod";

// Test 1: Object with user-facing strings
const errorMessages = {
required: "This field is required",
invalid: "Invalid input provided",
tooShort: "Must be at least 5 characters",
};

const config = {
title: "Welcome to our platform",
description: "Start your journey today",
buttonText: "Get Started Now",
};

// Test 2: Zod schemas
const userSchema = z.object({
email: z.string().email("Please enter a valid email address"),
password: z
.string()
.min(8, "Password must be at least 8 characters")
.max(100, { message: "Password is too long" }),
username: z.string().describe("Your unique username"),
});

const ageSchema = z.number().min(18, "You must be 18 or older");

// Test 3: Toast/Error calls
function handleSubmit() {
toast.success("Form submitted successfully!");
toast.error("Failed to submit form");
console.error("Something went wrong");
console.warn("This action cannot be undone");
}

// Test 4: Error throwing
function validateInput(value: string) {
if (!value) {
throw new Error("Input cannot be empty");
}
if (value.length < 3) {
throw new Error("Input must be at least 3 characters long");
}
}

// Test 5: Should NOT extract (technical strings)
const technicalStrings = {
className: "flex items-center", // Should skip
apiUrl: "/api/users", // Should skip
method: "POST", // Should skip
};

// Test 6: Mixed object (should extract some, skip others)
const mixedConfig = {
endpoint: "/api/submit", // Should skip
message: "Your data has been saved", // Should extract
className: "button-primary", // Should skip
label: "Save Changes", // Should extract
};

export function TestComponent() {
return (
<div>
<h1>Test Component</h1>
<p>Regular JSX text should still work</p>
</div>
);
}
Loading