Skip to content

Contributing a New Locale

This guide explains how to add localization support for a new language to Askimo Desktop. By following these steps, you can help make Askimo accessible to users who speak your language.

Askimo Desktop uses Java’s properties files for localization, with MessageFormat syntax for string interpolation. The localization system automatically discovers and loads available locales based on the properties files present in the resources directory.

Before you start, make sure you have:

  1. A working development environment for Askimo (see Development Guide)
  2. Native or fluent proficiency in the target language
  3. Basic understanding of:
  • Properties file format
  • MessageFormat syntax for placeholders
  • UTF-8 encoding

Navigate to the localization resources directory:

cd desktop/src/main/resources/i18n/

Create a new properties file following the naming convention:

# For language only (e.g., Spanish)
messages_es.properties
 
# For language + region (e.g., Spanish - Mexico)
messages_es_MX.properties
 
# For language + region (e.g., Chinese - Simplified)
messages_zh_CN.properties
 
# For language + region (e.g., Chinese - Traditional)
messages_zh_TW.properties

Naming Convention:

  • Format: messages_[language]_[COUNTRY].properties or messages_[language].properties
  • Use ISO 639-1 language codes (lowercase): en, es, fr, de, ja, zh, etc.
  • Use ISO 3166-1 country codes (uppercase): US, GB, MX, CN, TW, JP, etc.

Start by copying the English properties file as your template:

cp messages.properties messages_[your_locale].properties

For example, to create French localization:

cp messages.properties messages_fr.properties

Open your new properties file and translate each string. Here’s what you need to know:

# Askimo Desktop Application - [Your Language Name] Strings

Most strings are straightforward key-value pairs:

# English
chat.new=New Chat
settings.title=Settings
 
# Your translation (example: French)
chat.new=Nouveau chat
settings.title=Paramètres

Askimo uses MessageFormat syntax with {0}, {1}, {2} for placeholders. These must be preserved in your translation:

# English
about.title=About {0}
about.copyright=Copyright (c) {0} {1}
 
# Your translation (example: French)
about.title=À propos de {0}
about.copyright=Copyright (c) {0} {1}

Important: The placeholders represent dynamic values:

  • {0} = first argument (e.g., “Askimo” in “About Askimo”)
  • {1} = second argument (e.g., “Hai Nguyen” in “Copyright (c) 2025 Hai Nguyen”)
  • And so on…

You can reorder placeholders if needed for your language’s grammar:

# English (subject-verb-object)
validation.min=Value must be at least {0}
 
# Your translation (example: Japanese - different word order)
validation.min={0}以上の値を入力してください

Use \n for line breaks:

# English
chat.provider.model=Provider: {0}\nModel: {1}
 
# Your translation
chat.provider.model=Fournisseur: {0}\nModèle: {1}

For special characters and Unicode, use UTF-8 encoding directly:

# English
sessions.empty.hint=💡 Start a new conversation to create your first session!
 
# Your translation (example: French)
sessions.empty.hint=💡 Démarrez une nouvelle conversation pour créer votre première session!

Follow these best practices for high-quality translations:

  • Use consistent terminology throughout
  • Keep the same level of formality across all strings
  • Maintain consistent capitalization rules for your language
  • Consider where the text appears (button, title, tooltip, error message)
  • Match the tone and style of each context:
  • Buttons: Action-oriented, concise
  • Titles: Clear and descriptive
  • Tooltips: Brief but helpful
  • Error messages: Clear and actionable
  1. Application (app.*)
  • Application name, title, description
  1. Menu Items (menu.*)
  • File, Edit, View, Help menus
  1. Chat Interface (chat.*)
  • Input placeholders, buttons, status messages
  1. Sessions (session.*)
  • Session management, export, rename functions
  1. Settings (settings.*)
  • Configuration options, preferences
  1. Theme (theme.*)
  • Light, dark, system theme descriptions
  1. Models & Providers (model., provider.)
  • AI provider names and configuration
  1. Errors & Validation (error., validation.)
  • Error messages, validation feedback
  1. Actions (action.*)
  • Common actions (copy, paste, save, cancel, etc.)
  1. About Dialog (about.*)
  • Version info, copyright, license
  1. Directives (directive.*)
  • Directive management interface
  1. Language Instructions (language.*)
  • AI communication language directives
# Sessions - English Original
session.new=New Session
session.rename=Rename Session
session.delete=Delete Session
session.star=Star
session.unstar=Unstar
session.starred=Starred
session.all=All Sessions
session.delete.confirm=Are you sure you want to delete this session?
session.export=Export
session.export.tooltip=Export entire chat history
 
# Sessions - French Translation
session.new=Nouvelle session
session.rename=Renommer la session
session.delete=Supprimer la session
session.star=Ajouter aux favoris
session.unstar=Retirer des favoris
session.starred=Favoris
session.all=Toutes les sessions
session.delete.confirm=Êtes-vous sûr de vouloir supprimer cette session ?
session.export=Exporter
session.export.tooltip=Exporter l'historique complet du chat

Step 5: Handle Language-Specific Directives

Section titled “Step 5: Handle Language-Specific Directives”

The application includes special directives that tell the AI how to communicate with users. Translate these carefully:

# Language Directive Templates
language.directive.instruction=LANGUAGE INSTRUCTION: You must communicate with the user in %s. Read user messages in %s and respond in %s. Use natural, conversational %s appropriate for the context.
 
language.directive.fallback=FALLBACK: If you do not support %s or cannot generate proper %s text, respond in English and inform the user that %s is not fully supported.
 
language.name.display=English

Important:

  • The %s placeholders in these strings use String.format syntax (not MessageFormat)
  • These are template strings used by the code, keep %s as-is
  • Only translate language.name.display to your language name
  • In the actual properties file, use \n for line breaks, but here we show them expanded for readability

Example for French:

language.directive.instruction=INSTRUCTION LINGUISTIQUE: Vous devez communiquer avec l'utilisateur en %s. Lisez les messages de l'utilisateur en %s et répondez en %s. Utilisez un %s naturel et conversationnel adapté au contexte.
 
language.directive.fallback=SOLUTION DE REPLI: Si vous ne prenez pas en charge le %s ou ne pouvez pas générer un texte %s correct, répondez en anglais et informez l'utilisateur que le %s n'est pas entièrement pris en charge.
 
language.name.display=Français
  1. Missing placeholders: Ensure all {0}, {1}, etc. are preserved
  2. Encoding issues: Save the file as UTF-8
  3. Syntax errors: No unescaped special characters
  4. Completeness: All keys from English file are present

You can use these shell commands to verify your translation is complete. These commands use Unix utilities (comm, grep, cut, sort) that are available on macOS and Linux.

Check for missing keys (keys in English but not in your translation):

comm -23 <(grep "^[a-z]" messages.properties | cut -d= -f1 | sort) \
         <(grep "^[a-z]" messages_[your_locale].properties | cut -d= -f1 | sort)

Check for extra keys (typos in key names in your translation):

comm -13 <(grep "^[a-z]" messages.properties | cut -d= -f1 | sort) \
         <(grep "^[a-z]" messages_[your_locale].properties | cut -d= -f1 | sort)

If these commands return any output, you have missing or extra keys that need to be fixed.

What these commands do:

  1. Extract all property keys from both files
  2. Sort them alphabetically
  3. Compare the two lists to find differences

Alternative: Manual Verification

If you’re on Windows or prefer a simpler approach:

  1. Open both messages.properties and your translation file

  2. Count the lines in both files (should be similar)

  3. Search for each key prefix in your translation:

    • Search for app. - should find all application strings
    • Search for chat. - should find all chat strings
    • Search for settings. - should find all settings strings
    • And so on for each category
  4. Use your IDE’s “Find in Files” feature to search for any untranslated English text

./gradlew :desktop:build
./gradlew :desktop:run
  1. Open the application
  2. Go to Settings (⚙️)
  3. Find Application Language dropdown
  4. Select your new locale
  5. Navigate through all screens and verify:
  • All text displays correctly
  • No missing translations (showing keys instead)
  • Placeholders are properly replaced with values
  • Text fits in UI elements (not truncated)
  • Special characters render correctly
Language Selection Dropdown

Once you’ve completed and tested your translation:

  1. Commit your changes:
git add desktop/src/main/resources/i18n/messages_[your_locale].properties
git commit -m "Add [Language Name] localization"
  1. Create a pull request:
  • Fork the repository
  • Push your changes to your fork
  • Open a pull request with:
  • Clear title: “Add [Language] localization”
  • Description of what you translated
  • Any notes about language-specific choices you made
  1. Include in your PR description:
  • Native language name (e.g., “Français” for French)
  • Your language proficiency level
  • Any areas where you’d like review
  • Screenshots showing the translation in the app (optional but helpful)

If your language has significant regional differences, create separate files:

# Spanish (generic)
messages_es.properties
 
# Spanish (Mexico)
messages_es_MX.properties
 
# Spanish (Spain)
messages_es_ES.properties

The application will:

  1. Try to load the specific regional variant (e.g., es_MX)
  2. Fall back to the language-only variant (e.g., es)
  3. Fall back to English if neither exists

Common MessageFormat patterns you’ll encounter:

# Simple substitution
text=Hello {0}!
# Usage: "Hello World!"
 
# Multiple arguments
text=Welcome {0}, you have {1} messages.
# Usage: "Welcome John, you have 5 messages."
 
# Reordering (for different grammar)
text.en=Show {0} results for {1}
text.ja={1}の検索結果を{0}件表示
# Both valid, different word order

For RTL languages (Arabic, Hebrew, etc.), the properties file remains the same format. The UI layout will be handled by the application’s RTL support (if implemented).

In properties files, these characters may need special handling:

  • # at line start → comment (use \# if needed in text)
  • ! at line start → comment (use \! if needed in text)
  • = in values → no escaping needed
  • : in values → no escaping needed
  • Backslash \ → use \\ for literal backslash
  • Newline → use \n
  • Tab → use \t

The application automatically discovers available locales by:

  1. Scanning for messages_*.properties files in i18n/ directory
  2. Parsing the locale code from the filename
  3. Building the display name using Locale.getDisplayLanguage()
  4. Adding the locale to the Settings dropdown

No code changes required - just add your properties file and rebuild!

Locales appear in the Settings dropdown with this format:

Native Name (English Name)

Examples:

  • English
  • 日本語 (Japanese)
  • Tiếng Việt (Vietnamese)
  • Français (French)
  • Español (Spanish)

For regional variants:

Native Name - Native Region (English Name - English Region)

Examples:

  • 中文 - 中国 (Chinese - China)
  • 中文 - 台灣 (Chinese - Taiwan)
  • Español - México (Spanish - Mexico)

After your locale is added:

  1. Monitor Updates: Watch for new strings added to messages.properties
  2. Update Your Translation: Add translations for new keys as they’re added
  3. Community Review: Help review updates to your locale from other contributors
  4. Report Issues: If you notice translation errors, open an issue or PR to fix them

If you need assistance:

  1. Look at existing translations: messages_ja_JP.properties and messages_vi_VN.properties are complete examples
  2. Check the source code: See how strings are used in context
  3. Ask questions: Open a GitHub issue with the localization label
  4. Join discussions: Participate in localization discussions in the community

Your contribution helps make Askimo accessible to users around the world. Thank you for taking the time to translate and localize the application! 🌍 ❤️