Lokalise Hello World
Overview
End-to-end walkthrough: list projects, create a test project, add keys, set translations across languages, and retrieve them. Covers both the Node SDK (@lokalise/node-api) and the CLI (lokalise2).
Prerequisites
- Lokalise API token exported as
LOKALISE_API_TOKEN - Node.js 18+ with
@lokalise/node-apiinstalled (npm i @lokalise/node-api) - Lokalise CLI v2 installed (
brew install lokalise2or binary releases)
Instructions
- List all projects using the SDK and CLI.
import { LokaliseApi } from "@lokalise/node-api";
const client = new LokaliseApi({ apiKey: process.env.LOKALISE_API_TOKEN! });
const projects = await client.projects().list({ page: 1, limit: 20 });
for (const p of projects.items) {
console.log(`${p.project_id} ${p.name} (${p.statistics.languages} languages)`);
}
set -euo pipefail
lokalise2 --token "$LOKALISE_API_TOKEN" project list
- Create a test project with three languages.
const project = await client.projects().create({
name: "hello-world-test",
description: "Quick start demo",
languages: [
{ lang_iso: "en", custom_name: "English" },
{ lang_iso: "fr", custom_name: "French" },
{ lang_iso: "de", custom_name: "German" },
],
base_language_iso: "en",
});
const PROJECT_ID = project.project_id;
console.log(`Created project: ${PROJECT_ID}`);
- Add translation keys with their English (base language) translations in a single call.
const keys = await client.keys().create({
project_id: PROJECT_ID,
keys: [
{
key_name: { web: "greeting.hello" },
platforms: ["web"],
translations: [{ language_iso: "en", translation: "Hello" }],
},
{
key_name: { web: "greeting.goodbye" },
platforms: ["web"],
translations: [{ language_iso: "en", translation: "Goodbye" }],
},
{
key_name: { web: "app.title" },
platforms: ["web"],
translations: [{ language_iso: "en", translation: "My Application" }],
},
],
});
console.log(`Created ${keys.items.length} keys`);
- Set translations for French and German by retrieving key IDs and updating each translation.
const allKeys = await client.keys().list({
project_id: PROJECT_ID,
limit: 100,
});
const translations: Record<string, Record<string, string>> = {
"greeting.hello": { fr: "Bonjour", de: "Hallo" },
"greeting.goodbye": { fr: "Au revoir", de: "Auf Wiedersehen" },
"app.title": { fr: "Mon Application", de: "Meine Anwendung" },
};
for (const key of allKeys.items) {
const keyName = key.key_name.web;
const langs = translations[keyName];
if (!langs) continue;
for (const [langIso, value] of Object.entries(langs)) {
const existing = key.translations.find(
(t: { language_iso: string }) => t.language_iso === langIso
);
if (existing) {
await client.translations().update(existing.translation_id, {
project_id: PROJECT_ID,
translation: value,
});
}
}
}
console.log("Translations set for fr and de");
- Retrieve and display all translations grouped by key.
const result = await client.translations().list({
project_id: PROJECT_ID,
page: 1,
limit: 100,
});
const grouped = new Map<number, { key: string; langs: Record<string, string> }>();
for (const t of result.items) {
if (!grouped.has(t.key_id)) {
grouped.set(t.key_id, { key: `key:${t.key_id}`, langs: {} });
}
grouped.get(t.key_id)!.langs[t.language_iso] = t.translation;
}
for (const [, entry] of grouped) {
console.log(`\n${entry.key}`);
for (const [lang, text] of Object.entries(entry.langs)) {
console.log(` ${lang}: ${text}`);
}
}
- Verify via CLI by listing keys and exporting translations.
set -euo pipefail
PROJECT_ID="YOUR_PROJECT_ID"
# List keys
lokalise2 --token "$LOKALISE_API_TOKEN" key list \
--project-id "$PROJECT_ID" \
--limit 100
# Export all translations as JSON
lokalise2 --token "$LOKALISE_API_TOKEN" file download \
--project-id "$PROJECT_ID" \
--format json \
--original-filenames=false \
--bundle-structure "%LANG_ISO%.json" \
--unzip-to ./locales
Output
- A new Lokalise project with 3 keys and translations in 3 languages
- Console output showing all key/translation pairs
- Exported JSON files in
./locales/(if CLI step run)
Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| 401 Unauthorized | Invalid or expired API token | Verify LOKALISE_API_TOKEN is set and valid |
| 400 Bad Request | Missing required fields (e.g., key_name) | Check payload matches API schema |
| 404 Not Found | Project ID does not exist | Run project list to get correct ID |
| 429 Too Many Requests | Exceeded 6 req/sec rate limit | Add 170ms delay between calls or batch operations |
| Cannot find module | SDK not installed | Run npm i @lokalise/node-api |
Examples
Minimal One-File Script
// hello-lokalise.ts — run with: npx tsx hello-lokalise.ts
import { LokaliseApi } from "@lokalise/node-api";
const api = new LokaliseApi({ apiKey: process.env.LOKALISE_API_TOKEN! });
// Create project
const proj = await api.projects().create({
name: `demo-${Date.now()}`,
languages: [{ lang_iso: "en" }, { lang_iso: "es" }],
base_language_iso: "en",
});
// Add a key with translations
await api.keys().create({
project_id: proj.project_id,
keys: [{
key_name: { web: "welcome" },
platforms: ["web"],
translations: [
{ language_iso: "en", translation: "Welcome" },
{ language_iso: "es", translation: "Bienvenido" },
],
}],
});
// Read it back
const translations = await api.translations().list({
project_id: proj.project_id,
limit: 10,
});
for (const t of translations.items) {
console.log(`[${t.language_iso}] ${t.translation}`);
}
// Cleanup
await api.projects().delete(proj.project_id);
console.log("Project deleted");
CLI-Only Quick Test
set -euo pipefail
# Create project
PROJECT=$(lokalise2 --token "$LOKALISE_API_TOKEN" project create \
--name "cli-test-$(date +%s)" \
--base-language-iso en \
--languages '[{"lang_iso":"en"},{"lang_iso":"ja"}]' 2>&1)
PROJECT_ID=$(echo "$PROJECT" | grep -oP 'Project ID: \K[^\s]+')
# Upload a source file
echo '{"hello":"Hello","bye":"Bye"}' > /tmp/en.json
lokalise2 --token "$LOKALISE_API_TOKEN" file upload \
--project-id "$PROJECT_ID" \
--file /tmp/en.json \
--lang-iso en \
--poll
echo "Project $PROJECT_ID created and source uploaded"
Resources
Next Steps
Proceed to lokalise-local-dev-loop for development workflow setup, or lokalise-core-workflow-a for file upload and key management.