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
107 changes: 106 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Write your app in one language. Wrap text in `<T>`. Get translations generated a
- **`msg()`** — mark strings for extraction outside of JSX
- **CLI Tool** — translate JSON, Markdown, and MDX files from the command line
- **Vite Plugin** — build-time translation with smart change detection
- **GitHub Action** — `omniaura/solid-translate@v1` for CI/CD translation automation
- **BYOK** — use any [Vercel AI SDK](https://ai-sdk.dev/) provider (OpenRouter, OpenAI, Anthropic, Google, etc.)

## Install
Expand Down Expand Up @@ -376,6 +377,109 @@ const model = google("gemini-2.0-flash");

## CI/CD Integration

### GitHub Action

Use the official action to keep translations up to date automatically:

```yaml
# .github/workflows/translate.yml
name: Translate

on:
push:
branches: [main]

jobs:
translate:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- uses: omniaura/solid-translate@v1
env:
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
with:
commit: true
commit-message: "chore: update translations"
```

#### Action Inputs

| Input | Default | Description |
|-------|---------|-------------|
| `command` | `both` | `extract`, `translate`, or `both` |
| `working-directory` | `.` | Working directory |
| `commit` | `false` | Auto-commit updated translation files |
| `commit-message` | `chore: update translations` | Commit message |
| `node-version` | `22` | Node.js version |
| `package-manager` | `npm` | `npm`, `bun`, `pnpm`, or `yarn` |

#### Action Outputs

| Output | Description |
|--------|-------------|
| `changed` | `true` if translation files were updated |
| `files` | Newline-separated list of changed translation files |

The action reports and commits changes for JSON, Markdown, MDX, and `.solid-translate.lock` files only. Package manifests and package manager lockfiles are intentionally excluded.

#### Examples

**Translate on push and commit back:**

Use auto-commit only on trusted refs where `GITHUB_TOKEN` can push to the checked-out branch, such as `push` events on your own repository. For pull requests, leave `commit` disabled and use the `changed`/`files` outputs to decide whether to fail CI or open a separate update PR.

```yaml
name: Translate

on:
push:
branches: [main]

jobs:
translate:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- uses: omniaura/solid-translate@v1
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
with:
commit: true
package-manager: bun
```

**Extract only (no AI calls):**

```yaml
- uses: omniaura/solid-translate@v1
with:
command: extract
```

**Use output in subsequent steps:**

```yaml
- uses: omniaura/solid-translate@v1
id: translate
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

- name: Create PR with translations
if: steps.translate.outputs.changed == 'true'
env:
TRANSLATION_FILES: ${{ steps.translate.outputs.files }}
run: |
printf 'Updated files:\n%s\n' "$TRANSLATION_FILES"
```

### Manual CI Setup

Add to your build script for automatic translations on every deploy:

```json
Expand All @@ -387,7 +491,7 @@ Add to your build script for automatic translations on every deploy:
}
```

Or in GitHub Actions:
Or directly in a workflow:

```yaml
- name: Translate
Expand Down Expand Up @@ -439,6 +543,7 @@ declare module "virtual:solid-translate" {
| Shared strings (`msg()`) | ✅ | ✅ |
| CLI for JSON/MD/MDX | ✅ | ✅ |
| CI/CD integration | ✅ | ✅ |
| Official GitHub Action | ❌ | ✅ |
| Zero refactoring | ✅ | ✅ |
| BYOK (bring your own key) | ❌ (SaaS) | ✅ |
| No vendor lock-in | ❌ | ✅ |
Expand Down
169 changes: 169 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
name: "solid-translate"
description: "AI-powered translations for SolidJS apps. Extract and translate strings in CI."
author: "omniaura"

branding:
icon: "globe"
color: "blue"

inputs:
command:
description: "Command to run: extract, translate, or both (default: both)"
required: false
default: "both"
working-directory:
description: "Working directory (default: repo root)"
required: false
default: "."
commit:
description: "Commit updated translation files (default: false)"
required: false
default: "false"
commit-message:
description: "Commit message for translation updates"
required: false
default: "chore: update translations"
node-version:
description: "Node.js version to use"
required: false
default: "22"
package-manager:
description: "Package manager: npm, bun, pnpm, or yarn"
required: false
default: "npm"

outputs:
changed:
description: "Whether translation files were changed (true/false)"
value: ${{ steps.check-changes.outputs.changed }}
files:
description: "Newline-separated list of changed translation files"
value: ${{ steps.check-changes.outputs.files }}

runs:
using: "composite"
steps:
- name: Validate inputs
shell: bash
env:
COMMAND: ${{ inputs.command }}
COMMIT: ${{ inputs.commit }}
PACKAGE_MANAGER: ${{ inputs.package-manager }}
run: |
case "$COMMAND" in
extract|translate|both) ;;
*) echo "Invalid command: $COMMAND" >&2; exit 1 ;;
esac

case "$COMMIT" in
true|false) ;;
*) echo "Invalid commit value: $COMMIT" >&2; exit 1 ;;
esac

case "$PACKAGE_MANAGER" in
npm|bun|pnpm|yarn) ;;
*) echo "Invalid package-manager: $PACKAGE_MANAGER" >&2; exit 1 ;;
esac

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}

- name: Setup Bun
if: inputs.package-manager == 'bun'
uses: oven-sh/setup-bun@v2

- name: Enable package manager shims
if: inputs.package-manager == 'pnpm' || inputs.package-manager == 'yarn'
shell: bash
run: corepack enable

- name: Run extract
if: inputs.command == 'extract' || inputs.command == 'both'
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
PACKAGE_MANAGER: ${{ inputs.package-manager }}
run: |
case "$PACKAGE_MANAGER" in
bun) bunx solid-translate extract ;;
pnpm) pnpm dlx solid-translate extract ;;
yarn) yarn dlx solid-translate extract ;;
npm) npx --yes solid-translate extract ;;
esac

- name: Run translate
if: inputs.command == 'translate' || inputs.command == 'both'
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
PACKAGE_MANAGER: ${{ inputs.package-manager }}
run: |
case "$PACKAGE_MANAGER" in
bun) bunx solid-translate translate ;;
pnpm) pnpm dlx solid-translate translate ;;
yarn) yarn dlx solid-translate translate ;;
npm) npx --yes solid-translate translate ;;
esac

- name: Check for changes
id: check-changes
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
mapfile -t files < <(
{
git diff --name-only -- \
'*.json' ':(glob)**/*.json' \
'*.md' ':(glob)**/*.md' \
'*.mdx' ':(glob)**/*.mdx' \
'.solid-translate.lock' ':(glob)**/.solid-translate.lock' \
':(exclude)package.json' \
':(exclude)package-lock.json' \
':(exclude)bun.lock' \
':(exclude)pnpm-lock.yaml' \
':(exclude)yarn.lock'
git ls-files --others --exclude-standard -- \
'*.json' ':(glob)**/*.json' \
'*.md' ':(glob)**/*.md' \
'*.mdx' ':(glob)**/*.mdx' \
'.solid-translate.lock' ':(glob)**/.solid-translate.lock' \
':(exclude)package.json' \
':(exclude)package-lock.json' \
':(exclude)bun.lock' \
':(exclude)pnpm-lock.yaml' \
':(exclude)yarn.lock'
} | sort -u
)

if [ "${#files[@]}" -gt 0 ]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
{
echo "files<<EOF"
printf '%s\n' "${files[@]}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
{
echo "files<<EOF"
echo "EOF"
} >> "$GITHUB_OUTPUT"
fi

- name: Commit changes
if: inputs.commit == 'true' && steps.check-changes.outputs.changed == 'true'
shell: bash
working-directory: ${{ inputs.working-directory }}
env:
COMMIT_MESSAGE: ${{ inputs.commit-message }}
TRANSLATION_FILES: ${{ steps.check-changes.outputs.files }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
while IFS= read -r file; do
[ -n "$file" ] && git add -- "$file"
done <<< "$TRANSLATION_FILES"
git commit -m "$COMMIT_MESSAGE"
git push
Loading