mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[i18n] .i18nrc file as the source of truth and enhance tooling (#39774)
* scan node_modules/@kbn directory for i18n * remove es-lint-disable-next line * update paths * look inside .i18nrc.json instead of traversing files * split .i18nrc file and enhance i18n tooling * checkout master kibana config * include x-pack/.i18nrc.json file * remove unused import * fix i18n_integrate * fix i18n_integrate * code review changes * code review changes * try using scanDir * revert to using concat * add config type * code review fixes * finalize PR * revert kibana.yml
This commit is contained in:
parent
b22a28844a
commit
81153c2a22
20 changed files with 316 additions and 144 deletions
41
.i18nrc.json
41
.i18nrc.json
|
@ -23,45 +23,8 @@
|
|||
"timelion": "src/legacy/core_plugins/timelion",
|
||||
"tagCloud": "src/legacy/core_plugins/tagcloud",
|
||||
"tsvb": "src/legacy/core_plugins/metrics",
|
||||
"kbnESQuery": "packages/kbn-es-query",
|
||||
"xpack.actions": "x-pack/legacy/plugins/actions",
|
||||
"xpack.alerting": "x-pack/legacy/plugins/alerting",
|
||||
"xpack.apm": "x-pack/legacy/plugins/apm",
|
||||
"xpack.beatsManagement": "x-pack/legacy/plugins/beats_management",
|
||||
"xpack.canvas": "x-pack/legacy/plugins/canvas",
|
||||
"xpack.code": "x-pack/legacy/plugins/code",
|
||||
"xpack.crossClusterReplication": "x-pack/legacy/plugins/cross_cluster_replication",
|
||||
"xpack.dashboardMode": "x-pack/legacy/plugins/dashboard_mode",
|
||||
"xpack.fileUpload": "x-pack/legacy/plugins/file_upload",
|
||||
"xpack.graph": "x-pack/legacy/plugins/graph",
|
||||
"xpack.grokDebugger": "x-pack/legacy/plugins/grokdebugger",
|
||||
"xpack.idxMgmt": "x-pack/legacy/plugins/index_management",
|
||||
"xpack.indexLifecycleMgmt": "x-pack/legacy/plugins/index_lifecycle_management",
|
||||
"xpack.infra": "x-pack/legacy/plugins/infra",
|
||||
"xpack.kueryAutocomplete": "x-pack/legacy/plugins/kuery_autocomplete",
|
||||
"xpack.licenseMgmt": "x-pack/legacy/plugins/license_management",
|
||||
"xpack.maps": "x-pack/legacy/plugins/maps",
|
||||
"xpack.ml": "x-pack/legacy/plugins/ml",
|
||||
"xpack.logstash": "x-pack/legacy/plugins/logstash",
|
||||
"xpack.main": "x-pack/legacy/plugins/xpack_main",
|
||||
"xpack.telemetry": "x-pack/legacy/plugins/telemetry",
|
||||
"xpack.monitoring": "x-pack/legacy/plugins/monitoring",
|
||||
"xpack.remoteClusters": "x-pack/legacy/plugins/remote_clusters",
|
||||
"xpack.reporting": "x-pack/legacy/plugins/reporting",
|
||||
"xpack.rollupJobs": "x-pack/legacy/plugins/rollup",
|
||||
"xpack.searchProfiler": "x-pack/legacy/plugins/searchprofiler",
|
||||
"xpack.siem": "x-pack/legacy/plugins/siem",
|
||||
"xpack.security": "x-pack/legacy/plugins/security",
|
||||
"xpack.server": "x-pack/legacy/server",
|
||||
"xpack.snapshotRestore": "x-pack/legacy/plugins/snapshot_restore",
|
||||
"xpack.spaces": "x-pack/legacy/plugins/spaces",
|
||||
"xpack.upgradeAssistant": "x-pack/legacy/plugins/upgrade_assistant",
|
||||
"xpack.uptime": "x-pack/legacy/plugins/uptime",
|
||||
"xpack.watcher": "x-pack/legacy/plugins/watcher"
|
||||
"kbnESQuery": "packages/kbn-es-query"
|
||||
},
|
||||
"exclude": ["src/legacy/ui/ui_render/ui_render_mixin.js"],
|
||||
"translations": [
|
||||
"x-pack/plugins/translations/translations/zh-CN.json",
|
||||
"x-pack/plugins/translations/translations/ja-JP.json"
|
||||
]
|
||||
"translations": []
|
||||
}
|
||||
|
|
|
@ -111,5 +111,5 @@
|
|||
#ops.interval: 5000
|
||||
|
||||
# Specifies locale to be used for all localizable strings, dates and number formats.
|
||||
# Supported languages are the following: English - en , by default , Chinese - zh-CN .
|
||||
# Supported languages are the following: English - en , by default , Chinese - zh-CN .
|
||||
#i18n.locale: "en"
|
||||
|
|
|
@ -47,6 +47,7 @@ export const CopySourceTask = {
|
|||
'webpackShims/**',
|
||||
'config/kibana.yml',
|
||||
'tsconfig*.json',
|
||||
'.i18nrc.json',
|
||||
'kibana.d.ts'
|
||||
],
|
||||
});
|
||||
|
|
|
@ -21,45 +21,47 @@ import { resolve } from 'path';
|
|||
|
||||
// @ts-ignore
|
||||
import { normalizePath, readFileAsync } from '.';
|
||||
// @ts-ignore
|
||||
import rootConfig from '../../../.i18nrc.json';
|
||||
|
||||
export interface I18nConfig {
|
||||
paths: Record<string, string>;
|
||||
exclude: string[];
|
||||
translations: string[];
|
||||
prefix?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges root .i18nrc.json config with any other additional configs (e.g. from
|
||||
* third-party plugins).
|
||||
* @param configPaths List of config paths.
|
||||
*/
|
||||
export async function mergeConfigs(configPaths: string | string[] = []) {
|
||||
const mergedConfig: I18nConfig = { exclude: [], translations: [], ...rootConfig };
|
||||
|
||||
for (const configPath of Array.isArray(configPaths) ? configPaths : [configPaths]) {
|
||||
const additionalConfig: I18nConfig = {
|
||||
paths: {},
|
||||
exclude: [],
|
||||
translations: [],
|
||||
...JSON.parse(await readFileAsync(resolve(configPath))),
|
||||
};
|
||||
|
||||
for (const [namespace, path] of Object.entries(additionalConfig.paths)) {
|
||||
mergedConfig.paths[namespace] = normalizePath(resolve(configPath, '..', path));
|
||||
}
|
||||
|
||||
for (const exclude of additionalConfig.exclude) {
|
||||
mergedConfig.exclude.push(normalizePath(resolve(configPath, '..', exclude)));
|
||||
}
|
||||
|
||||
for (const translations of additionalConfig.translations) {
|
||||
mergedConfig.translations.push(normalizePath(resolve(configPath, '..', translations)));
|
||||
export async function checkConfigNamespacePrefix(configPath: string) {
|
||||
const { prefix, paths } = JSON.parse(await readFileAsync(resolve(configPath)));
|
||||
for (const [namespace] of Object.entries(paths)) {
|
||||
if (prefix && prefix !== namespace.split('.')[0]) {
|
||||
throw new Error(`namespace ${namespace} must be prefixed with ${prefix} in ${configPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mergedConfig;
|
||||
export async function assignConfigFromPath(
|
||||
config: I18nConfig = { exclude: [], translations: [], paths: {} },
|
||||
configPath: string
|
||||
) {
|
||||
const additionalConfig: I18nConfig = {
|
||||
paths: {},
|
||||
exclude: [],
|
||||
translations: [],
|
||||
...JSON.parse(await readFileAsync(resolve(configPath))),
|
||||
};
|
||||
|
||||
for (const [namespace, path] of Object.entries(additionalConfig.paths)) {
|
||||
config.paths[namespace] = normalizePath(resolve(configPath, '..', path));
|
||||
}
|
||||
|
||||
for (const exclude of additionalConfig.exclude) {
|
||||
config.exclude.push(normalizePath(resolve(configPath, '..', exclude)));
|
||||
}
|
||||
|
||||
for (const translations of additionalConfig.translations) {
|
||||
config.translations.push(normalizePath(resolve(configPath, '..', translations)));
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,3 +20,4 @@
|
|||
export const DEFAULT_MESSAGE_KEY = 'defaultMessage';
|
||||
export const DESCRIPTION_KEY = 'description';
|
||||
export const VALUES_KEY = 'values';
|
||||
export const I18N_RC = '.i18nrc.json';
|
||||
|
|
|
@ -22,7 +22,12 @@ export { extractMessagesFromPathToMap } from './extract_default_translations';
|
|||
// @ts-ignore
|
||||
export { matchEntriesWithExctractors } from './extract_default_translations';
|
||||
// @ts-ignore
|
||||
export { writeFileAsync, readFileAsync, normalizePath, ErrorReporter } from './utils';
|
||||
export { arrayify, writeFileAsync, readFileAsync, normalizePath, ErrorReporter } from './utils';
|
||||
export { serializeToJson, serializeToJson5 } from './serializers';
|
||||
export { I18nConfig, filterConfigPaths, mergeConfigs } from './config';
|
||||
export {
|
||||
I18nConfig,
|
||||
filterConfigPaths,
|
||||
assignConfigFromPath,
|
||||
checkConfigNamespacePrefix,
|
||||
} from './config';
|
||||
export { integrateLocaleFiles } from './integrate_locale_files';
|
||||
|
|
44
src/dev/i18n/tasks/check_configs.ts
Normal file
44
src/dev/i18n/tasks/check_configs.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { resolve, join } from 'path';
|
||||
import { I18N_RC } from '../constants';
|
||||
import { ErrorReporter, checkConfigNamespacePrefix, arrayify } from '..';
|
||||
|
||||
export function checkConfigs(additionalConfigPaths: string | string[] = []) {
|
||||
const root = join(__dirname, '../../../../');
|
||||
const kibanaRC = resolve(root, I18N_RC);
|
||||
const xpackRC = resolve(root, 'x-pack', I18N_RC);
|
||||
|
||||
const configPaths = [kibanaRC, xpackRC, ...arrayify(additionalConfigPaths)];
|
||||
|
||||
return configPaths.map(configPath => ({
|
||||
task: async (context: { reporter: ErrorReporter }) => {
|
||||
try {
|
||||
await checkConfigNamespacePrefix(configPath);
|
||||
} catch (err) {
|
||||
const { reporter } = context;
|
||||
const reporterWithContext = reporter.withContext({ name: configPath });
|
||||
reporterWithContext.report(err);
|
||||
throw reporter;
|
||||
}
|
||||
},
|
||||
title: `Checking configs in ${configPath}`,
|
||||
}));
|
||||
}
|
|
@ -21,14 +21,7 @@ import chalk from 'chalk';
|
|||
import { createFailError } from '../../run';
|
||||
import { ErrorReporter, extractMessagesFromPathToMap, filterConfigPaths, I18nConfig } from '..';
|
||||
|
||||
export function extractDefaultMessages({
|
||||
path,
|
||||
config,
|
||||
}: {
|
||||
path?: string | string[];
|
||||
config: I18nConfig;
|
||||
}) {
|
||||
const inputPaths = Array.isArray(path) ? path : [path || './'];
|
||||
export function extractDefaultMessages(config: I18nConfig, inputPaths: string[]) {
|
||||
const filteredPaths = filterConfigPaths(inputPaths, config) as string[];
|
||||
if (filteredPaths.length === 0) {
|
||||
throw createFailError(
|
||||
|
@ -37,7 +30,6 @@ export function extractDefaultMessages({
|
|||
)} None of input paths is covered by the mappings in .i18nrc.json.`
|
||||
);
|
||||
}
|
||||
|
||||
return filteredPaths.map(filteredPath => ({
|
||||
task: async (context: {
|
||||
messages: Map<string, { message: string }>;
|
||||
|
|
|
@ -92,13 +92,13 @@ export async function extractUntrackedMessagesTask({
|
|||
}
|
||||
}
|
||||
|
||||
export function extractUntrackedMessages(srcPaths: string[], config: I18nConfig) {
|
||||
return srcPaths.map(srcPath => ({
|
||||
title: `Checking untracked messages in ${srcPath}`,
|
||||
task: async (context: { reporter: ErrorReporter }) => {
|
||||
const { reporter } = context;
|
||||
export function extractUntrackedMessages(inputPaths: string[]) {
|
||||
return inputPaths.map(inputPath => ({
|
||||
title: `Checking untracked messages in ${inputPath}`,
|
||||
task: async (context: { reporter: ErrorReporter; config: I18nConfig }) => {
|
||||
const { reporter, config } = context;
|
||||
const initialErrorsNumber = reporter.errors.length;
|
||||
const result = await extractUntrackedMessagesTask({ path: srcPath, config, reporter });
|
||||
const result = await extractUntrackedMessagesTask({ path: inputPath, config, reporter });
|
||||
if (reporter.errors.length === initialErrorsNumber) {
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -20,3 +20,5 @@
|
|||
export { extractDefaultMessages } from './extract_default_translations';
|
||||
export { extractUntrackedMessages } from './extract_untracked_translations';
|
||||
export { checkCompatibility } from './check_compatibility';
|
||||
export { mergeConfigs } from './merge_configs';
|
||||
export { checkConfigs } from './check_configs';
|
||||
|
|
43
src/dev/i18n/tasks/merge_configs.ts
Normal file
43
src/dev/i18n/tasks/merge_configs.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { resolve, join } from 'path';
|
||||
import { ErrorReporter, I18nConfig, assignConfigFromPath, arrayify } from '..';
|
||||
|
||||
export function mergeConfigs(additionalConfigPaths: string | string[] = []) {
|
||||
const root = join(__dirname, '../../../../');
|
||||
const kibanaRC = resolve(root, '.i18nrc.json');
|
||||
const xpackRC = resolve(root, 'x-pack/.i18nrc.json');
|
||||
|
||||
const configPaths = [kibanaRC, xpackRC, ...arrayify(additionalConfigPaths)];
|
||||
|
||||
return configPaths.map(configPath => ({
|
||||
task: async (context: { reporter: ErrorReporter; config?: I18nConfig }) => {
|
||||
try {
|
||||
context.config = await assignConfigFromPath(context.config, configPath);
|
||||
} catch (err) {
|
||||
const { reporter } = context;
|
||||
const reporterWithContext = reporter.withContext({ name: configPath });
|
||||
reporterWithContext.report(err);
|
||||
throw reporter;
|
||||
}
|
||||
},
|
||||
title: `Merging configs in ${configPath}`,
|
||||
}));
|
||||
}
|
|
@ -324,3 +324,8 @@ export class ErrorReporter {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// export function arrayify<Subj = any>(subj: Subj | Subj[]): Subj[] {
|
||||
export function arrayify(subj) {
|
||||
return Array.isArray(subj) ? subj : [subj];
|
||||
}
|
||||
|
|
|
@ -20,10 +20,18 @@
|
|||
import chalk from 'chalk';
|
||||
import Listr from 'listr';
|
||||
|
||||
import { ErrorReporter, mergeConfigs } from './i18n';
|
||||
import { extractDefaultMessages, extractUntrackedMessages, checkCompatibility } from './i18n/tasks';
|
||||
import { ErrorReporter, I18nConfig } from './i18n';
|
||||
import {
|
||||
extractDefaultMessages,
|
||||
extractUntrackedMessages,
|
||||
checkCompatibility,
|
||||
checkConfigs,
|
||||
mergeConfigs,
|
||||
} from './i18n/tasks';
|
||||
import { createFailError, run } from './run';
|
||||
|
||||
const skipNoTranslations = ({ config }: { config: I18nConfig }) => !config.translations.length;
|
||||
|
||||
run(
|
||||
async ({
|
||||
flags: {
|
||||
|
@ -59,27 +67,34 @@ run(
|
|||
throw createFailError(`${chalk.white.bgRed(' I18N ERROR ')} --fix can't have a value`);
|
||||
}
|
||||
|
||||
const config = await mergeConfigs(includeConfig);
|
||||
const srcPaths = Array().concat(path || ['./src', './packages', './x-pack']);
|
||||
|
||||
if (config.translations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const list = new Listr(
|
||||
[
|
||||
{
|
||||
title: 'Checking .i18nrc.json files',
|
||||
task: () => new Listr(checkConfigs(includeConfig), { exitOnError: true }),
|
||||
},
|
||||
{
|
||||
title: 'Merging .i18nrc.json files',
|
||||
task: () => new Listr(mergeConfigs(includeConfig), { exitOnError: true }),
|
||||
},
|
||||
{
|
||||
title: 'Checking For Untracked Messages based on .i18nrc.json',
|
||||
task: () => new Listr(extractUntrackedMessages(srcPaths, config), { exitOnError: true }),
|
||||
skip: skipNoTranslations,
|
||||
task: ({ config }) =>
|
||||
new Listr(extractUntrackedMessages(srcPaths), { exitOnError: true }),
|
||||
},
|
||||
{
|
||||
title: 'Validating Default Messages',
|
||||
task: () =>
|
||||
new Listr(extractDefaultMessages({ path: srcPaths, config }), { exitOnError: true }),
|
||||
skip: skipNoTranslations,
|
||||
task: ({ config }) =>
|
||||
new Listr(extractDefaultMessages(config, srcPaths), { exitOnError: true }),
|
||||
},
|
||||
{
|
||||
title: 'Compatibility Checks',
|
||||
task: () =>
|
||||
skip: skipNoTranslations,
|
||||
task: ({ config }) =>
|
||||
new Listr(
|
||||
checkCompatibility(
|
||||
config,
|
||||
|
|
|
@ -21,14 +21,8 @@ import chalk from 'chalk';
|
|||
import Listr from 'listr';
|
||||
import { resolve } from 'path';
|
||||
|
||||
import {
|
||||
ErrorReporter,
|
||||
mergeConfigs,
|
||||
serializeToJson,
|
||||
serializeToJson5,
|
||||
writeFileAsync,
|
||||
} from './i18n';
|
||||
import { extractDefaultMessages } from './i18n/tasks';
|
||||
import { ErrorReporter, serializeToJson, serializeToJson5, writeFileAsync } from './i18n';
|
||||
import { extractDefaultMessages, mergeConfigs } from './i18n/tasks';
|
||||
import { createFailError, run } from './run';
|
||||
|
||||
run(
|
||||
|
@ -52,13 +46,17 @@ run(
|
|||
`${chalk.white.bgRed(' I18N ERROR ')} --path and --include-config require a value`
|
||||
);
|
||||
}
|
||||
|
||||
const config = await mergeConfigs(includeConfig);
|
||||
const srcPaths = Array().concat(path || ['./src', './packages', './x-pack']);
|
||||
|
||||
const list = new Listr([
|
||||
{
|
||||
title: 'Merging .i18nrc.json files',
|
||||
task: () => new Listr(mergeConfigs(includeConfig), { exitOnError: true }),
|
||||
},
|
||||
{
|
||||
title: 'Extracting Default Messages',
|
||||
task: () => new Listr(extractDefaultMessages({ path, config }), { exitOnError: true }),
|
||||
task: ({ config }) =>
|
||||
new Listr(extractDefaultMessages(config, srcPaths), { exitOnError: true }),
|
||||
},
|
||||
{
|
||||
title: 'Writing to file',
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
import chalk from 'chalk';
|
||||
import Listr from 'listr';
|
||||
|
||||
import { ErrorReporter, integrateLocaleFiles, mergeConfigs } from './i18n';
|
||||
import { extractDefaultMessages } from './i18n/tasks';
|
||||
import { ErrorReporter, integrateLocaleFiles } from './i18n';
|
||||
import { extractDefaultMessages, mergeConfigs } from './i18n/tasks';
|
||||
import { createFailError, run } from './run';
|
||||
|
||||
run(
|
||||
|
@ -75,15 +75,21 @@ run(
|
|||
);
|
||||
}
|
||||
|
||||
const config = await mergeConfigs(includeConfig);
|
||||
const srcPaths = Array().concat(path || ['./src', './packages', './x-pack']);
|
||||
|
||||
const list = new Listr([
|
||||
{
|
||||
title: 'Merging .i18nrc.json files',
|
||||
task: () => new Listr(mergeConfigs(includeConfig), { exitOnError: true }),
|
||||
},
|
||||
{
|
||||
title: 'Extracting Default Messages',
|
||||
task: () => new Listr(extractDefaultMessages({ path, config }), { exitOnError: true }),
|
||||
task: ({ config }) =>
|
||||
new Listr(extractDefaultMessages(config, srcPaths), { exitOnError: true }),
|
||||
},
|
||||
{
|
||||
title: 'Intregrating Locale File',
|
||||
task: async ({ messages }) => {
|
||||
task: async ({ messages, config }) => {
|
||||
await integrateLocaleFiles(messages, {
|
||||
sourceFileName: source,
|
||||
targetFileName: target,
|
||||
|
|
20
src/legacy/server/i18n/constants.js
Normal file
20
src/legacy/server/i18n/constants.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export const I18N_RC = '.i18nrc.json';
|
47
src/legacy/server/i18n/get_translations_path.js
Normal file
47
src/legacy/server/i18n/get_translations_path.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { promisify } from 'util';
|
||||
import { readFile } from 'fs';
|
||||
import { resolve, dirname } from 'path';
|
||||
import globby from 'globby';
|
||||
|
||||
const readFileAsync = promisify(readFile);
|
||||
|
||||
export async function getTranslationPaths({ cwd, glob }) {
|
||||
const entries = await globby(glob, { cwd });
|
||||
const translationPaths = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
const entryFullPath = resolve(cwd, entry);
|
||||
const pluginBasePath = dirname(entryFullPath);
|
||||
try {
|
||||
const content = await readFileAsync(entryFullPath, 'utf8');
|
||||
const { translations } = JSON.parse(content);
|
||||
translations.forEach(translation => {
|
||||
const translationFullPath = resolve(pluginBasePath, translation);
|
||||
translationPaths.push(translationFullPath);
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to parse .i18nrc.json file at ${entryFullPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
return translationPaths;
|
||||
}
|
|
@ -17,46 +17,30 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { resolve } from 'path';
|
||||
import globby from 'globby';
|
||||
import { i18n, i18nLoader } from '@kbn/i18n';
|
||||
|
||||
import { basename } from 'path';
|
||||
import { fromRoot } from '../../utils';
|
||||
import { getTranslationPaths } from './get_translations_path';
|
||||
import { I18N_RC } from './constants';
|
||||
|
||||
export async function i18nMixin(kbnServer, server, config) {
|
||||
const locale = config.get('i18n.locale');
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
const translationsDirs = [fromRoot('src/legacy/ui/translations'), fromRoot('src/legacy/server/translations'), fromRoot('src/core/translations')];
|
||||
|
||||
const groupedEntries = await Promise.all([
|
||||
...config.get('plugins.scanDirs').map(async path => {
|
||||
const entries = await globby(`*/translations/${locale}.json`, {
|
||||
cwd: path,
|
||||
});
|
||||
return entries.map(entry => resolve(path, entry));
|
||||
const translationPaths = await Promise.all([
|
||||
getTranslationPaths({
|
||||
cwd: fromRoot('.'),
|
||||
glob: I18N_RC,
|
||||
}),
|
||||
|
||||
...config.get('plugins.paths').map(async path => {
|
||||
const entries = await globby(
|
||||
[`translations/${locale}.json`, `plugins/*/translations/${locale}.json`],
|
||||
{
|
||||
cwd: path,
|
||||
}
|
||||
);
|
||||
return entries.map(entry => resolve(path, entry));
|
||||
}),
|
||||
|
||||
...translationsDirs.map(async path => {
|
||||
const entries = await globby(`${locale}.json`, {
|
||||
cwd: path,
|
||||
});
|
||||
return entries.map(entry => resolve(path, entry));
|
||||
...config.get('plugins.paths').map(cwd => getTranslationPaths({ cwd, glob: I18N_RC })),
|
||||
...config.get('plugins.scanDirs').map(cwd => getTranslationPaths({ cwd, glob: `*/${I18N_RC}` })),
|
||||
getTranslationPaths({
|
||||
cwd: fromRoot('../kibana-extra'),
|
||||
glob: `*/${I18N_RC}`,
|
||||
}),
|
||||
]);
|
||||
|
||||
const translationPaths = [].concat(...groupedEntries);
|
||||
i18nLoader.registerTranslationFiles(translationPaths);
|
||||
const currentTranslationPaths = [].concat(...translationPaths).filter(translationPath => basename(translationPath, '.json') === locale);
|
||||
i18nLoader.registerTranslationFiles(currentTranslationPaths);
|
||||
|
||||
const translations = await i18nLoader.getTranslationsByLocale(locale);
|
||||
i18n.init(Object.freeze({
|
||||
|
@ -64,5 +48,5 @@ export async function i18nMixin(kbnServer, server, config) {
|
|||
...translations,
|
||||
}));
|
||||
|
||||
server.decorate('server', 'getTranslationsFilePaths', () => translationPaths);
|
||||
server.decorate('server', 'getTranslationsFilePaths', () => currentTranslationPaths);
|
||||
}
|
||||
|
|
43
x-pack/.i18nrc.json
Normal file
43
x-pack/.i18nrc.json
Normal file
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"prefix": "xpack",
|
||||
"paths": {
|
||||
"xpack.actions": "legacy/plugins/actions",
|
||||
"xpack.alerting": "legacy/plugins/alerting",
|
||||
"xpack.apm": "legacy/plugins/apm",
|
||||
"xpack.beatsManagement": "legacy/plugins/beats_management",
|
||||
"xpack.canvas": "legacy/plugins/canvas",
|
||||
"xpack.code": "legacy/plugins/code",
|
||||
"xpack.crossClusterReplication": "legacy/plugins/cross_cluster_replication",
|
||||
"xpack.dashboardMode": "legacy/plugins/dashboard_mode",
|
||||
"xpack.fileUpload": "legacy/plugins/file_upload",
|
||||
"xpack.graph": "legacy/plugins/graph",
|
||||
"xpack.grokDebugger": "legacy/plugins/grokdebugger",
|
||||
"xpack.idxMgmt": "legacy/plugins/index_management",
|
||||
"xpack.indexLifecycleMgmt": "legacy/plugins/index_lifecycle_management",
|
||||
"xpack.infra": "legacy/plugins/infra",
|
||||
"xpack.kueryAutocomplete": "legacy/plugins/kuery_autocomplete",
|
||||
"xpack.licenseMgmt": "legacy/plugins/license_management",
|
||||
"xpack.maps": "legacy/plugins/maps",
|
||||
"xpack.ml": "legacy/plugins/ml",
|
||||
"xpack.logstash": "legacy/plugins/logstash",
|
||||
"xpack.main": "legacy/plugins/xpack_main",
|
||||
"xpack.telemetry": "legacy/plugins/telemetry",
|
||||
"xpack.monitoring": "legacy/plugins/monitoring",
|
||||
"xpack.remoteClusters": "legacy/plugins/remote_clusters",
|
||||
"xpack.reporting": "legacy/plugins/reporting",
|
||||
"xpack.rollupJobs": "legacy/plugins/rollup",
|
||||
"xpack.searchProfiler": "legacy/plugins/searchprofiler",
|
||||
"xpack.siem": "legacy/plugins/siem",
|
||||
"xpack.security": "legacy/plugins/security",
|
||||
"xpack.server": "legacy/server",
|
||||
"xpack.snapshotRestore": "legacy/plugins/snapshot_restore",
|
||||
"xpack.spaces": "legacy/plugins/spaces",
|
||||
"xpack.upgradeAssistant": "legacy/plugins/upgrade_assistant",
|
||||
"xpack.uptime": "legacy/plugins/uptime",
|
||||
"xpack.watcher": "legacy/plugins/watcher"
|
||||
},
|
||||
"translations": [
|
||||
"plugins/translations/translations/zh-CN.json",
|
||||
"plugins/translations/translations/ja-JP.json"
|
||||
]
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
"yarn.lock",
|
||||
"tsconfig.json",
|
||||
"index.js",
|
||||
".i18nrc.json",
|
||||
"plugins/**/*",
|
||||
"legacy/plugins/reporting/.phantom/*",
|
||||
"legacy/plugins/reporting/.chromium/*",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue