[7.x] i18n - allow plugins to specify multiple paths (#46578) (#46712)

* allow plugins to specify multiple paths

* use native Array.flat instead of _.flatten()
This commit is contained in:
Larry Gregory 2019-09-26 11:52:13 -04:00 committed by GitHub
parent 612aaf4bf3
commit 4416163ecb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 50 additions and 21 deletions

View file

@ -0,0 +1,8 @@
/* eslint-disable */
i18n('plugin_3.duplicate_id', { defaultMessage: 'Message 1' });
i18n.translate('plugin_3.duplicate_id', {
defaultMessage: 'Message 2',
description: 'Message description',
});

View file

@ -23,7 +23,7 @@ import { resolve } from 'path';
import { normalizePath, readFileAsync } from '.';
export interface I18nConfig {
paths: Record<string, string>;
paths: Record<string, string[]>;
exclude: string[];
translations: string[];
prefix?: string;
@ -49,8 +49,9 @@ export async function assignConfigFromPath(
...JSON.parse(await readFileAsync(resolve(configPath))),
};
for (const [namespace, path] of Object.entries(additionalConfig.paths)) {
config.paths[namespace] = normalizePath(resolve(configPath, '..', path));
for (const [namespace, namespacePaths] of Object.entries(additionalConfig.paths)) {
const paths = Array.isArray(namespacePaths) ? namespacePaths : [namespacePaths];
config.paths[namespace] = paths.map(path => normalizePath(resolve(configPath, '..', path)));
}
for (const exclude of additionalConfig.exclude) {
@ -71,7 +72,7 @@ export async function assignConfigFromPath(
* @param config I18n config instance.
*/
export function filterConfigPaths(inputPaths: string[], config: I18nConfig) {
const availablePaths = Object.values(config.paths);
const availablePaths = Object.values(config.paths).flat();
const pathsForExtraction = new Set();
for (const inputPath of inputPaths) {

View file

@ -50,8 +50,8 @@ function filterEntries(entries, exclude) {
export function validateMessageNamespace(id, filePath, allowedPaths, reporter) {
const normalizedPath = normalizePath(filePath);
const [expectedNamespace] = Object.entries(allowedPaths).find(([, pluginPath]) =>
normalizedPath.startsWith(`${pluginPath}/`)
const [expectedNamespace] = Object.entries(allowedPaths).find(([, pluginPaths]) =>
pluginPaths.some(pluginPath => normalizedPath.startsWith(`${pluginPath}/`))
);
if (!id.startsWith(`${expectedNamespace}.`)) {

View file

@ -30,13 +30,17 @@ const pluginsPaths = [
path.join(fixturesPath, 'test_plugin_1'),
path.join(fixturesPath, 'test_plugin_2'),
path.join(fixturesPath, 'test_plugin_3'),
path.join(fixturesPath, 'test_plugin_3_additional_path'),
];
const config = {
paths: {
plugin_1: 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1',
plugin_2: 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2',
plugin_3: 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3',
plugin_1: ['src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1'],
plugin_2: ['src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2'],
plugin_3: [
'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3',
'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3_additional_path'
],
},
exclude: [],
};
@ -69,6 +73,20 @@ describe('dev/i18n/extract_default_translations', () => {
expect(() => validateMessageNamespace(id, filePath, config.paths)).not.toThrow();
});
test('validates message namespace with multiple paths', () => {
const id = 'plugin_3.message-id';
const filePath1 = path.resolve(
__dirname,
'__fixtures__/extract_default_translations/test_plugin_3/test_file.html'
);
const filePath2 = path.resolve(
__dirname,
'__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.html'
);
expect(() => validateMessageNamespace(id, filePath1, config.paths)).not.toThrow();
expect(() => validateMessageNamespace(id, filePath2, config.paths)).not.toThrow();
});
test('throws on wrong message namespace', () => {
const report = jest.fn();
const id = 'wrong_plugin_namespace.message-id';

View file

@ -40,8 +40,8 @@ const defaultIntegrateOptions = {
ignoreUnused: false,
config: {
paths: {
'plugin-1': 'src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_1',
'plugin-2': 'src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_2',
'plugin-1': ['src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_1'],
'plugin-2': ['src/dev/i18n/__fixtures__/integrate_locale_files/test_plugin_2'],
},
exclude: [],
translations: [],

View file

@ -160,17 +160,19 @@ async function writeMessages(
// Use basename of source file name to write the same locale name as the source file has.
const fileName = path.basename(options.sourceFileName);
for (const [namespace, messages] of localizedMessagesByNamespace) {
const destPath = path.resolve(options.config.paths[namespace], 'translations');
for (const namespacedPath of options.config.paths[namespace]) {
const destPath = path.resolve(namespacedPath, 'translations');
try {
await accessAsync(destPath);
} catch (_) {
await makeDirAsync(destPath);
try {
await accessAsync(destPath);
} catch (_) {
await makeDirAsync(destPath);
}
const writePath = path.resolve(destPath, fileName);
await writeFileAsync(writePath, serializeToJson(messages, formats));
options.log.success(`Translations have been integrated to ${normalizePath(writePath)}`);
}
const writePath = path.resolve(destPath, fileName);
await writeFileAsync(writePath, serializeToJson(messages, formats));
options.log.success(`Translations have been integrated to ${normalizePath(writePath)}`);
}
}

View file

@ -42,7 +42,7 @@ export async function extractUntrackedMessagesTask({
reporter: any;
}) {
const inputPaths = Array.isArray(path) ? path : [path || './'];
const availablePaths = Object.values(config.paths);
const availablePaths = Object.values(config.paths).flat();
const ignore = availablePaths.concat([
'**/build/**',
'**/webpackShims/**',