I18n: Add new language options (#101899)
* Add newLanguages feature toggle * add additional languages behind feature toggle * be more forgiving of what config looks like in tests * tweak regex * put pt-br back * restore order of pt-BR and cn-Hans, rename EXTRA_LANGUAGES to NEW_LANGUAGES * stricter test regex
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -258,4 +258,5 @@ export interface FeatureToggles {
|
||||
alertRuleRestore?: boolean;
|
||||
grafanaManagedRecordingRulesDatasources?: boolean;
|
||||
inviteUserExperimental?: boolean;
|
||||
extraLanguages?: boolean;
|
||||
}
|
||||
|
||||
@@ -1812,6 +1812,13 @@ var (
|
||||
HideFromDocs: true,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
{
|
||||
Name: "extraLanguages",
|
||||
Description: "Enables additional languages",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaFrontendPlatformSquad,
|
||||
FrontendOnly: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -966,4 +966,8 @@ const (
|
||||
// FlagInviteUserExperimental
|
||||
// Renders invite user button along the app
|
||||
FlagInviteUserExperimental = "inviteUserExperimental"
|
||||
|
||||
// FlagExtraLanguages
|
||||
// Enables additional languages
|
||||
FlagExtraLanguages = "extraLanguages"
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Namespace extends string = string> {
|
||||
loader: Record<Namespace, LocaleFileLoader>;
|
||||
}
|
||||
|
||||
// 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<LanguageDefinition<'grafana'>>;
|
||||
|
||||
if (config.featureToggles?.extraLanguages) {
|
||||
LANGUAGES.push(...NEW_LANGUAGES);
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
LANGUAGES.push({
|
||||
code: PSEUDO_LOCALE,
|
||||
|
||||
Reference in New Issue
Block a user