@shipi18n/api
v1.0.6
Published
Official Shipi18n API client for Node.js - translate JSON, text, and i18n files
Maintainers
Readme
@shipi18n/api
Official Node.js client for the Shipi18n translation API. Translate JSON, text, and i18n files with a simple, type-safe API.
Why Shipi18n?
- Stop copy-pasting into Google Translate - One API call translates to 100+ languages
- Placeholders stay intact -
{name},{{count}},%sare preserved automatically - i18next-native - Built-in pluralization, namespaces, ICU MessageFormat support
- 90-day Translation Memory - Same content? Cached. No extra cost.
- Key-based pricing - Pay for unique strings, not characters or API calls
Installation
npm install @shipi18n/apiQuick Start
import { Shipi18n } from '@shipi18n/api';
const shipi18n = new Shipi18n({
apiKey: 'your-api-key', // Get your API key at https://shipi18n.com
});
// Translate JSON
const result = await shipi18n.translateJSON({
content: {
greeting: 'Hello',
farewell: 'Goodbye',
},
sourceLanguage: 'en',
targetLanguages: ['es', 'fr', 'de'],
});
console.log(result.es); // { greeting: 'Hola', farewell: 'Adiós' }
console.log(result.fr); // { greeting: 'Bonjour', farewell: 'Au revoir' }
console.log(result.de); // { greeting: 'Hallo', farewell: 'Auf Wiedersehen' }Features
- JSON Translation - Translate nested JSON objects while preserving structure
- Placeholder Preservation - Keeps
{name},{{count}},%splaceholders intact - i18next Support - Full support for pluralization, namespaces, and ICU MessageFormat
- TypeScript - Full type definitions included
- Zero Dependencies - Uses native fetch (Node.js 18+)
API Reference
Constructor
const shipi18n = new Shipi18n({
apiKey: 'your-api-key', // Required
baseUrl: 'https://ydjkwckq3f.execute-api.us-east-1.amazonaws.com', // Optional, default shown
timeout: 30000, // Optional, request timeout in ms
});translateJSON(options)
Translate JSON content to multiple languages.
const result = await shipi18n.translateJSON({
content: { greeting: 'Hello' }, // Object or JSON string
sourceLanguage: 'en',
targetLanguages: ['es', 'fr'],
preservePlaceholders: true, // Default: true
enablePluralization: true, // Default: true (i18next-style)
htmlHandling: 'none', // 'none' | 'strip' | 'decode' | 'preserve'
namespace: 'common', // Optional: wrap output in namespace
groupByNamespace: 'auto', // 'auto' | 'true' | 'false'
exportPerNamespace: false, // Split output by namespace
skipKeys: ['brandName'], // Skip exact key paths from translation
skipPaths: ['states.*'], // Skip using glob patterns (*, **)
});HTML Handling Modes:
| Mode | Description |
|------|-------------|
| none | Leave HTML as-is (default) |
| strip | Remove all HTML tags |
| decode | Decode HTML entities (& → &) |
| preserve | Keep HTML tags and translate text between them |
translateText(options)
Translate plain text to multiple languages.
const result = await shipi18n.translateText({
content: 'Hello, world!', // String or string[]
sourceLanguage: 'en',
targetLanguages: ['es', 'fr'],
preservePlaceholders: true,
htmlHandling: 'none', // 'none' | 'strip' | 'decode' | 'preserve'
});
// result.es = [{ original: 'Hello, world!', translated: '¡Hola, mundo!' }]translateI18next(options)
Convenience method for i18next files with all features enabled.
const result = await shipi18n.translateI18next({
content: {
common: {
greeting: 'Hello, {{name}}!',
items_one: '{{count}} item',
items_other: '{{count}} items',
},
},
sourceLanguage: 'en',
targetLanguages: ['es', 'fr', 'de'],
});Fallback Options
Handle missing translations gracefully with built-in fallback support:
const result = await shipi18n.translateJSON({
content: { greeting: 'Hello', farewell: 'Goodbye' },
sourceLanguage: 'en',
targetLanguages: ['es', 'pt-BR', 'zh-TW'],
fallback: {
fallbackToSource: true, // Use source content when translation missing (default: true)
regionalFallback: true, // pt-BR → pt, zh-TW → zh fallback (default: true)
fallbackLanguage: 'en', // Custom fallback language (optional)
},
});
// If pt-BR translation fails, uses pt translation
// If pt also fails, uses English source content
// Check what fallbacks were used:
if (result.fallbackInfo?.used) {
console.log(result.fallbackInfo.regionalFallbacks); // { 'pt-BR': 'pt' }
console.log(result.fallbackInfo.languagesFallbackToSource); // ['zh-TW']
console.log(result.fallbackInfo.keysFallback); // { es: ['farewell'] }
}Fallback behavior: | Scenario | Behavior | |----------|----------| | Missing translation for language | Falls back to regional variant (pt-BR → pt), then source | | Missing translation for key | Fills key from source content | | API error | Returns source content for all languages (if enabled) |
Skipping Keys
Exclude specific keys or patterns from translation - useful for brand names, US state codes, or config values that should remain untranslated:
const result = await shipi18n.translateJSON({
content: {
greeting: 'Hello',
brandName: 'Acme Inc', // Should stay as-is
states: { CA: 'California', NY: 'New York' }, // State names
config: { api: { secret: 'xyz' } },
},
sourceLanguage: 'en',
targetLanguages: ['es', 'fr'],
skipKeys: ['brandName', 'config.api.secret'], // Exact paths
skipPaths: ['states.*'], // Glob patterns
});
// Skipped keys are preserved in original language
// result.es.brandName === 'Acme Inc'
// result.es.states.CA === 'California'
// Check what was skipped:
if (result.skipped) {
console.log(`Skipped ${result.skipped.count} keys:`, result.skipped.keys);
}Pattern Matching:
| Pattern | Matches |
|---------|---------|
| states.CA | Exact path only |
| states.* | states.CA, states.NY (single level) |
| config.*.secret | config.api.secret, config.db.secret |
| **.internal | Any path ending with .internal |
Examples
Nested JSON with Namespaces
const result = await shipi18n.translateJSON({
content: {
common: {
buttons: {
submit: 'Submit',
cancel: 'Cancel',
},
},
checkout: {
total: 'Total: {{amount}}',
pay: 'Pay Now',
},
},
sourceLanguage: 'en',
targetLanguages: ['es'],
});
// Namespaces are auto-detected and preserved
console.log(result.es);
// {
// common: { buttons: { submit: 'Enviar', cancel: 'Cancelar' } },
// checkout: { total: 'Total: {{amount}}', pay: 'Pagar ahora' }
// }Pluralization (i18next-style)
const result = await shipi18n.translateJSON({
content: {
items_one: '{{count}} item',
items_other: '{{count}} items',
},
sourceLanguage: 'en',
targetLanguages: ['ru'], // Russian has more plural forms
});
// Automatically generates correct plural forms for each language
console.log(result.ru);
// {
// items_one: '{{count}} элемент',
// items_few: '{{count}} элемента',
// items_many: '{{count}} элементов',
// items_other: '{{count}} элементов'
// }ICU MessageFormat
const result = await shipi18n.translateJSON({
content: {
welcome: '{gender, select, male {Welcome, Mr. {name}} female {Welcome, Ms. {name}} other {Welcome, {name}}}',
},
sourceLanguage: 'en',
targetLanguages: ['es'],
});
// ICU syntax is preserved, only translatable text is translatedExport Per Namespace (for separate files)
const result = await shipi18n.translateJSON({
content: {
common: { greeting: 'Hello' },
checkout: { pay: 'Pay' },
},
sourceLanguage: 'en',
targetLanguages: ['es', 'fr'],
exportPerNamespace: true,
});
// result.namespaceFiles contains pre-split translations:
// {
// common: { es: { greeting: 'Hola' }, fr: { greeting: 'Bonjour' } },
// checkout: { es: { pay: 'Pagar' }, fr: { pay: 'Payer' } }
// }
// result.namespaceFileNames suggests file names:
// [
// { namespace: 'common', files: ['common.es.json', 'common.fr.json'] },
// { namespace: 'checkout', files: ['checkout.es.json', 'checkout.fr.json'] }
// ]Error Handling
import { Shipi18n, Shipi18nError } from '@shipi18n/api';
try {
const result = await shipi18n.translateJSON({ ... });
} catch (error) {
if (error instanceof Shipi18nError) {
console.error(`Error ${error.statusCode}: ${error.message}`);
console.error(`Code: ${error.code}`);
}
}Error Codes
| Code | Description |
|------|-------------|
| MISSING_API_KEY | API key not provided |
| INVALID_API_KEY | API key is invalid |
| QUOTA_EXCEEDED | Monthly character limit reached |
| RATE_LIMITED | Too many requests |
| TIMEOUT | Request timed out |
| NETWORK_ERROR | Network connection failed |
Supported Languages
Over 100 languages supported. Common codes:
| Code | Language |
|------|----------|
| en | English |
| es | Spanish |
| fr | French |
| de | German |
| it | Italian |
| pt | Portuguese |
| zh | Chinese |
| ja | Japanese |
| ko | Korean |
| ar | Arabic |
| ru | Russian |
| hi | Hindi |
Get Your API Key
- Sign up at shipi18n.com
- Go to Dashboard > API Keys
- Generate a new API key
Documentation & Resources
📚 Full Documentation: shipi18n.com/integrations/nodejs-sdk
| Resource | Link | |----------|------| | Getting Started | shipi18n.com | | API Reference | shipi18n.com/api | | i18next Best Practices | shipi18n.com/integrations/react | | Blog & Tutorials | shipi18n.com/blog |
Related Packages
| Package | Description | |---------|-------------| | @shipi18n/cli | CLI tool for translating files | | vite-plugin-shipi18n | Vite plugin for build-time translation | | i18next-shipi18n-backend | i18next backend for dynamic loading | | shipi18n-github-action | GitHub Action for CI/CD |
Examples
- Node.js Example - Basic usage examples
- Vue Example - Vue 3 + vue-i18n integration
License
MIT
