openrouter-compliance-review
Review OpenRouter integration for regulatory compliance (SOC2, GDPR,
lokalise-deploy-integration
Deploy Lokalise integrations to Vercel, Netlify, and Cloud Run platforms.
Full skill instructions
Translations must be downloaded fresh during CI/CD builds to ensure production always ships the latest reviewed content. This skill covers downloading translations as a build step, GitHub Actions workflows for translation sync, Vercel and Netlify build plugin integration, OTA (over-the-air) updates for mobile apps via Lokalise's iOS and Android SDKs, and environment-specific translation bundles.
LOKALISE_API_TOKEN and LOKALISE_PROJECT_ID stored as CI secretscurl and unzip available in CI environment (standard on GitHub Actions runners)Add a pre-build script that pulls translations from Lokalise before your framework compiles:
#!/bin/bash
# scripts/download-translations.sh
set -euo pipefail
PROJECT_ID="${LOKALISE_PROJECT_ID:?Missing LOKALISE_PROJECT_ID}"
API_TOKEN="${LOKALISE_API_TOKEN:?Missing LOKALISE_API_TOKEN}"
DEST_DIR="${1:-./src/locales}"
echo "Downloading translations for project $PROJECT_ID..."
BUNDLE_URL=$(curl -sf -X POST \
"https://api.lokalise.com/api2/projects/${PROJECT_ID}/files/download" \
-H "X-Api-Token: ${API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"format\": \"json\",
\"original_filenames\": false,
\"bundle_structure\": \"%LANG_ISO%.json\",
\"export_empty_as\": \"base\",
\"json_unescaped_slashes\": true,
\"include_tags\": [\"production\"],
\"filter_data\": [\"translated\", \"reviewed\"]
}" | jq -r '.bundle_url')
if [ -z "$BUNDLE_URL" ] || [ "$BUNDLE_URL" = "null" ]; then
echo "ERROR: Failed to get bundle URL from Lokalise"
exit 1
fi
mkdir -p "$DEST_DIR"
curl -sfL "$BUNDLE_URL" -o /tmp/translations.zip
unzip -o /tmp/translations.zip -d "$DEST_DIR"
rm /tmp/translations.zip
FILE_COUNT=$(ls -1 "$DEST_DIR"/*.json 2>/dev/null | wc -l)
echo "Downloaded $FILE_COUNT translation files to $DEST_DIR"
Wire it into package.json:
{
"scripts": {
"prebuild": "./scripts/download-translations.sh ./src/locales",
"build": "next build"
}
}
Full workflow that downloads translations, builds, and deploys:
# .github/workflows/deploy.yml
name: Build & Deploy
on:
push:
branches: [main]
# Trigger from Lokalise webhook (via repository_dispatch)
repository_dispatch:
types: [translations_updated]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install dependencies
run: npm ci
- name: Download translations from Lokalise
env:
LOKALISE_API_TOKEN: ${{ secrets.LOKALISE_API_TOKEN }}
LOKALISE_PROJECT_ID: ${{ secrets.LOKALISE_PROJECT_ID }}
run: |
chmod +x ./scripts/download-translations.sh
./scripts/download-translations.sh ./src/locales
- name: Verify translation integrity
run: |
# Ensure all expected languages are present
EXPECTED_LANGS="en fr de ja es"
for lang in $EXPECTED_LANGS; do
if [ ! -f "./src/locales/${lang}.json" ]; then
echo "ERROR: Missing translation file for ${lang}"
exit 1
fi
# Validate JSON
jq empty "./src/locales/${lang}.json" || {
echo "ERROR: Invalid JSON in ${lang}.json"
exit 1
}
done
echo "All translation files present and valid"
- name: Build
run: npm run build
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: --prod
To trigger builds when translations change, set up a Lokalise webhook that fires a GitHub repository_dispatch:
# In your webhook handler (see lokalise-webhooks-events)
curl -X POST \
"https://api.github.com/repos/OWNER/REPO/dispatches" \
-H "Authorization: token ${GITHUB_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"event_type": "translations_updated"}'
For Vercel, translations download during the build phase. Configure the token as an environment variable:
# Set Lokalise secrets in Vercel
vercel env add LOKALISE_API_TOKEN production preview
vercel env add LOKALISE_PROJECT_ID production preview
In vercel.json, ensure the build command runs the translation download:
{
"buildCommand": "./scripts/download-translations.sh ./src/locales && next build",
"outputDirectory": ".next"
}
For ISR/SSR apps that need translations at runtime (not just build time), cache translations in a KV store or download on cold start:
// lib/translations.ts (Next.js example)
import { unstable_cache } from "next/cache";
export const getTranslations = unstable_cache(
async (locale: string) => {
const res = await fetch(
`https://api.lokalise.com/api2/projects/${process.env.LOKALISE_PROJECT_ID}/translations`,
{
headers: { "X-Api-Token": process.env.LOKALISE_API_TOKEN! },
}
);
const data = await res.json();
return data.translations
.filter((t: any) => t.language_iso === locale)
.reduce(
(acc: Record<string, string>, t: any) => ({
...acc,
[t.key_name]: t.translation,
}),
{}
);
},
["translations"],
{ revalidate: 3600, tags: ["translations"] }
);
Netlify uses build plugins or the prebuild command. The simplest approach uses netlify.toml:
# netlify.toml
[build]
command = "./scripts/download-translations.sh ./src/locales && npm run build"
publish = "dist"
[build.environment]
NODE_VERSION = "20"
Set secrets via Netlify CLI:
netlify env:set LOKALISE_API_TOKEN "your-token" --scope builds
netlify env:set LOKALISE_PROJECT_ID "123456789.abcdefgh" --scope builds
For a custom Netlify Build Plugin that integrates more deeply:
// plugins/netlify-plugin-lokalise/index.js
module.exports = {
async onPreBuild({ utils, constants }) {
const { execSync } = require("child_process");
try {
console.log("Downloading translations from Lokalise...");
execSync("./scripts/download-translations.sh ./src/locales", {
stdio: "inherit",
env: process.env,
});
} catch (error) {
utils.build.failBuild("Failed to download translations from Lokalise");
}
},
};
Over-the-air updates let you push translation changes without an app store release. Lokalise provides native SDKs for this.
iOS (Swift):
// AppDelegate.swift
import Lokalise
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize with OTA SDK token and project ID
Lokalise.shared.setProjectID(
"123456789.abcdefgh",
token: "ota-sdk-token-from-lokalise-dashboard"
)
// Preemptively check for updates
Lokalise.shared.checkForUpdates { updated, error in
if let error = error {
print("OTA update check failed: \(error.localizedDescription)")
return
}
if updated {
print("Translations updated OTA")
}
}
return true
}
// Usage — works with NSLocalizedString automatically
let welcome = NSLocalizedString("welcome.title", comment: "Welcome screen title")
Android (Kotlin):
// Application.kt
import com.lokalise.sdk.Lokalise
import com.lokalise.sdk.LokaliseCallback
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
Lokalise.init(this)
Lokalise.updateTranslations()
// Optional: listen for update completion
Lokalise.setUpdateCallback(object : LokaliseCallback {
override fun onUpdated(oldBundleId: Long, newBundleId: Long) {
Log.d("Lokalise", "Translations updated: $oldBundleId -> $newBundleId")
}
override fun onErrorOccurred(e: LokaliseException) {
Log.e("Lokalise", "OTA update failed", e)
}
})
}
}
// Usage — strings.xml values are overridden by OTA bundles
val welcome = getString(R.string.welcome_title)
Both SDKs fall back to the bundled translations if OTA download fails, so the app always has working strings.
Use tags in Lokalise to manage environment-specific content:
# Download only production-tagged translations
./scripts/download-translations.sh ./src/locales # uses "production" tag filter
# For staging: modify the script or use an env var
LOKALISE_TAGS="staging,beta" ./scripts/download-translations.sh ./src/locales
Update download-translations.sh to support dynamic tags:
# Add near the top of download-translations.sh
TAGS="${LOKALISE_TAGS:-production}"
TAG_JSON=$(echo "$TAGS" | jq -R 'split(",")' )
# Use in the curl payload:
# "include_tags": $TAG_JSON
This lets you maintain separate translation sets:
| Issue | Cause | Solution |
|---|---|---|
| Missing translations in build | download-translations.sh failed silently | Use set -euo pipefail and check bundle URL |
| Secret not found in CI | Env var not configured | Add via vercel env add / netlify env:set / GitHub Secrets |
| Build timeout | Large project with many languages | Filter with filter_langs and include_tags |
| OTA fails on device | Network blocked or token invalid | SDKs fall back to bundled translations automatically |
| Stale translations in production | Cache not invalidated | Use repository_dispatch webhook to trigger rebuild |
| Empty JSON files | No translations match tag filter | Verify tag names match between Lokalise and script |
# .github/workflows/sync-translations.yml
name: Sync Translations
on:
schedule:
- cron: "0 */6 * * *" # Every 6 hours
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download translations
env:
LOKALISE_API_TOKEN: ${{ secrets.LOKALISE_API_TOKEN }}
LOKALISE_PROJECT_ID: ${{ secrets.LOKALISE_PROJECT_ID }}
run: |
chmod +x ./scripts/download-translations.sh
./scripts/download-translations.sh ./src/locales
- name: Commit if changed
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add ./src/locales/
git diff --cached --quiet || git commit -m "chore: sync translations from Lokalise"
git push
# Dockerfile
FROM node:20-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
ARG LOKALISE_API_TOKEN
ARG LOKALISE_PROJECT_ID
COPY . .
RUN chmod +x ./scripts/download-translations.sh \
&& ./scripts/download-translations.sh ./src/locales \
&& npm run build
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package*.json ./
RUN npm ci --production
EXPOSE 3000
CMD ["npm", "start"]
Build with: docker build --build-arg LOKALISE_API_TOKEN=$TOKEN --build-arg LOKALISE_PROJECT_ID=$PID .
For handling errors during API calls in your pipeline, see lokalise-common-errors. For managing translation data formats and encoding, see lokalise-data-handling.
Review OpenRouter integration for regulatory compliance (SOC2, GDPR,
Data synchronization, ETL patterns, and data management for MaintainX.
Execute Groq primary workflow: chat completions with tool use and JSON
Validate CSRF protection implementations for security gaps. Use when
Configure this skill should be used when the user asks about "API contract",
Execute Ideogram incident response with triage, mitigation, and postmortem.
Execute create, select, and transform features to improve machine learning
Reusable patterns for the Gamma REST API (no SDK exists).
Apply production-ready Deepgram SDK patterns for TypeScript and Python.
Migrate to OpenRouter from direct provider APIs or upgrade between SDK/model
Diagnose and fix common OpenRouter API errors. Use when encountering
Optimize Linear API usage, reduce unnecessary calls, and maximize
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience