How the i18n System in websocket-devtools Enables Multi-Language Support
The i18n system in websocket-devtools implements a hybrid internationalization layer that dynamically switches between custom translation tables and Chrome's native chrome.i18n API based on user preferences and runtime environment.
The websocket-devtools extension provides a flexible internationalization (i18n) system that allows developers to display the UI in multiple bundled languages while intelligently falling back to browser-native localization when appropriate. This architecture ensures optimal performance and compatibility across different Chrome extension contexts, from devtools panels to popup windows.
Architecture of the Hybrid i18n System
The core implementation resides in src/utils/i18n.js, which serves as the central adapter between two translation sources: pre-compiled JSON language packs and Chrome's built-in i18n infrastructure. This dual-mode design allows the extension to operate efficiently whether running as a standard Chrome extension or in environments where the Chrome API is unavailable.
The system maintains translation tables imported from src/locales/index.js, flattening nested JSON structures into dot-notation keys (e.g., monitor.status.active) during initialization. This transformation occurs synchronously on construction at lines 25-29, enabling O(1) key lookups during runtime.
Translation Loading and Key Structure
All supported languages are aggregated in src/locales/index.js, which exports the complete translations object, supportedLanguages array, and display name mappings. Individual language files (such as en-us.json and zh-cn.json) contain hierarchical translation objects that the system flattens into portable dot-notation keys.
This flattening process converts nested structures like {"monitor": {"status": {"active": "Active"}}} into accessible keys such as monitor.status.active. The system stores these in memory for immediate access, avoiding repeated JSON parsing during UI rendering.
Chrome API Integration and Fallback Logic
The i18n system detects Chrome API availability at lines 52-58 by checking for chrome.i18n.getMessage. When available, the system can delegate string lookups to the browser's native implementation for better performance, converting between the extension's dot-notation keys and Chrome's _locales format using src/utils/i18n-converter.js.
Locale mapping occurs at lines 155-164, transforming standard identifiers like en-US into the extension's internal lowercase format (en-us). The priority chain for language selection follows this order:
- Manual user preference stored in
chrome.storage.localunderws_inspector_language - Chrome UI language via
chrome.i18n.getUILanguage()when the API is available - Browser navigator language as a secondary fallback
- Built-in default (
en-us) as the final safety net
This logic is implemented in the initUserPreference method at lines 126-146.
The Unified Translation Helper
The system exposes a t(key, params?) function that serves as the single entry point for all translations. Located at lines 40-45, this helper internally routes requests either to chrome.i18n.getMessage (via the converter utility) or to the custom translation table based on the current operational mode.
For parameterized strings, the helper supports placeholder replacement through the replaceParams utility, allowing dynamic injection of values into translation templates using standard interpolation syntax.
Runtime Language Management
Users can switch languages dynamically without reloading the extension. The setLanguage(lang) method at lines 96-114 forces the system into custom-table mode, persists the selection to chrome.storage.local, and broadcasts change notifications to registered UI listeners.
Additional utility functions provide:
getCurrentLanguage(): Returns the active locale identifiergetSupportedLanguages(): Lists all bundled language codes available at build time- Initialization variants:
initForPanel()for devtools contexts andinitForPopup()for browser popup UIs, each optimized for their respective execution environments
Implementation Examples
Initialize the system for a devtools panel context, respecting saved preferences or falling back to the Chrome UI language:
import { initForPanel } from './utils/i18n.js';
await initForPanel();
For popup interfaces where browser-language detection takes priority:
import { initForPopup } from './utils/i18n.js';
await initForPopup();
Translate UI strings using dot-notation keys:
import { t } from './utils/i18n.js';
document.title = t('app.title');
statusIndicator.textContent = t('monitor.status.active');
Implement a language selector with runtime switching:
import { setLanguage, getSupportedLanguages } from './utils/i18n.js';
const selector = document.getElementById('lang-select');
selector.innerHTML = getSupportedLanguages()
.map(lang => `<option value="${lang}">${lang}</option>`)
.join('');
selector.addEventListener('change', (e) => setLanguage(e.target.value));
Summary
src/utils/i18n.jsserves as the central hub, implementing a hybrid adapter that routes translation requests between custom tables and Chrome's native i18n API.- Dot-notation key flattening at initialization enables efficient lookups while maintaining readable nested JSON source files.
- Automatic detection of
chrome.i18navailability allows the extension to leverage browser-native performance when possible. - Multi-tier fallback logic prioritizes user preferences, then Chrome UI language, then browser settings, ensuring the UI always renders in an available language.
- Runtime language switching via
setLanguage()provides immediate UI updates without requiring extension reloads.
Frequently Asked Questions
How does websocket-devtools handle missing translations?
The system implements a cascading fallback mechanism. If a specific key is missing in the selected language, it defaults to the built-in en-us translation. For Chrome API mode, the browser automatically handles missing keys by returning the message name; in custom mode, the system ensures all keys are pre-loaded from the fallback language during initialization.
Can the extension use Chrome's native _locales directory instead of custom JSON files?
Yes. When the user has not manually selected a language and chrome.i18n is available, the system delegates to src/utils/i18n-converter.js to transform the extension's dot-notation keys into Chrome's expected format. This allows seamless use of the _locales directory structure while maintaining a unified internal API for developers.
What is the difference between initForPanel and initForPopup?
initForPanel() initializes the i18n system for Chrome DevTools panel contexts, prioritizing the ws_inspector_language storage key and Chrome UI language detection. initForPopup() is optimized for browser popup windows, placing higher priority on navigator.language for immediate locale detection suitable for transient UI surfaces.
How are new languages added to the system?
Developers add new languages by creating JSON files in src/locales/ (e.g., de-de.json) with the same nested structure as existing files, then updating src/locales/index.js to export the new translation table. The build process automatically includes these in the supportedLanguages array, making them available to getSupportedLanguages() and the language selector UI.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →