mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
I18n: Add server config to detect browser language (#69396)
* I18N: Add browser language detector * Improve style * No new property for type check * Add dependency * Suggested doc change Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> * Fix prettier * 'detect' as special language, no cache and only navigator detector As per PR suggestion comments * Update language configuration doc * Suggested change in doc From @chri2547 Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> * Fix import * Revert public/app/types/explore.ts changes * Prettier write --------- Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> Co-authored-by: joshhunt <josh@trtr.co>
This commit is contained in:
parent
e7e5ae2ee0
commit
5654359813
@ -827,7 +827,9 @@ Sets the default UI theme: `dark`, `light`, or `system`. The default theme is `d
|
||||
|
||||
### default_language
|
||||
|
||||
This setting configures the default UI language, which must be a supported IETF language tag, such as `en-US`.
|
||||
This option will set the default UI language if a supported IETF language tag like `en-US` is available.
|
||||
If set to `detect`, the default UI language will be determined by browser preference.
|
||||
The default is `en-US`.
|
||||
|
||||
### home_page
|
||||
|
||||
|
@ -337,6 +337,7 @@
|
||||
"history": "4.10.1",
|
||||
"hoist-non-react-statics": "3.3.2",
|
||||
"i18next": "^22.0.0",
|
||||
"i18next-browser-languagedetector": "^7.0.2",
|
||||
"immer": "10.0.2",
|
||||
"immutable": "4.3.0",
|
||||
"jquery": "3.7.0",
|
||||
|
@ -71,6 +71,7 @@
|
||||
"date-fns": "2.30.0",
|
||||
"hoist-non-react-statics": "3.3.2",
|
||||
"i18next": "^22.0.0",
|
||||
"i18next-browser-languagedetector": "^7.0.2",
|
||||
"immutable": "4.3.0",
|
||||
"is-hotkey": "0.2.0",
|
||||
"jquery": "3.7.0",
|
||||
|
@ -14,7 +14,8 @@ import { Trans as I18NextTrans, initReactI18next } from 'react-i18next'; // esli
|
||||
// Creates a default, english i18next instance when running outside of grafana.
|
||||
// we don't support changing the locale of grafana ui when outside of Grafana
|
||||
function initI18n() {
|
||||
if (!i18next.options.lng) {
|
||||
// resources is undefined by default and set either by grafana app.ts or here
|
||||
if (typeof i18next.options.resources !== 'object') {
|
||||
i18next.use(initReactI18next).init({
|
||||
resources: {},
|
||||
returnEmptyString: false,
|
||||
|
@ -1,50 +1,59 @@
|
||||
import i18n, { BackendModule } from 'i18next';
|
||||
import i18n, { BackendModule, InitOptions } from 'i18next';
|
||||
import LanguageDetector, { DetectorOptions } from 'i18next-browser-languagedetector';
|
||||
import React from 'react';
|
||||
import { Trans as I18NextTrans, initReactI18next } from 'react-i18next'; // eslint-disable-line no-restricted-imports
|
||||
|
||||
import { DEFAULT_LANGUAGE, LANGUAGES, VALID_LANGUAGES } from './constants';
|
||||
import { LANGUAGES, VALID_LANGUAGES } from './constants';
|
||||
|
||||
const getLanguagePartFromCode = (code: string) => code.split('-')[0].toLowerCase();
|
||||
|
||||
const loadTranslations: BackendModule = {
|
||||
type: 'backend',
|
||||
init() {},
|
||||
async read(language, namespace, callback) {
|
||||
const localeDef = LANGUAGES.find((v) => v.code === language);
|
||||
|
||||
let localeDef = LANGUAGES.find((v) => v.code === language);
|
||||
if (!localeDef) {
|
||||
localeDef = LANGUAGES.find((v) => getLanguagePartFromCode(v.code) === getLanguagePartFromCode(language));
|
||||
}
|
||||
if (!localeDef) {
|
||||
return callback(new Error('No message loader available for ' + language), null);
|
||||
}
|
||||
|
||||
const messages = await localeDef.loader();
|
||||
callback(null, messages);
|
||||
},
|
||||
};
|
||||
|
||||
export function initializeI18n(language: string) {
|
||||
const validLocale = VALID_LANGUAGES.includes(language) ? language : DEFAULT_LANGUAGE;
|
||||
|
||||
// This is a placeholder so we can put a 'comment' in the message json files.
|
||||
// Starts with an underscore so it's sorted to the top of the file. Even though it is in a comment the following line is still extracted
|
||||
// t('_comment', 'This file is the source of truth for English strings. Edit this to change plurals and other phrases for the UI.');
|
||||
|
||||
return i18n
|
||||
const options: InitOptions = {
|
||||
// We don't bundle any translations, we load them async
|
||||
partialBundledLanguages: true,
|
||||
resources: {},
|
||||
|
||||
// If translations are empty strings (no translation), fall back to the default value in source code
|
||||
returnEmptyString: false,
|
||||
|
||||
pluralSeparator: '__',
|
||||
};
|
||||
let init = i18n;
|
||||
if (language === 'detect') {
|
||||
init = init.use(LanguageDetector);
|
||||
const detection: DetectorOptions = { order: ['navigator'], caches: [] };
|
||||
options.detection = detection;
|
||||
} else {
|
||||
options.lng = VALID_LANGUAGES.includes(language) ? language : undefined;
|
||||
}
|
||||
return init
|
||||
.use(loadTranslations)
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
.init({
|
||||
lng: validLocale,
|
||||
|
||||
// We don't bundle any translations, we load them async
|
||||
partialBundledLanguages: true,
|
||||
resources: {},
|
||||
|
||||
// If translations are empty strings (no translation), fall back to the default value in source code
|
||||
returnEmptyString: false,
|
||||
|
||||
pluralSeparator: '__',
|
||||
});
|
||||
.init(options);
|
||||
}
|
||||
|
||||
export function changeLanguage(locale: string) {
|
||||
const validLocale = VALID_LANGUAGES.includes(locale) ? locale : DEFAULT_LANGUAGE;
|
||||
const validLocale = VALID_LANGUAGES.includes(locale) ? locale : undefined;
|
||||
return i18n.changeLanguage(validLocale);
|
||||
}
|
||||
|
||||
@ -63,8 +72,6 @@ export const i18nDate = (value: number | Date | string, format: Intl.DateTimeFor
|
||||
if (typeof value === 'string') {
|
||||
return i18nDate(new Date(value), format);
|
||||
}
|
||||
const locale = i18n.options.lng ?? DEFAULT_LANGUAGE;
|
||||
|
||||
const dateFormatter = new Intl.DateTimeFormat(locale, format);
|
||||
const dateFormatter = new Intl.DateTimeFormat(i18n.language, format);
|
||||
return dateFormatter.format(value);
|
||||
};
|
||||
|
13
yarn.lock
13
yarn.lock
@ -2219,7 +2219,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:7.22.3, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
|
||||
"@babel/runtime@npm:7.22.3, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.17.2, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
|
||||
version: 7.22.3
|
||||
resolution: "@babel/runtime@npm:7.22.3"
|
||||
dependencies:
|
||||
@ -4184,6 +4184,7 @@ __metadata:
|
||||
expose-loader: 4.1.0
|
||||
hoist-non-react-statics: 3.3.2
|
||||
i18next: ^22.0.0
|
||||
i18next-browser-languagedetector: ^7.0.2
|
||||
immutable: 4.3.0
|
||||
is-hotkey: 0.2.0
|
||||
jquery: 3.7.0
|
||||
@ -19410,6 +19411,7 @@ __metadata:
|
||||
http-server: 14.1.1
|
||||
husky: 8.0.3
|
||||
i18next: ^22.0.0
|
||||
i18next-browser-languagedetector: ^7.0.2
|
||||
i18next-parser: 6.6.0
|
||||
immer: 10.0.2
|
||||
immutable: 4.3.0
|
||||
@ -20272,6 +20274,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"i18next-browser-languagedetector@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "i18next-browser-languagedetector@npm:7.0.2"
|
||||
dependencies:
|
||||
"@babel/runtime": ^7.19.4
|
||||
checksum: ea6c5254308965ff9fe9870c16b546b67d065cc00bd3af961f1414ba73fbe323a7d1b76fe7524b1581307f7849fa6d04c36336693818a23d3690ebefc60d9071
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"i18next-parser@npm:6.6.0":
|
||||
version: 6.6.0
|
||||
resolution: "i18next-parser@npm:6.6.0"
|
||||
|
Loading…
Reference in New Issue
Block a user