lokalise-local-dev-loop
Configure Lokalise local development with file sync and hot reload. Use when setting up a development environment, configuring translation sync, or establishing a fast iteration cycle with Lokalise. Trigger with phrases like "lokalise dev setup", "lokalise local development", "lokalise dev environment", "develop with lokalise", "lokalise sync".
What this skill does
# Lokalise Local Dev Loop
## Overview
Set up a complete local development workflow with Lokalise: project structure for i18n files, CLI push/pull commands, file watching for auto-upload, mock translations for offline development, framework integration (React i18next, Vue i18n), and a pre-commit hook to keep translations synced.
## Prerequisites
- Lokalise API token exported as `LOKALISE_API_TOKEN`
- Lokalise project ID exported as `LOKALISE_PROJECT_ID`
- Node.js 18+ with npm or pnpm
- `lokalise2` CLI installed
- Git (for pre-commit hook)
## Instructions
1. Set up the project directory structure for i18n files, compatible with Lokalise's `bundle_structure` and most i18n frameworks.
```
project-root/
├── src/
│ └── locales/
│ ├── en.json # Base language (source of truth)
│ ├── fr.json # Downloaded from Lokalise
│ ├── de.json
│ ├── es.json
│ └── index.ts # Barrel export + type definitions
├── scripts/
│ ├── i18n-push.sh # Upload source to Lokalise
│ ├── i18n-pull.sh # Download translations from Lokalise
│ └── i18n-mock.ts # Generate mock translations
├── .env.local # LOKALISE_API_TOKEN, LOKALISE_PROJECT_ID
└── package.json # i18n:push, i18n:pull, i18n:sync scripts
```
**Barrel export with type safety (`src/locales/index.ts`):**
```typescript
import en from "./en.json";
// Type derived from base language — all other locales must match this shape
export type TranslationKeys = typeof en;
export const defaultLocale = "en" as const;
export const supportedLocales = ["en", "fr", "de", "es"] as const;
export type Locale = (typeof supportedLocales)[number];
export async function loadLocale(locale: Locale): Promise<TranslationKeys> {
const mod = await import(`./${locale}.json`);
return mod.default;
}
```
1. Create CLI push/pull scripts for the upload-translate-download cycle.
**Push script (`scripts/i18n-push.sh`):**
```bash
#!/usr/bin/env bash
set -euo pipefail
# Upload source language file to Lokalise
lokalise2 --token "$LOKALISE_API_TOKEN" file upload \
--project-id "$LOKALISE_PROJECT_ID" \
--file ./src/locales/en.json \
--lang-iso en \
--replace-modified \
--include-path \
--detect-icu-plurals \
--poll \
--tag-inserted-keys \
--tag-updated-keys
echo "Source strings pushed to Lokalise"
```
**Pull script (`scripts/i18n-pull.sh`):**
```bash
#!/usr/bin/env bash
set -euo pipefail
# Download all translations from Lokalise
lokalise2 --token "$LOKALISE_API_TOKEN" file download \
--project-id "$LOKALISE_PROJECT_ID" \
--format json \
--original-filenames=false \
--bundle-structure "%LANG_ISO%.json" \
--export-empty-as base \
--export-sort a_z \
--replace-breaks=false \
--placeholder-format icu \
--unzip-to ./src/locales
echo "Translations pulled to ./src/locales/"
# Show what changed
git diff --stat src/locales/ || true
```
**Package.json scripts:**
```json
{
"scripts": {
"i18n:push": "bash scripts/i18n-push.sh",
"i18n:pull": "bash scripts/i18n-pull.sh",
"i18n:sync": "npm run i18n:push && npm run i18n:pull"
}
}
```
**Typical workflow:**
```bash
# Edit source strings locally
vim src/locales/en.json
# Push changes to Lokalise
npm run i18n:push
# ... translators work in Lokalise UI ...
# Pull completed translations
npm run i18n:pull
# Full round-trip
npm run i18n:sync
```
1. Set up watch mode to auto-upload source strings when `en.json` changes during development.
```typescript
// scripts/i18n-watch.ts — run with: npx tsx scripts/i18n-watch.ts
import { watch } from "node:fs";
import { execSync } from "node:child_process";
const SOURCE_FILE = "./src/locales/en.json";
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
function pushToLokalise() {
console.log(`[${new Date().toISOString()}] Uploading ${SOURCE_FILE}...`);
try {
execSync("npm run i18n:push", { stdio: "inherit" });
console.log("Upload complete\n");
} catch (err) {
console.error("Upload failed:", (err as Error).message);
}
}
watch(SOURCE_FILE, (eventType) => {
if (eventType !== "change") return;
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(pushToLokalise, 2000); // 2s debounce
});
console.log(`Watching ${SOURCE_FILE} for changes... (Ctrl+C to stop)`);
```
Add to package.json:
```json
{
"scripts": {
"i18n:watch": "npx tsx scripts/i18n-watch.ts"
}
}
```
1. Generate mock translations for offline development and layout testing.
```typescript
// scripts/i18n-mock.ts
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
const source: Record<string, string> = JSON.parse(
readFileSync("./src/locales/en.json", "utf-8")
);
// Pseudo-localization: wraps text in brackets and adds length
function pseudoLocalize(text: string): string {
// Preserve ICU placeholders like {name}, {count, plural, ...}
return text.replace(/([^{}]+)/g, (match) => {
const padded = match.replace(/[a-zA-Z]/g, (c) => {
const base = c === c.toUpperCase() ? 65 : 97;
return String.fromCharCode(((c.charCodeAt(0) - base + 13) % 26) + base);
});
return `[${padded}]`;
});
}
// Generate longer text to test layout overflow
function stretchLocalize(text: string): string {
return `[${text}${"~".repeat(Math.ceil(text.length * 0.3))}]`;
}
const pseudo: Record<string, string> = {};
const stretch: Record<string, string> = {};
for (const [key, value] of Object.entries(source)) {
pseudo[key] = pseudoLocalize(value);
stretch[key] = stretchLocalize(value);
}
mkdirSync("./src/locales", { recursive: true });
writeFileSync("./src/locales/pseudo.json", JSON.stringify(pseudo, null, 2));
writeFileSync("./src/locales/xx-long.json", JSON.stringify(stretch, null, 2));
console.log("Generated pseudo.json and xx-long.json for testing");
```
Use in development:
```typescript
// In your app's locale config, add mock locales for dev only
const devLocales = process.env.NODE_ENV === "development"
? { pseudo: () => import("./locales/pseudo.json"), "xx-long": () => import("./locales/xx-long.json") }
: {};
```
1. Integrate with React i18next (or skip to step 6 for Vue i18n).
```typescript
// src/i18n.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import en from "./locales/en.json";
i18n.use(initReactI18next).init({
resources: {
en: { translation: en },
},
lng: "en",
fallbackLng: "en",
interpolation: { escapeValue: false },
});
// Lazy-load other languages
export async function changeLanguage(lng: string) {
if (!i18n.hasResourceBundle(lng, "translation")) {
const mod = await import(`./locales/${lng}.json`);
i18n.addResourceBundle(lng, "translation", mod.default);
}
await i18n.changeLanguage(lng);
}
export default i18n;
```
1. Integrate with Vue i18n (alternative to step 5).
```typescript
// src/i18n.ts
import { createI18n } from "vue-i18n";
import en from "./locales/en.json";
const i18n = createI18n({
legacy: false,
locale: "en",
fallbackLocale: "en",
messages: { en },
});
// Lazy-load translations
export async function loadLocaleMessages(locale: string) {
if (i18n.global.availableLocales.includes(locale)) {
i18n.global.locale.value = locale;
return;
}
const messages = await import(`./locales/${locale}.json`);
i18n.global.setLocaleMessage(locale, messages.default);
i18n.global.locale.value = locale;
}
export default i18n;
```
1. Add a pre-commit hook to prevent committing stale translations.
```bash
#!/usr/bin/env bash
# .husky/pre-commit (or .git/hooks/pre-commit)
set -euo pipefail
# Only run if locale files are staged
STAGED_LOCALES=$(git diff --cached --name-only -- 'src/locales/*.json' || true)
if [ -z "$STAGED_LOCALES" ]; then
exit 0
fi
echo "Locale files staged — pulling latest translations from Lokalise..."
# Pull latest translations
npm run i18n:pull
# Check if pull changed any staged files
CHANGED=$(git diff --name-only -- 'src/locales/Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.