Translation & Localization System

Developer guide for the Bus Pirate translation system — how strings are managed, translated, and selected at runtime.


How T_ Constants Work

Every user-facing string in Bus Pirate firmware is represented by a T_ constant defined in the auto-generated src/translation/base.h. These constants are members of the enum T_translations:

enum T_translations{
    T_ON=0,
    T_OFF,
    T_GND,
    T_INPUT,
    T_OUTPUT,
    T_EXIT,
    // ... hundreds of T_ constants ...
    T_LAST_ITEM_ALWAYS_AT_THE_END // compiler sentinel
};

Do NOT edit base.h directly. It is auto-generated by json2h.py from parsing src/translation/en-us.h and the template src/translation/templates/base.ht.

At runtime, GET_T(T_CONSTANT) retrieves the localized string for the currently selected language:

printf("%s%s%s\r\n", ui_term_color_info(), GET_T(T_USE_PREVIOUS_SETTINGS), ui_term_color_reset());

The GET_T() implementation (in src/translation/base.c) provides automatic fallback — if a translation is missing (NULL), the English string is returned instead:

const char* GET_T(enum T_translations index) {
    const char* result = NULL;
    if (index < T_LAST_ITEM_ALWAYS_AT_THE_END) {
        translation_table_t* table = translation_tables[current_language];
        result = (*table)[index];
    }
    // fallback to en-US if no translated string was provided
    if (result == NULL) {
        translation_table_t* table = translation_tables[language_idx_en_us];
        result = (*table)[index];
    }
    return result;
}

Adding a New String

  1. Add the string to src/translation/en-us.h — this is the English source of truth. Each entry maps a T_ constant to its English text:

    static char const * const en_us[]={
        [T_ON]="ON",
        [T_OFF]="OFF",
        [T_USE_PREVIOUS_SETTINGS]="Use previous settings?",
        // add your new string here:
        [T_YOUR_NEW_KEY]="Your new user-facing string",
    };
  2. Run json2h.py from the translation directory to regenerate base.h:

    cd src/translation && python json2h.py

    This parses en-us.h, extracts all T_ keys, and regenerates base.h (from templates/base.ht) with the updated enum.

  3. Use GET_T(T_YOUR_NEW_KEY) in your code wherever the string should appear.

Translations for other languages are optional — GET_T() will fall back to the English string automatically.

The 0 Placeholder Convention

When defining bp_val_constraint_t structs during development, use 0 for translation key fields that don’t have a real T_ constant yet:

static const bp_val_constraint_t dummy1_speed_range = {
    .type = BP_VAL_UINT32,
    .u = { .min = 1, .max = 1000, .def = 100 },
    .prompt = 0, // T_ key placeholder — add real key later
    .hint = 0,   // T_ key placeholder — add real key later
};

This is safe — since T_ON=0 is a valid enum value, the system handles it gracefully. The same convention applies to choice labels:

static const bp_val_choice_t dummy1_output_choices[] = {
    { "push-pull",  "pp", 0, 0 }, // label=0 → placeholder
    { "open-drain", "od", 0, 1 },
};

Replace 0 with a real T_ key once the translation string is finalized.

Adding a New Language

The full process is documented in src/translation/base.c. Here is a summary:

  1. Add a language name string to en-us.h. The enum name must begin with T_CONFIG_LANGUAGE_:

    [T_CONFIG_LANGUAGE_ESPERANTO] = "Lingvo - esperanto (Mondo)",
  2. Create a .json translation file in src/translation/templates/ using the IETF language tag (e.g., eo-001.json). Start from an existing file as a template. Untranslated entries can be omitted or set to null — they will fall back to English.

  3. Add the language index to base.ht (src/translation/templates/base.ht):

    typedef enum _language_idx_t {
        language_idx_en_us,
        language_idx_pl_pl,
        language_idx_bs_latn_ba,
        language_idx_it_it,
        language_idx_eo_001, // new language
        COUNT_OF_LANGUAGE_IDX,
    } language_idx_t;
  4. Run json2h.py to regenerate base.h and produce the new .h file from the .json:

    cd src/translation && python json2h.py
  5. Register the language in base.c. Include the generated header and add entries to the translation table and get_language_name():

    #include "translation/eo-001.h"
    
    static const translation_table_t* translation_tables[COUNT_OF_LANGUAGE_IDX] = {
        [language_idx_en_us]      = &en_us,
        [language_idx_pl_pl]      = &pl_pl,
        [language_idx_bs_latn_ba] = &bs_ba,
        [language_idx_it_it]      = &it_it,
        [language_idx_eo_001]     = &eo_001, // new
    };
  6. Build and test to verify the new language compiles and displays correctly.

Translation Toolchain

ToolLocationPurpose
json2h.pysrc/translation/json2h.pyParses en-us.h, generates en-us.json, regenerates base.h from base.ht, and converts each .json translation to a .h file
check_translations.pycheck_translations.pyValidates all translation files match the base definitions
base.htsrc/translation/templates/base.htTemplate for base.h generation — contains the language_idx_t enum and the %%%enum_list%%% placeholder

Language Index

The language_idx_t enum in base.h (generated from base.ht) defines the available languages:

typedef enum _language_idx_t {
    language_idx_en_us,
    language_idx_pl_pl,
    language_idx_bs_latn_ba,
    language_idx_it_it,
    COUNT_OF_LANGUAGE_IDX,
} language_idx_t;
Enum ValueLanguage
language_idx_en_usEnglish (US)
language_idx_pl_plPolish
language_idx_bs_latn_baBosnian (Latin)
language_idx_it_itItalian
  • Language selection is stored in system_config.terminal_language
  • translation_set() switches the active language at runtime
  • GET_T() indexes into the correct translation table based on current_language
  • Each translation table is a const char* const array indexed by T_ enum values, stored in ROM

Translation File Structure

Each language has a header file that defines a translation array:

// en-us.h — English source of truth
#include "translation/base.h"
static char const * const en_us[]={
    [T_ON]="ON",
    [T_OFF]="OFF",
    [T_GND]="GND",
    [T_INPUT]="INPUT",
    [T_OUTPUT]="OUTPUT",
    [T_EXIT]="Exit",
    // ...
};

For non-English languages, the generated .h file has the same structure but with translated values. Missing entries are left as NULL and GET_T() falls back to the English string.