Skip to main content

Localization & Internationalization

Local GPT supports 11 languages out of the box, with automatic language detection and a community-driven translation system.

Supported Languages

The plugin includes complete translations for:

English

en - Default language

German

de - Deutsch

Spanish

es - Español

French

fr - Français

Italian

it - Italiano

Japanese

ja - 日本語

Korean

ko - 한국어

Dutch

nl - Nederlands

Portuguese

pt - Português

Russian

ru - Русский

Chinese

zh - 中文

Language Detection

Local GPT uses Obsidian’s language setting automatically. The active language is determined by:
const locale = window.localStorage.getItem("language") || "en";
No manual configuration is needed - the plugin adapts to your Obsidian interface language.

How Language Detection Works

For advanced use cases like detecting the language of user content, Local GPT includes a sophisticated language detection system in src/languageDetection.ts.

Script-Based Detection

The system first checks for distinctive writing systems:
Detects Unicode ranges:
  • U+3040 - U+309F: Hiragana
  • U+30A0 - U+30FF: Katakana
  • U+31F0 - U+31FF: Katakana Phonetic Extensions
  • U+FF65 - U+FF9F: Halfwidth Katakana
Detects Unicode ranges:
  • U+3400 - U+4DBF: CJK Unified Ideographs Extension A
  • U+4E00 - U+9FFF: CJK Unified Ideographs
When Japanese text contains Han characters (Kanji), they are counted as Japanese if Hiragana/Katakana is also present.
Detects Unicode ranges:
  • U+1100 - U+11FF: Hangul Jamo
  • U+3130 - U+318F: Hangul Compatibility Jamo
  • U+AC00 - U+D7AF: Hangul Syllables
Detects Unicode ranges:
  • U+0400 - U+04FF: Cyrillic
  • U+0500 - U+052F: Cyrillic Supplement
  • U+2DE0 - U+2DFF: Cyrillic Extended-A
  • U+A640 - U+A69F: Cyrillic Extended-B
The ru code represents Cyrillic script used by Russian, Ukrainian, Bulgarian, Serbian, and other languages. The detector identifies the script, not the specific language.
Also supports:
  • Arabic (ar): U+0600 - U+06FF, U+0750 - U+077F, U+08A0 - U+08FF
  • Hebrew (he): U+0590 - U+05FF
  • Greek (el): U+0370 - U+03FF, U+1F00 - U+1FFF
  • Hindi/Devanagari (hi): U+0900 - U+097F

Latin-Script Detection

For Latin-based languages (English, Spanish, French, etc.), the system uses: Character-based scoring:
  • Distinctive diacritics (ñ, ü, ç, õ, etc.)
  • Weight: 3 points per character
Word-based scoring:
  • Common words (“the”, “der”, “le”, “el”, etc.)
  • Weight: 2 points per occurrence
Example detection logic:
// French detection
chars: ["œ", "æ", "ç", "é", "è", "ê", "ë", ...]
words: ["le", "la", "les", "est", "pour", "que", ...]

// Spanish detection  
chars: ["ñ", "á", "é", "í", "ó", "ú", "¿", "¡"]
words: ["el", "la", "que", "para", "con", "muy", ...]

Performance Optimization

const MAX_ANALYSIS_LENGTH = 1000;
const MIN_LATIN_LETTERS_THRESHOLD = 10;
  • Only the first 1,000 characters are analyzed
  • Latin language detection requires at least 10 letters to avoid false positives on short text

Translation System

The i18n system is implemented in src/i18n/index.ts:
export class I18n {
  static t(key: string, params?: { [key: string]: string }): string {
    const locale = window.localStorage.getItem("language") || "en";
    const keys = key.split(".");
    let translations = locales[locale] || locales["en"];
    
    // Traverse nested keys
    for (const k of keys) {
      if (translations?.[k] === undefined) {
        // Fall back to English
        translations = locales["en"];
      }
      translations = translations[k];
    }
    
    // String interpolation
    if (params) {
      Object.entries(params).forEach(([key, value]) => {
        result = result.replace(`{{${key}}}`, value);
      });
    }
    
    return result;
  }
}

Translation File Structure

Translations are stored in src/i18n/*.json files:
{
  "commands": {
    "showContextMenu": "Show context menu",
    "actionPalette": {
      "name": "Action Palette",
      "placeholder": "Your prompt... | Enter: send, Esc: cancel"
    }
  },
  "notices": {
    "errorGenerating": "Error while generating text: {{message}}"
  },
  "settings": {
    "creativity": "Creativity",
    "creativityLow": "💡 Low"
  }
}

Using Translations

In the code, translations are accessed via dot notation:
import { I18n } from "./i18n";

// Simple translation
I18n.t("commands.showContextMenu")
// → "Show context menu"

// With parameters
I18n.t("notices.errorGenerating", { message: "Connection failed" })
// → "Error while generating text: Connection failed"

Contributing Translations

We welcome community contributions to improve translations or add new languages!
1

Fork the Repository

2

Choose a Language

To improve an existing translation, edit src/i18n/[code].json.To add a new language:
  1. Copy src/i18n/en.json to src/i18n/[code].json
  2. Add the import in src/i18n/index.ts:
import [code] from "./[code].json";
  1. Add to the locales object:
const locales: { [key: string]: any } = {
  // ...
  [code],
};
3

Translate Strings

Translate all string values in the JSON file. Keep:
  • JSON structure identical
  • Placeholder variables like {{message}} unchanged
  • Emoji prefixes (💡, 🎨, 🚀) if they add meaning
4

Test Your Translation

  1. Build the plugin: npm run build
  2. Change Obsidian’s language to your locale
  3. Verify all UI strings appear correctly
5

Submit a Pull Request

Create a PR with:
  • Clear title: “Add [Language] translation” or “Improve [Language] translation”
  • Description of what was changed
  • Mention if you’re a native speaker
When translating, prioritize:
  • Accuracy: Match the original meaning
  • Brevity: UI space is limited
  • Natural phrasing: Use how native speakers would say it
  • Consistency: Use the same terms throughout

Translation Guidelines

Placeholders like {{message}}, {{name}}, {{percent}} must remain unchanged:Correct:
"errorGenerating": "Fehler beim Generieren: {{message}}"
Incorrect:
"errorGenerating": "Fehler beim Generieren: {{nachricht}}"
Do not change key names, only values:Correct:
{
  "commands": {
    "showContextMenu": "Menú contextual"
  }
}
Incorrect:
{
  "comandos": {
    "mostrarMenuContextual": "Menú contextual"
  }
}
Emojis can be kept, adapted, or removed based on cultural appropriateness:Option 1 - Keep:
"creativityLow": "💡 Niedrig"
Option 2 - Adapt:
"creativityLow": "🔆 Niedrig"
Option 3 - Remove:
"creativityLow": "Niedrig"
Verify your translations work in:
  • Long text: Does it fit in the UI?
  • Short text: Is it still clear?
  • With placeholders: Does the sentence flow naturally?
Example:
"actionRewritten": "Rewritten \"{{name}}\" action"
Should work with both short names (“Fix”) and long names (“Summarize this document in detail”).

Fallback Behavior

If a translation key is missing:
  1. The system logs a warning: Translation missing: [key]
  2. Falls back to English (en)
  3. If English is also missing, returns the raw key
if (translations?.[k] === undefined) {
  logger.warn(`Translation missing: ${key}`);
  translations = locales["en"];
  // ... attempt to load from English
}
Missing translations won’t crash the plugin, but users will see English text instead.

Community Actions Localization

Community actions can be filtered by language in the settings:
  • Setting: settings.communityActionsLanguage
  • Default: Matches Obsidian’s active language
  • Purpose: Shows community-contributed actions in your language
This allows users to discover actions written in their preferred language for better prompt clarity.

Source Code References

i18n System

src/i18n/index.ts - Translation loader and interpolation

Language Detection

src/languageDetection.ts - Content language detection

Translation Files

src/i18n/*.json - All language files

English (Reference)

src/i18n/en.json - Base translation file

Next Steps

Contributing Guide

Learn how to contribute to the project

Troubleshooting

Debug language detection and display issues

Build docs developers (and LLMs) love