diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 48d57b65263..c401c8dd581 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -227,6 +227,7 @@ Experimental features might be changed or removed without prior notice. | `newLogsPanel` | Enables the new logs panel in Explore | | `pluginsCDNSyncLoader` | Load plugins from CDN synchronously | | `assetSriChecks` | Enables SRI checks for Grafana JavaScript assets | +| `extraLanguages` | Enables additional languages | ## Development feature toggles diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 350a9bf7b5a..edadbb77c33 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -258,4 +258,5 @@ export interface FeatureToggles { alertRuleRestore?: boolean; grafanaManagedRecordingRulesDatasources?: boolean; inviteUserExperimental?: boolean; + extraLanguages?: boolean; } diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index c42f344f4a3..d80d539dc7d 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -1812,6 +1812,13 @@ var ( HideFromDocs: true, FrontendOnly: true, }, + { + Name: "extraLanguages", + Description: "Enables additional languages", + Stage: FeatureStageExperimental, + Owner: grafanaFrontendPlatformSquad, + FrontendOnly: true, + }, } ) diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 8e0710c30ef..3ac8a36313b 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -239,3 +239,4 @@ assetSriChecks,experimental,@grafana/frontend-ops,false,false,true alertRuleRestore,preview,@grafana/alerting-squad,false,false,false grafanaManagedRecordingRulesDatasources,experimental,@grafana/alerting-squad,false,false,false inviteUserExperimental,experimental,@grafana/sharing-squad,false,false,true +extraLanguages,experimental,@grafana/grafana-frontend-platform,false,false,true diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 739f826d187..47f3239cd3d 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -966,4 +966,8 @@ const ( // FlagInviteUserExperimental // Renders invite user button along the app FlagInviteUserExperimental = "inviteUserExperimental" + + // FlagExtraLanguages + // Enables additional languages + FlagExtraLanguages = "extraLanguages" ) diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index d4685e9017b..c42c556a928 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -1713,6 +1713,19 @@ "hideFromAdminPage": true } }, + { + "metadata": { + "name": "extraLanguages", + "resourceVersion": "1741626708204", + "creationTimestamp": "2025-03-10T17:11:48Z" + }, + "spec": { + "description": "Enables additional languages", + "stage": "experimental", + "codeowner": "@grafana/grafana-frontend-platform", + "frontend": true + } + }, { "metadata": { "name": "extraThemes", diff --git a/public/app/core/internationalization/constants.test.ts b/public/app/core/internationalization/constants.test.ts index 68defb081b3..e48cae33433 100644 --- a/public/app/core/internationalization/constants.test.ts +++ b/public/app/core/internationalization/constants.test.ts @@ -1,30 +1,8 @@ import { uniqBy } from 'lodash'; -import { - BRAZILIAN_PORTUGUESE, - CHINESE_SIMPLIFIED, - DEFAULT_LANGUAGE, - ENGLISH_US, - FRENCH_FRANCE, - GERMAN_GERMANY, - LANGUAGES, - PSEUDO_LOCALE, - SPANISH_SPAIN, - VALID_LANGUAGES, -} from './constants'; +import { LANGUAGES, VALID_LANGUAGES } from './constants'; describe('internationalization constants', () => { - it('should have set the constants correctly', () => { - expect(ENGLISH_US).toBe('en-US'); - expect(FRENCH_FRANCE).toBe('fr-FR'); - expect(SPANISH_SPAIN).toBe('es-ES'); - expect(GERMAN_GERMANY).toBe('de-DE'); - expect(BRAZILIAN_PORTUGUESE).toBe('pt-BR'); - expect(CHINESE_SIMPLIFIED).toBe('zh-Hans'); - expect(PSEUDO_LOCALE).toBe('pseudo'); - expect(DEFAULT_LANGUAGE).toBe(ENGLISH_US); - }); - it('should match a canonical locale definition', () => { for (const lang of LANGUAGES) { const resolved = Intl.getCanonicalLocales(lang.code); @@ -32,6 +10,12 @@ describe('internationalization constants', () => { } }); + it('should have locale codes including the country code', () => { + for (const lang of LANGUAGES) { + expect(lang.code).toMatch(/^[a-z]{2}-[a-zA-Z]+$/); + } + }); + it('should not have duplicate languages codes', () => { const uniqLocales = uniqBy(LANGUAGES, (v) => v.code); expect(LANGUAGES).toHaveLength(uniqLocales.length); diff --git a/public/app/core/internationalization/constants.ts b/public/app/core/internationalization/constants.ts index 0c79eb560ef..32088f5d4dc 100644 --- a/public/app/core/internationalization/constants.ts +++ b/public/app/core/internationalization/constants.ts @@ -1,12 +1,28 @@ import { ResourceKey } from 'i18next'; import { uniq } from 'lodash'; +import { config } from '@grafana/runtime'; + export const ENGLISH_US = 'en-US'; export const FRENCH_FRANCE = 'fr-FR'; export const SPANISH_SPAIN = 'es-ES'; export const GERMAN_GERMANY = 'de-DE'; export const BRAZILIAN_PORTUGUESE = 'pt-BR'; export const CHINESE_SIMPLIFIED = 'zh-Hans'; +export const ITALIAN_ITALY = 'it-IT'; +export const JAPANESE_JAPAN = 'ja-JP'; +export const INDONESIAN_INDONESIA = 'id-ID'; +export const KOREAN_KOREA = 'ko-KR'; +export const RUSSIAN_RUSSIA = 'ru-RU'; +export const CZECH_CZECHIA = 'cs-CZ'; +export const DUTCH_NETHERLANDS = 'nl-NL'; +export const HUNGARIAN_HUNGARY = 'hu-HU'; +export const PORTUGUESE_PORTUGAL = 'pt-PT'; +export const POLISH_POLAND = 'pl-PL'; +export const SWEDISH_SWEDEN = 'sv-SE'; +export const TURKISH_TURKEY = 'tr-TR'; +export const CHINESE_TRADITIONAL = 'zh-Hant'; + export const PSEUDO_LOCALE = 'pseudo'; export const DEFAULT_LANGUAGE = ENGLISH_US; @@ -24,6 +40,113 @@ export interface LanguageDefinition { loader: Record; } +// New languages added recently without translations available yet +const NEW_LANGUAGES: LanguageDefinition[] = [ + { + code: CHINESE_TRADITIONAL, + name: '中文(繁體)', + loader: { + grafana: () => import('../../../locales/zh-Hant/grafana.json'), + }, + }, + + { + code: ITALIAN_ITALY, + name: 'Italiano', + loader: { + grafana: () => import('../../../locales/it-IT/grafana.json'), + }, + }, + + { + code: JAPANESE_JAPAN, + name: '日本語', + loader: { + grafana: () => import('../../../locales/ja-JP/grafana.json'), + }, + }, + + { + code: INDONESIAN_INDONESIA, + name: 'Bahasa Indonesia', + loader: { + grafana: () => import('../../../locales/id-ID/grafana.json'), + }, + }, + + { + code: KOREAN_KOREA, + name: '한국어', + loader: { + grafana: () => import('../../../locales/ko-KR/grafana.json'), + }, + }, + + { + code: RUSSIAN_RUSSIA, + name: 'Русский', + loader: { + grafana: () => import('../../../locales/ru-RU/grafana.json'), + }, + }, + + { + code: CZECH_CZECHIA, + name: 'Čeština', + loader: { + grafana: () => import('../../../locales/cs-CZ/grafana.json'), + }, + }, + + { + code: DUTCH_NETHERLANDS, + name: 'Nederlands', + loader: { + grafana: () => import('../../../locales/nl-NL/grafana.json'), + }, + }, + + { + code: HUNGARIAN_HUNGARY, + name: 'Magyar', + loader: { + grafana: () => import('../../../locales/hu-HU/grafana.json'), + }, + }, + + { + code: PORTUGUESE_PORTUGAL, + name: 'Português', + loader: { + grafana: () => import('../../../locales/pt-PT/grafana.json'), + }, + }, + + { + code: POLISH_POLAND, + name: 'Polski', + loader: { + grafana: () => import('../../../locales/pl-PL/grafana.json'), + }, + }, + + { + code: SWEDISH_SWEDEN, + name: 'Svenska', + loader: { + grafana: () => import('../../../locales/sv-SE/grafana.json'), + }, + }, + + { + code: TURKISH_TURKEY, + name: 'Türkçe', + loader: { + grafana: () => import('../../../locales/tr-TR/grafana.json'), + }, + }, +]; + export const LANGUAGES: LanguageDefinition[] = [ { code: ENGLISH_US, @@ -74,6 +197,10 @@ export const LANGUAGES: LanguageDefinition[] = [ }, ] satisfies Array>; +if (config.featureToggles?.extraLanguages) { + LANGUAGES.push(...NEW_LANGUAGES); +} + if (process.env.NODE_ENV === 'development') { LANGUAGES.push({ code: PSEUDO_LOCALE,