Architecture and Implementation of Internationalization within the Grafana Ecosystem

The globalization of observability platforms necessitates a robust framework for Internationalization (i18n) and Localization (l10n), ensuring that critical telemetry data and interface elements are accessible to a diverse, multilingual workforce. In the context of Grafana, i18n is not merely a cosmetic feature but a complex architectural requirement involving the management of runtime dependencies, plugin-specific translation lifecycles, and the integration of centralized translation providers. As Grafana evolves, specifically transitioning through versions like 9.3, 10.2.x, and 11.0.0, the underlying mechanisms for delivering localized content have shifted from decentralized, plugin-bundled dependencies to a centralized, runtime-provided model. This architectural pivot is designed to mitigate version conflicts and reduce the memory footprint of the browser environment, but it introduces specific challenges for plugin developers maintaining backward compatibility with legacy Grafiona environments. Understanding the nuances of this system requires an exploration of the core Grafana i18n runtime, the specific implementation patterns for plugin developers using @grafana/create-plugin, and the advanced methodologies for dynamic language injection via external APIs such as the EF Agent Desk.

The Evolution of i18next Dependency Management in Grafana Plugins

A significant architectural shift occurred within the Grafana plugin tooling to streamline the delivery of localized strings. Historically, many plugins operated by bundling their own instance of the i18next NPM package. This approach, while providing isolation, led to a fragmented ecosystem where multiple, potentially conflicting versions of i18next were loaded into the same browser memory space, causing instability and bloating the application bundle.

To address this, starting from version 5.26.1 of the @grafana/create-plugin utility, Grafana officially marked the i18next package as an external dependency. This means that the i18next runtime is now provided by the Grafana core itself.

The implications of this change are profound for the plugin lifecycle:

  • Centralization of Runtime: By marking i18next as external, Grafana ensures that a single, consistent version of the translation engine is used across all active plugins. This prevents the "multiple bundle" problem where different plugins might attempt to initialize different translation instances.
  • Version Compatibility Constraints: This change introduces a breaking dependency for plugins that require i18next to be bundled at build time and those that declare compatibility with Grafana versions older than 10.2.x. If a plugin expects a bundled version but runs on a modern Grafana instance that provides its own external version, conflicts may arise.
  • Workaround Strategies: For developers maintaining legacy compatibility, the recommended technical solution is to manually customize the Webpack configuration to force the bundling of i18next within the plugin. This ensures that the plugin carries its own translation engine when running on older Grafana servers (pre-v10.2.x).
  • Implementation of @grafana/i18n: The modern approach shifts the focus from managing the i18next engine to utilizing the @grafana/i18n helper library. This library provides the necessary APIs to interface with the core Grafana translation runtime without the overhead of managing the underlying engine.
Feature Legacy Plugin Approach Modern Grafana Plugin Approach
Dependency Type Bundled (Internal) External (Provided by Grafana)
Package Requirement i18next included in bundle @grafana/i18n helpers used
Version Conflict Risk High (Multiple instances) Low (Single shared instance)
Compatible Grafana Versions Any (if bundled) 10.2.x and above (preferred)
- Dependency Tooling @grafana/create-plugin < 5.26.1 @grafana/create-plugin >= 5.26.1

Core Internationalization Capabilities in Grafana Interface

The native internationalization support within the Grafana core provides a standardized experience for users across various interface layers. It is crucial to distinguish between what is natively supported by the Grafana runtime and what remains static within the dashboard definitions.

Official built-in language support became a standard feature in Grafana version 9.3 and subsequent releases. This support covers several critical UI components, ensuring that the administrative and navigational elements of the platform are localized.

The scope of natively translated areas includes:

  • Navigation and Sidebar: This encompasses the primary movement controls within the application, including menu items such as Dash/Dashboards, Explore, Alerts, and Configuration.
  • System UI Elements: Tooltips that provide context for user actions and the standard system buttons used throughout the interface.
  • Dashboard Interface: Common actions including Save, Share, Edit, and Settings. This also extends to panel menus, header actions, the time picker, and auto-refresh controls.
  • User Preferences and Profile: The language selector within the user profile, as well as localized settings for date/time formats and theme preferences.
  • Settings and Administration: The settings pages for Organization and User management, plugin installation/configuration screens, and team or role management interfaces.
  • Alerts and Notification Channels: UI labels, status indicators, and the configuration actions associated with the alerting engine.
  • Authentication UI: The critical security-facing surfaces, including login screens, registration forms, password reset interfaces, field labels, and error messages.

However, developers and administrators must be aware of the functional boundaries of this native support. Grafana's built-in i18n does not automatically translate content that is part of the dashboard data or configuration, such as:

  • Panel titles and descriptions.
  • Unit labels and data labels.
  • Dashboard-specific text elements.

For deployments on older versions, such as v9.1.6, these built-in i18n features are not available, necessitating alternative strategies like manual hardcoding or browser-based translation.

Technical Implementation for Plugin Developers

Developing a localized plugin requires a specific sequence of configuration steps, ranging from dependency declaration in package.json to the initialization of translation modules in the plugin's entry point.

Dependency and Language Configuration

The foundation of a localized plugin begins in the package.json file. Developers must specify the required version of the Grafana dependency and define which languages the plugin intends to support.

  • Dependency Requirements: The @grafana/i18n helper library is operational from version 11.0.0 and higher. Therefore, the grafanaDependency must be set to a compatible version, such as ">=12.1.0".
  • Language Declaration: The languages array must explicitly list the supported locales, for example, ["en-US", "es-ES"].

Plugin Initialization Patterns

The method for initializing translations depends heavily on the underlying architecture of the plugin, specifically whether it utilizes the @grafana/scenes library.

For plugins that do not utilize @grafana/scenes, the initialization in module.ts is straightforward:

```typescript
import { initPluginTranslations } from '@grafana/i18n';
import pluginJson from 'plugin.json';

await initPluginTranslations(pluginJson.id);
```

For more complex plugins built with @grafana/scenes, the initialization must account for the resource loading mechanism:

```typescript
import { initPluginTranslations }rypt from '@grafana/i18n';
import pluginJson from 'plugin.json';
import { loadResources } from '@grafana/scenes';

await initPluginTranslations(pluginJson.id, [loadResources]);
```

Tooling and Linting for Translation Integrity

To prevent the introduction of untranslated strings into the production codebase, developers should implement strict ESLint rules. The @grafana/i18n ESLint plugin can be integrated into the eslint.config.mjs to automate the detection of missing keys.

The configuration should include rules to catch untranslated strings and prevent top-level translation usage:

```javascript
import grafanaI18nPlugin from '@grafana/i18n/eslint-plugin';

export default defineConfig([
{
name: 'grafana/i18n-rules',
plugins: { '@grafana/i18n': grafanaI18nPlugin },
rules: {
'@grafana/i18n/no-untranslated-strings': ['error', { calleesToIgnore: ['^css$', 'use[A-Z].*'] }],
'@grafana/i18n/no-translation-top-level': 'error',
},
},
]);
```

In this configuration, the no-untranslated-strings rule is set to an error level, while ignoring specific CSS or React hook patterns (e.g., use[A-Z].*) to avoid false positives in complex UI logic.

Advanced Dynamic Translation via External APIs

In specific enterprise environments, such as those using the EF Agent Desk, it is possible to achieve dynamic rendering of dashboard elements (titles, descriptions, etc.) based on a user's selected language. This is achieved through a specialized Translation API that goes beyond the capabilities of the standard Grafiana i18n runtime.

This method involves fetching a JSON payload from a specific endpoint, often following a predictable URL structure based on the language format prefix. For example, for Arabic (ar), the system might target:

<FQDN>/assets/i18n/ar.json

The response from such an API provides a structured mapping of keys to their translated values. An example response for Arabic might look like this:

json { "queue_summary_stats_title": "نشط مع الوكلاء", "queue_summary_stats_description": "عنوان ملخص إحصائيات الطابور", "bot_stats_title": "عنوان إحصائيات الروبوت", "bot_stats_summary_description": "وصف ملخص إحصائيات الروبوت" }

To implement this, the workflow involves:

  1. Extracting the specific required key (e.g., queue_summary_stats_panel_title) from the HTTP response.
  2. Storing this value in a dynamically defined variable.
  3. Utilizing Grafana variables to replace the static text in the panel with the dynamic, translated value.

While powerful, this approach has distinct limitations that developers must account for:

  • Static Elements: Dashboard folder names and file names cannot be translated via this method.
  • Variable Labels: The labels for dropdown variables are static text in the UI. While the value of the variable can be dynamically translated, the label itself remains unchanged.
  • Error Handling: Error messages originating from the API itself are not automatically caught by this translation layer.

As an alternative for these unresolvable static elements, developers may rely on browser-based translation extensions, which can translate the entire DOM of the Grafana interface on-the-fly.

Challenges in Automated Key Generation

A common friction point in the development workflow is the use of automated scanners like i18next-scanner. While these tools are designed to simplify the process by automatically generating required keys in translation JSON files, they are prone to configuration failures.

Developers have reported scenarios where running the configuration command, such as npm run node i18n.config.js, fails to populate the translation JSON files with new keys. This typically stems from a misalignment between the scanner's configuration and the plugin's source code structure, or the inability of the scanner to correctly traverse the transformed code during the build process. When this occurs, manual key entry and careful auditing of the i18n.config.js file are required to ensure that the translation files remain synchronized with the actual strings used in the application logic.

Comprehensive Analysis of Localization Strategies

The implementation of internationalization in Grafana is a multi-tiered architecture that requires different strategies depending on the layer of the application being addressed. For the core platform, the strategy is centralized and runtime-based, focusing on reducing complexity and ensuring a unified user experience across all native UI elements. For plugin developers, the strategy is centered on compliance with the @grafana/i18n standard, requiring strict adherence to new dependency models and the adoption of modern initialization patterns.

The transition toward an externalized i18next dependency represents a maturation of the Grafana ecosystem, moving away from the "siloed plugin" model toward a "shared platform" model. While this introduces breaking changes for legacy plugins, the long-term benefits of reduced bundle sizes and version stability are paramount for the scalability of the ecosystem. However, the complexity of managing translation keys—especially when utilizing automated scanners or attempting to bridge the gap between static dashboard elements and dynamic API-driven translations—remains a significant technical hurdle.

Ultimately, a successful i18n implementation in Grafana requires a holistic approach: utilizing the native runtime for core UI, adhering to the @grafana/i18n standards for plugin development, implementing strict linting to maintain data integrity, and leveraging advanced API-driven techniques for high-fidelity, dynamic dashboard localization.

Sources

  1. Grafana Community - Marking i18next as an external package
  2. ExpertFlow - Technical solution to set Grafana language dynamic
  3. Grafana Developers - Plugin Internationalization
  4. Grafana Community - i18next-scanner in Grafana framework

Related Posts