
Objectstack I18n
- 132 installs
- 18 repo stars
- Updated August 5, 2026
- objectstack-ai/framework
Helps with ai & agent building tasks.
About
objectstack-i18n is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- objectstack-i18n
- AI & Agent Building
- AI-coding skill
Objectstack I18n by the numbers
- 132 all-time installs (skills.sh)
- +4 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #3,608 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/objectstack-ai/framework --skill objectstack-i18nAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 132 |
|---|---|
| repo stars | ★ 18 |
| Last updated | August 5, 2026 |
| Repository | objectstack-ai/framework ↗ |
What it does
Helps with ai & agent building tasks.
Files
Internationalization — ObjectStack I18n Protocol
Expert instructions for designing internationalization (i18n) and localization (l10n) strategies using the ObjectStack specification. This skill covers translation bundle structures, locale configuration, object-first translation patterns, coverage detection, and integration with the I18nService.
---
When to Use This Skill
- You are configuring i18n for a new ObjectStack project.
- You need to create translation bundles for multiple locales.
- You are designing object-first translation structures (per-object translation files).
- You need to detect missing or stale translations (coverage analysis).
- You are integrating AI-powered translation suggestions.
- You are implementing locale-specific formatting (dates, numbers, currency).
Workspace regional defaults — referencetimezone,locale, and `currency`
— live in the localization SETTINGS (tenant-scoped), are resolved onto everyrequest's ExecutionContext, and are exposed to the client atGET /api/v1/auth/me/localization.localization.currencyis the fallback a
currency field/measure uses when it omits its own (ADR-0053).
- You need to understand translation file organization strategies (bundled, per_locale, per_namespace).
---
Core Concepts
Translation Architecture Overview
ObjectStack follows an object-first translation model inspired by Salesforce and Dynamics 365:
1. Object-First Aggregation: All translatable content for an object (labels, fields, options, views, actions) is grouped under a single namespace: o.{object_name}.
2. Global Groups: Non-object-bound translations (apps, navigation, messages) live at the top level.
3. Locale Files: Each locale has its own complete translation bundle (e.g., en.json, zh-CN.json).
4. Coverage Detection: The system can compare translation bundles against source metadata to identify missing, redundant, or stale entries.
---
Translation Configuration
Stack-Level I18n Config
Configure i18n settings in your objectstack.config.ts:
import { defineStack } from '@objectstack/spec';
export default defineStack({
i18n: {
defaultLocale: 'en',
supportedLocales: ['en', 'zh-CN', 'ja-JP', 'es-ES'],
fallbackLocale: 'en',
fileOrganization: 'per_locale',
messageFormat: 'simple', // or 'icu' for complex plurals
lazyLoad: false,
cache: true,
},
});| Property | Type | Default | Description |
|---|---|---|---|
defaultLocale | string | 'en' | Default BCP-47 locale code |
supportedLocales | string[] | ['en'] | All supported locales |
fallbackLocale | string | same as defaultLocale | Fallback when translation missing |
fileOrganization | 'bundled' \ | 'per_locale' \ | 'per_namespace' |
messageFormat | 'simple' \ | 'icu' | 'simple' |
lazyLoad | boolean | false | Load translations on demand |
cache | boolean | true | Cache loaded translations in memory |
BCP-47 Locale Codes: Use standard locale tags (e.g.,en-US,zh-CN,pt-BR,en-GB).
---
File Organization Strategies
1. Bundled (Single File)
All locales in one file. Best for small projects with few objects.
src/translations/
crm.translation.ts # { en: {...}, "zh-CN": {...} }When to use: Fewer than 5 objects, 2-3 locales, < 200 translation keys total.
2. Per-Locale (Recommended)
One file per locale containing all namespaces. Recommended when a single locale file stays under ~500 lines.
src/translations/
en.ts # TranslationData for English
zh-CN.ts # TranslationData for Chinese
ja-JP.ts # TranslationData for JapaneseWhen to use: Medium projects (5-20 objects), 3-5 locales, organized by language.
3. Per-Namespace (Enterprise)
One file per namespace (object) per locale. Recommended for large projects with many objects/languages. Aligns with Salesforce DX and ServiceNow conventions.
i18n/
en/
account.json # ObjectTranslationData
contact.json
project_task.json
common.json # messages + app labels
zh-CN/
account.json
contact.json
project_task.json
common.jsonWhen to use: Large projects (20+ objects), 5+ locales, team collaboration, CI/CD pipelines.
---
Object-First Translation Bundle
AppTranslationBundle Structure
The AppTranslationBundle is the canonical format for a single locale:
const zh: AppTranslationBundle = {
_meta: {
locale: 'zh-CN',
direction: 'ltr',
},
// Object-first translations
o: {
account: {
label: '客户',
pluralLabel: '客户',
description: '客户管理对象',
fields: {
name: { label: '客户名称', help: '公司或组织的法定名称' },
industry: {
label: '行业',
options: { tech: '科技', finance: '金融', retail: '零售' }
},
website: { label: '网站', placeholder: '输入网站地址' },
},
_options: {
status: { active: '活跃', inactive: '停用' },
},
_views: {
all_accounts: { label: '全部客户' },
my_accounts: { label: '我的客户' },
},
_sections: {
basic_info: { label: '基本信息' },
contact_info: { label: '联系方式' },
},
_actions: {
convert_lead: { label: '转换线索', confirmMessage: '确认转换为客户?' },
merge: { label: '合并客户', confirmMessage: '此操作无法撤销,确认合并?' },
},
},
},
// Global picklist options (not object-specific)
_globalOptions: {
currency: { usd: '美元', eur: '欧元', cny: '人民币' },
},
// App-level translations
app: {
crm: { label: '客户关系管理', description: '管理销售流程' },
helpdesk: { label: '服务台', description: '客户支持系统' },
},
// Navigation menu
nav: {
home: '首页',
settings: '设置',
reports: '报表',
admin: '管理',
},
// Dashboard translations
dashboard: {
sales_overview: { label: '销售概览', description: '销售漏斗与目标' },
},
// Report translations
reports: {
pipeline_report: { label: '管道报表' },
},
// Page translations
pages: {
landing: { title: '欢迎', description: '开始使用 ObjectStack' },
},
// UI messages (supports ICU MessageFormat if enabled)
messages: {
'common.save': '保存',
'common.cancel': '取消',
'common.delete': '删除',
'common.confirm': '确认',
'validation.required': '此字段为必填项',
'pagination.showing': '显示 {start} 到 {end},共 {total} 条',
},
// Validation error messages
validationMessages: {
discount_limit: '折扣不能超过40%',
end_date_after_start: '结束日期必须晚于开始日期',
},
// Global notifications
notifications: {
record_created: { title: '创建成功', body: '记录已创建' },
},
// Global error messages
errors: {
'ERR_NETWORK': '网络连接失败',
'ERR_PERMISSION': '权限不足',
},
};---
Object-Level Translation Structure
All translatable content for a single object is aggregated under o.{object_name} with these sub-keys:
| Sub-key | Holds |
|---|---|
label / pluralLabel / description / helpText | Object-level text |
fields.{field_name} | label, help, placeholder, options per field |
_options.{picklist_name} | Object-scoped picklist option labels |
_views.{view_name} | View label / description |
_sections.{section_name} | Form section / tab labels |
_actions.{action_name} | Action label + confirmMessage |
_notifications.{key} / _errors.{code} | Per-object messages |
For the exact Zod shape (and any field that may have been added since), read node_modules/@objectstack/spec/src/system/translation.zod.ts — ObjectTranslationNodeSchema and FieldTranslationSchema.
---
Naming Conventions
| Context | Convention | Example |
|---|---|---|
| Locale codes | BCP-47 | en, en-US, zh-CN, pt-BR |
Object keys in o.* | snake_case | o.project_task, o.support_case |
| Field keys | snake_case | fields.first_name, fields.due_date |
| Option values | lowercase | options.status.in_progress |
| Message keys | dot-separated | common.save, validation.required |
Critical: Object names and field keys in translation bundles must match the snake_case names defined in your Object and Field schemas.---
Translation Coverage & Diff Detection
Coverage Analysis
The II18nService.getCoverage() method compares a translation bundle against source metadata to detect:
1. Missing — Keys that exist in metadata but not in the translation bundle 2. Redundant — Keys in the bundle that have no matching metadata 3. Stale — Keys where the source metadata has changed since translation
const coverage = i18nService.getCoverage('zh-CN', 'account');
console.log(coverage);
// {
// locale: 'zh-CN',
// objectName: 'account',
// totalKeys: 120,
// translatedKeys: 105,
// missingKeys: 12,
// redundantKeys: 3,
// staleKeys: 0,
// coveragePercent: 87.5,
// items: [
// { key: 'o.account.fields.website.label', status: 'missing', locale: 'zh-CN' },
// ...
// ],
// breakdown: [
// { group: 'fields', totalKeys: 45, translatedKeys: 40, coveragePercent: 88.9 },
// { group: 'views', totalKeys: 8, translatedKeys: 8, coveragePercent: 100 },
// ],
// }TranslationDiffItem
Each item in coverage.items carries key (dot path), status (missing | redundant | stale), objectName, locale, optional sourceHash for stale detection, and AI-enrichment fields (aiSuggested, aiConfidence) when suggestTranslations() has been run. Full Zod shape: node_modules/@objectstack/spec/src/system/translation.zod.ts — TranslationDiffItemSchema.
---
AI-Powered Translation Suggestions
Using suggestTranslations()
The II18nService.suggestTranslations() method enriches diff items with AI-generated translations:
const missingItems = coverage.items.filter(item => item.status === 'missing');
const suggestions = await i18nService.suggestTranslations('zh-CN', missingItems);
suggestions.forEach(item => {
console.log(`${item.key}: ${item.aiSuggested} (confidence: ${item.aiConfidence})`);
// o.account.fields.website.label: 网站 (confidence: 0.95)
});Best Practice: AI suggestions work best when:
- You provide source locale context (e.g., English labels)
- You include domain-specific glossaries
- You review and approve suggestions before committing
---
Message Interpolation
Simple Format (Default)
Use {variable} placeholders:
{
"messages": {
"welcome": "Welcome, {userName}!",
"pagination": "Showing {start} to {end} of {total} items"
}
}Usage:
i18n.t('messages.welcome', 'en', { userName: 'Alice' });
// "Welcome, Alice!"ICU MessageFormat
For complex pluralization, gender, and select:
// Enable in stack config
i18n: { messageFormat: 'icu' }{
"messages": {
"inbox": "{count, plural, =0 {No messages} one {1 message} other {# messages}}",
"gender": "{gender, select, male {He} female {She} other {They}} replied"
}
}When to use ICU:
- Languages with complex plural rules (Arabic, Slavic languages)
- Gender-aware translations
- Ordinal numbers (1st, 2nd, 3rd)
- Date/time/number formatting
---
Integration with II18nService
Service Contract
II18nService is the kernel service that loads bundles, resolves keys with fallback chains, reports coverage, and (optionally) generates AI suggestions. Methods:
- `t(key, locale, params?)` — resolve a single key with interpolation
- `getTranslations(locale)` — full snapshot for a locale
- `loadTranslations(locale, bundle)` — programmatic load
- `getLocales()` / `getDefaultLocale()` / `setDefaultLocale()`
- `getAppBundle(locale)` / `loadAppBundle(locale, bundle)` — object-first format
- `getCoverage(locale, objectName?)` — diff vs metadata
- `suggestTranslations(locale, items)` — AI-fill missing keys
Full TypeScript signature lives in node_modules/@objectstack/spec/src/contracts/i18n-service.ts — read it when you need exact parameter shapes.
Plugin Setup
import { ObjectKernel } from '@objectstack/core';
import { I18nServicePlugin } from '@objectstack/service-i18n';
const kernel = new ObjectKernel();
kernel.use(new I18nServicePlugin({
defaultLocale: 'en',
localesDir: './i18n',
fallbackLocale: 'en',
registerRoutes: true, // Auto-register REST endpoints
basePath: '/api/v1/i18n',
}));
await kernel.bootstrap();
const i18n = kernel.getService<II18nService>('i18n');---
Translation Workflow Best Practices
1. Extract Keys from Metadata
Use the CLI or API to extract all translatable keys from your metadata:
objectstack i18n extract --locale zh-CN --output i18n/zh-CN.jsonThis generates a skeleton bundle with all required keys.
2. Translate
Fill in the translations manually, or use AI suggestions:
objectstack i18n suggest --locale zh-CN --input i18n/zh-CN.json3. Validate Coverage
Run coverage analysis to detect missing or stale translations:
objectstack i18n coverage --locale zh-CN4. Commit & Deploy
Commit translation files to version control. ObjectStack automatically loads them at runtime.
---
CRM I18n Blueprint
Reference implementation shape:
- Bundle entry:
src/translations/index.ts(orcrm.translation.ts) - Locale files:
src/translations/{en,zh-CN,ja-JP,es-ES}.ts
Use this structure for metadata apps:
| Layer | CRM Pattern |
|---|---|
| Stack config | i18n.fileOrganization = 'per_locale' with explicit locale list |
| Translation assembly | One TranslationBundle that imports per-locale files |
| Locale content | Object-scoped translations (objects.account.fields.*, _views, _actions) + global app/nav/messages |
| Naming integrity | Translation object/field keys exactly match metadata machine names |
For new locales, copy one locale file as a baseline, then run coverage before release.
---
Advanced Patterns
Namespace Isolation (Multi-Plugin)
When multiple plugins contribute translations, use namespaces to avoid collisions:
const crmBundle: AppTranslationBundle = {
namespace: 'crm',
o: {
account: { label: '客户' },
},
};
const helpdeskBundle: AppTranslationBundle = {
namespace: 'helpdesk',
o: {
ticket: { label: '工单' },
},
};Keys are prefixed: crm.o.account.label, helpdesk.o.ticket.label.
Right-to-Left (RTL) Support
const ar: AppTranslationBundle = {
_meta: {
locale: 'ar',
direction: 'rtl',
},
o: {
account: { label: 'حساب' },
},
};UI frameworks can use _meta.direction to apply RTL CSS.
Translation Memory Integration
Implement custom II18nService.suggestTranslations() to integrate with:
- Translation Management Systems (TMS) like Phrase, Crowdin, Lokalise
- Machine translation APIs (Google Translate, DeepL)
- Internal translation memory databases
---
Common Pitfalls
❌ Mismatched Object Names
Translation keys must match metadata exactly:
// Metadata
{ name: 'project_task' }
// Translation (WRONG)
{ o: { projectTask: { label: '项目任务' } } }
// Translation (CORRECT)
{ o: { project_task: { label: '项目任务' } } }❌ Hardcoded Option Values
Always use lowercase machine values for options:
// Metadata
options: [
{ value: 'in_progress', label: 'In Progress' },
]
// Translation (WRONG)
options: { 'In Progress': '进行中' }
// Translation (CORRECT)
options: { in_progress: '进行中' }❌ Ignoring Coverage Reports
Stale translations can cause confusion. Always run coverage analysis before releases.
---
Quick-Start Template
// i18n/zh-CN.ts
import type { AppTranslationBundle } from '@objectstack/spec';
export default {
_meta: {
locale: 'zh-CN',
direction: 'ltr',
},
o: {
account: {
label: '客户',
pluralLabel: '客户',
fields: {
name: { label: '客户名称' },
email: { label: '邮箱', placeholder: '输入邮箱地址' },
status: {
label: '状态',
options: {
active: '活跃',
inactive: '停用',
},
},
},
_views: {
all_accounts: { label: '全部客户' },
},
},
},
app: {
crm: { label: '客户关系管理' },
},
nav: {
home: '首页',
settings: '设置',
},
messages: {
'common.save': '保存',
'common.cancel': '取消',
},
} satisfies AppTranslationBundle;---
Verify your work
After editing a *.translation.ts bundle:
os i18n check # translation coverage vs the default locale (missing-key report)
os validate # the bundle conforms to the protocol schema (no artifact)
# or: os build # the same schema gate, plus emits dist/os i18n check lists keys missing per locale; os lint --i18n-strict turns coverage gaps into hard errors. In a scaffolded project the schema gate is npm run validate. See objectstack-platform → Verify your work.
---
References
See references/_index.md for the full list of Zod schemas (with one-line descriptions) — pointers into node_modules/@objectstack/spec/src/. Always Read the source for exact field shapes; do not rely on memory of property names.
See Also
- objectstack-data — For understanding object and field metadata structure
- objectstack-ui — For view, app, and action translations
- objectstack-automation — For workflow and flow message translations
Evaluation Tests (evals/)
This directory is reserved for future skill evaluation tests.
Purpose
Evaluation tests (evals) validate that AI assistants correctly understand and apply the rules defined in this skill when generating code or providing guidance.
Structure
When implemented, evals will follow this structure:
evals/
├── naming/
│ ├── test-object-names.md
│ ├── test-field-keys.md
│ └── test-option-values.md
├── relationships/
│ ├── test-lookup-vs-master-detail.md
│ └── test-junction-patterns.md
├── validation/
│ ├── test-script-inversion.md
│ └── test-state-machine.md
└── ...Format
Each eval file will contain: 1. Scenario — Description of the task 2. Expected Output — Correct implementation 3. Common Mistakes — Incorrect patterns to avoid 4. Validation Criteria — How to score the output
Status
⚠️ Not yet implemented — This is a placeholder for future development.
Contributing
When adding evals: 1. Each eval should test a single, specific rule or pattern 2. Include both positive (correct) and negative (incorrect) examples 3. Reference the corresponding rule file in rules/ 4. Use realistic scenarios from actual ObjectStack projects
objectstack-i18n — Schema References
Auto-generated by packages/spec/scripts/build-skill-references.ts.Do not edit — re-run pnpm --filter @objectstack/spec run gen:skill-refs to update.Schemas live in the published @objectstack/spec package. Read them directly from node_modules — there is no local copy in the skill bundle.
Core schemas
node_modules/@objectstack/spec/src/system/translation.zod.ts— Field Translation Schemanode_modules/@objectstack/spec/src/ui/i18n.zod.ts— I18n Object Schema
Transitive dependencies
node_modules/@objectstack/spec/src/shared/lazy-schema.ts— Wrap a Zod schema constructor so its body is only evaluated on first use.
How to read these
1. The schemas are runtime Zod definitions. Use Read on the absolute path under node_modules/@objectstack/spec/src/ to inspect field shapes, .describe() text, enums, and refinements. 2. TypeScript types: import type { … } from '@objectstack/spec' (or the matching subpath export). 3. Runtime values: import { … } from '@objectstack/spec' — the package re-exports every schema and helper.