i18n+

pending

by dangehub

Manage translations for plugins with hot-reload support and crowdsourced dictionaries.

Updated 2mo agoMITDiscovered via Obsidian Unofficial Plugins
View on GitHub

i18n+

GitHub release License: MIT

中文文档

A universal internationalization (i18n) framework for the Obsidian plugin ecosystem. Provides zero-dependency adapters, automated migration tools, and seamless multi-language support.

✨ Features

  • Zero Runtime Dependency: Plugins work perfectly without I18n Plus installed
  • Standalone + Mixed Mode: Built-in languages work independently; external dictionaries can override/extend them
  • Automated Migration: Transform hardcoded strings to t() calls with one command
  • Hot Reload: Switch languages instantly without restarting plugins
  • Community Translations: Users can import third-party translations without code changes

🚀 Quick Start

For Plugin Developers: See the full Migration Guide for detailed integration instructions.

For Plugin Developers

  1. Copy the adapter to your plugin:

    cp templates/adapter.ts your-plugin/src/lang/i18n.ts
    
  2. Initialize in main.ts:

    import { initI18n } from './lang/i18n';
    
    export default class MyPlugin extends Plugin {
        i18n: I18nAdapter;
        t: (key: string, params?: any) => string;
        
        async onload() {
            this.i18n = initI18n(this);
            this.t = this.i18n.t.bind(this.i18n);
        }
    }
    
  3. Use translations:

    new Notice(this.t("Hello, {name}!", { name: "World" }));
    

Automated Migration

Run the codemod to automatically replace hardcoded strings:

# Install jscodeshift
npm install -g jscodeshift

# Run codemod on your plugin
npx jscodeshift -t scripts/i18n-codemod.cjs your-plugin/src/ --parser=ts

# Extract keys to generate en.ts
node scripts/extract-keys.cjs your-plugin/src

📦 How It Works

Priority System

When t("key") is called, the adapter searches in this order:

  1. External Dictionary (loaded via I18n Plus)
  2. Built-in Language (current locale)
  3. Last Successful Locale (smart fallback to previous working language)
  4. Base Locale (configurable, defaults to English)
  5. Raw Key

This means:

  • Users can override built-in translations with custom JSON files
  • New languages can be added without modifying plugin code
  • If a new language fails, it falls back to the last working language (not hardcoded English)
  • Plugins work offline without I18n Plus installed

Architecture

┌─────────────────────────────────────────────────────────┐
│                     Your Plugin                          │
│  ┌─────────────────────────────────────────────────┐    │
│  │  adapter.ts (self-contained, ~150 lines)        │    │
│  │  ├── BUILTIN_LOCALES: { en, zh, ... }           │    │
│  │  └── _externalDictionaries: { de, fr, ... }     │    │
│  └─────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────┘
                          ▲
                          │ (optional)
                          ▼
┌─────────────────────────────────────────────────────────┐
│               I18n Plus Plugin (optional)                │
│  ├── Dictionary Manager UI                               │
│  ├── Global Locale Sync                                  │
│  └── External .json Import/Export                        │
└─────────────────────────────────────────────────────────┘

🛠️ Scripts

ScriptDescription
i18n-codemod.cjsTransform hardcoded strings to t() calls
extract-keys.cjsExtract all keys and generate en.ts
inject-i18n.cjsAuto-inject adapter into main.ts
generate-report.cjsGenerate migration report

🔧 Development

Available Commands

CommandDescription
npm run devStart development mode with hot reload
npm run buildBuild the plugin (output to project root)
npm run deployBuild and copy to Obsidian test vault
npm run lintRun ESLint checks

Deploy to Test Vault

The deploy command automatically copies build artifacts to your local Obsidian vault for testing.

Setup:

  1. Create deploy.config.local.json in project root:

    {
        "targetDir": "C:\\path\\to\\your\\.obsidian\\plugins\\i18n-plus"
    }
    
  2. Run:

    npm run deploy
    

Note: deploy.config.local.json is gitignored to keep your local paths private.

📁 Project Structure

templates/
  └── adapter.ts          # Copy this to your plugin
scripts/
  ├── i18n-codemod.cjs    # String replacement codemod
  ├── extract-keys.cjs    # Key extraction script
  └── inject-i18n.cjs     # Auto-injection script
examples/
  └── auto-migrate-workflow.yml  # GitHub Action template
docs/
  ├── README.zh-CN.md     # Chinese documentation
  └── I18N_MIGRATION_GUIDE.zh-CN.md  # Migration guide

🚨 Vibe Coding Warning

This project was built using Vibe Coding. While I have done my best to ensure the reliability of the code, please do not use this project if you are uncomfortable with this approach.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

MIT License - see LICENSE for details.

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.