Skip to content

Commit

Permalink
[7.x] [i18n] .i18nrc file as the source of truth and enhance tooling (#…
Browse files Browse the repository at this point in the history
…39774) (#43284)

* merge conflicts

* add inspector path
  • Loading branch information
Bamieh authored and stacey-gammon committed Aug 14, 2019
1 parent 1326d58 commit 5adf0f1
Show file tree
Hide file tree
Showing 20 changed files with 314 additions and 142 deletions.
43 changes: 3 additions & 40 deletions .i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"core": "src/core",
"inputControl": "src/legacy/core_plugins/input_control_vis",
"inspectorViews": "src/legacy/core_plugins/inspector_views",
"inspector": "src/plugins/inspector",
"interpreter": "src/legacy/core_plugins/interpreter",
"dashboardEmbeddableContainer": "src/legacy/core_plugins/dashboard_embeddable_container",
"kbn": "src/legacy/core_plugins/kibana",
Expand All @@ -24,46 +25,8 @@
"timelion": "src/legacy/core_plugins/timelion",
"visTypeTagCloud": "src/legacy/core_plugins/vis_type_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",
"inspector": "src/plugins/inspector"
"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": []
}
2 changes: 1 addition & 1 deletion config/kibana.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,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"
1 change: 1 addition & 0 deletions src/dev/build/tasks/copy_source_task.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const CopySourceTask = {
'webpackShims/**',
'config/kibana.yml',
'tsconfig*.json',
'.i18nrc.json',
'kibana.d.ts'
],
});
Expand Down
54 changes: 28 additions & 26 deletions src/dev/i18n/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
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}`);
}
}
}

for (const configPath of Array.isArray(configPaths) ? configPaths : [configPaths]) {
const additionalConfig: I18nConfig = {
paths: {},
exclude: [],
translations: [],
...JSON.parse(await readFileAsync(resolve(configPath))),
};
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)) {
mergedConfig.paths[namespace] = normalizePath(resolve(configPath, '..', path));
}
for (const [namespace, path] of Object.entries(additionalConfig.paths)) {
config.paths[namespace] = normalizePath(resolve(configPath, '..', path));
}

for (const exclude of additionalConfig.exclude) {
mergedConfig.exclude.push(normalizePath(resolve(configPath, '..', exclude)));
}
for (const exclude of additionalConfig.exclude) {
config.exclude.push(normalizePath(resolve(configPath, '..', exclude)));
}

for (const translations of additionalConfig.translations) {
mergedConfig.translations.push(normalizePath(resolve(configPath, '..', translations)));
}
for (const translations of additionalConfig.translations) {
config.translations.push(normalizePath(resolve(configPath, '..', translations)));
}

return mergedConfig;
return config;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/dev/i18n/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
9 changes: 7 additions & 2 deletions src/dev/i18n/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 44 additions & 0 deletions src/dev/i18n/tasks/check_configs.ts
Original file line number Diff line number Diff line change
@@ -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}`,
}));
}
10 changes: 1 addition & 9 deletions src/dev/i18n/tasks/extract_default_translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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 }>;
Expand Down
12 changes: 6 additions & 6 deletions src/dev/i18n/tasks/extract_untracked_translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 2 additions & 0 deletions src/dev/i18n/tasks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 43 additions & 0 deletions src/dev/i18n/tasks/merge_configs.ts
Original file line number Diff line number Diff line change
@@ -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}`,
}));
}
5 changes: 5 additions & 0 deletions src/dev/i18n/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
37 changes: 26 additions & 11 deletions src/dev/run_i18n_check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 5adf0f1

Please sign in to comment.