-
Notifications
You must be signed in to change notification settings - Fork 8.5k
migrate i18n mixin to KP #81799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
migrate i18n mixin to KP #81799
Changes from 16 commits
1250c9a
39559b6
19f2155
f022822
ea70bbf
305d323
e28215e
dbdea7a
19cf5fe
51b1d04
9812a93
bbc9bbd
fd1419c
7e7d247
1914c2f
9c8f087
6b6f58d
3eb3a91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <!-- Do not edit this file. It is automatically generated by API Documenter. --> | ||
|
|
||
| [Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreSetup](./kibana-plugin-core-server.coresetup.md) > [i18n](./kibana-plugin-core-server.coresetup.i18n.md) | ||
|
|
||
| ## CoreSetup.i18n property | ||
|
|
||
| [I18nServiceSetup](./kibana-plugin-core-server.i18nservicesetup.md) | ||
|
|
||
| <b>Signature:</b> | ||
|
|
||
| ```typescript | ||
| i18n: I18nServiceSetup; | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <!-- Do not edit this file. It is automatically generated by API Documenter. --> | ||
|
|
||
| [Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [I18nServiceSetup](./kibana-plugin-core-server.i18nservicesetup.md) > [getLocale](./kibana-plugin-core-server.i18nservicesetup.getlocale.md) | ||
|
|
||
| ## I18nServiceSetup.getLocale() method | ||
|
|
||
| Return the locale currently in use. | ||
|
|
||
| <b>Signature:</b> | ||
|
|
||
| ```typescript | ||
| getLocale(): string; | ||
| ``` | ||
| <b>Returns:</b> | ||
|
|
||
| `string` | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <!-- Do not edit this file. It is automatically generated by API Documenter. --> | ||
|
|
||
| [Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [I18nServiceSetup](./kibana-plugin-core-server.i18nservicesetup.md) > [getTranslationFiles](./kibana-plugin-core-server.i18nservicesetup.gettranslationfiles.md) | ||
|
|
||
| ## I18nServiceSetup.getTranslationFiles() method | ||
|
|
||
| Return the list of translation files currently in use. | ||
|
|
||
| <b>Signature:</b> | ||
|
|
||
| ```typescript | ||
| getTranslationFiles(): string[]; | ||
| ``` | ||
| <b>Returns:</b> | ||
|
|
||
| `string[]` | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <!-- Do not edit this file. It is automatically generated by API Documenter. --> | ||
|
|
||
| [Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [I18nServiceSetup](./kibana-plugin-core-server.i18nservicesetup.md) | ||
|
|
||
| ## I18nServiceSetup interface | ||
|
|
||
|
|
||
| <b>Signature:</b> | ||
|
|
||
| ```typescript | ||
| export interface I18nServiceSetup | ||
| ``` | ||
|
|
||
| ## Methods | ||
|
|
||
| | Method | Description | | ||
| | --- | --- | | ||
| | [getLocale()](./kibana-plugin-core-server.i18nservicesetup.getlocale.md) | Return the locale currently in use. | | ||
| | [getTranslationFiles()](./kibana-plugin-core-server.i18nservicesetup.gettranslationfiles.md) | Return the list of translation files currently in use. | | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| /* | ||
| * 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 { getKibanaTranslationFiles } from './get_kibana_translation_files'; | ||
| import { getTranslationPaths } from './get_translation_paths'; | ||
|
|
||
| const mockGetTranslationPaths = getTranslationPaths as jest.Mock; | ||
|
|
||
| jest.mock('./get_translation_paths', () => ({ | ||
| getTranslationPaths: jest.fn().mockResolvedValue([]), | ||
| })); | ||
| jest.mock('../utils', () => ({ | ||
| fromRoot: jest.fn().mockImplementation((path: string) => path), | ||
| })); | ||
|
|
||
| const locale = 'en'; | ||
|
|
||
| describe('getKibanaTranslationPaths', () => { | ||
| beforeEach(() => { | ||
| jest.clearAllMocks(); | ||
| }); | ||
|
|
||
| it('calls getTranslationPaths against kibana root and kibana-extra', async () => { | ||
| await getKibanaTranslationFiles(locale, []); | ||
|
|
||
| expect(mockGetTranslationPaths).toHaveBeenCalledTimes(2); | ||
|
|
||
| expect(mockGetTranslationPaths).toHaveBeenCalledWith({ | ||
| cwd: '.', | ||
| nested: true, | ||
| }); | ||
|
|
||
| expect(mockGetTranslationPaths).toHaveBeenCalledWith({ | ||
| cwd: '../kibana-extra', | ||
| nested: true, | ||
| }); | ||
| }); | ||
|
|
||
| it('calls getTranslationPaths for each config returned in plugin.paths and plugins.scanDirs', async () => { | ||
| const pluginPaths = ['/path/to/pluginA', '/path/to/pluginB']; | ||
|
|
||
| await getKibanaTranslationFiles(locale, pluginPaths); | ||
|
|
||
| expect(mockGetTranslationPaths).toHaveBeenCalledTimes(2 + pluginPaths.length); | ||
|
|
||
| pluginPaths.forEach((pluginPath) => { | ||
| expect(mockGetTranslationPaths).toHaveBeenCalledWith({ | ||
| cwd: pluginPath, | ||
| nested: false, | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| it('only return files for specified locale', async () => { | ||
| mockGetTranslationPaths.mockResolvedValueOnce(['/root/en.json', '/root/fr.json']); | ||
| mockGetTranslationPaths.mockResolvedValueOnce([ | ||
| '/kibana-extra/en.json', | ||
| '/kibana-extra/fr.json', | ||
| ]); | ||
|
|
||
| const translationFiles = await getKibanaTranslationFiles('en', []); | ||
|
|
||
| expect(translationFiles).toEqual(['/root/en.json', '/kibana-extra/en.json']); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -17,26 +17,27 @@ | |||
| * under the License. | ||||
| */ | ||||
|
|
||||
| import { KibanaConfig } from '../kbn_server'; | ||||
| import { fromRoot } from '../../../core/server/utils'; | ||||
| import { I18N_RC } from './constants'; | ||||
| import { basename } from 'path'; | ||||
| import { fromRoot } from '../utils'; | ||||
| import { getTranslationPaths } from './get_translation_paths'; | ||||
|
|
||||
| export async function getKibanaTranslationPaths(config: Pick<KibanaConfig, 'get'>) { | ||||
| return await Promise.all([ | ||||
| export const getKibanaTranslationFiles = async ( | ||||
| locale: string, | ||||
| pluginPaths: string[] | ||||
| ): Promise<string[]> => { | ||||
| const translationPaths = await Promise.all([ | ||||
| getTranslationPaths({ | ||||
| cwd: fromRoot('.'), | ||||
| glob: `*/${I18N_RC}`, | ||||
| nested: true, | ||||
| }), | ||||
|
Comment on lines
29
to
32
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAIK, this is only used to get the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pgayvallet only in
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does it mean that it traverses all the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, I didn't check implementation. but
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, you're right. I kinda wanted to rename it to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will need something similar to |
||||
| ...(config.get('plugins.paths') as string[]).map((cwd) => | ||||
| getTranslationPaths({ cwd, glob: I18N_RC }) | ||||
| ), | ||||
| ...(config.get('plugins.scanDirs') as string[]).map((cwd) => | ||||
| getTranslationPaths({ cwd, glob: `*/${I18N_RC}` }) | ||||
| ), | ||||
| ...pluginPaths.map((pluginPath) => getTranslationPaths({ cwd: pluginPath, nested: false })), | ||||
|
Comment on lines
-31
to
+33
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a slight change of behavior here: Legacy was just reading the plugin config and scanning the I chose to change that by retrieving the list of enabled plugins from the plugin service, and check if there is a i18n file at the root level of each plugin instead. This is basically the same, except that we are not loading translation files for disabled plugins (note that this only concerns 3rd parties plugin, as kibana translations are all defined from a single |
||||
| getTranslationPaths({ | ||||
| cwd: fromRoot('../kibana-extra'), | ||||
| glob: `*/${I18N_RC}`, | ||||
| nested: true, | ||||
| }), | ||||
|
Comment on lines
34
to
37
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we really still using
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As long as we still support running plugins from that path then we should keep it. If not there is no need to.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We still search for a plugin in
kbn-pm supports it as well https://github.com/elastic/kibana/blob/0068c87d6fe7713351acbf42080faa09ff73ef15/packages/kbn-pm/README.mdBut I haven't checked whether it works |
||||
| ]); | ||||
| } | ||||
|
|
||||
| return ([] as string[]) | ||||
| .concat(...translationPaths) | ||||
| .filter((translationPath) => basename(translationPath, '.json') === locale); | ||||
| }; | ||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /* | ||
| * 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 globbyMock = jest.fn(); | ||
| jest.doMock('globby', () => globbyMock); | ||
|
|
||
| export const readFileMock = jest.fn(); | ||
| jest.doMock('./fs', () => ({ | ||
| readFile: readFileMock, | ||
| })); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| /* | ||
| * 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 { globbyMock, readFileMock } from './get_translation_paths.test.mocks'; | ||
| import { getTranslationPaths } from './get_translation_paths'; | ||
|
|
||
| describe('getTranslationPaths', () => { | ||
| beforeEach(() => { | ||
| globbyMock.mockReset(); | ||
| readFileMock.mockReset(); | ||
|
|
||
| globbyMock.mockResolvedValue([]); | ||
| readFileMock.mockResolvedValue('{}'); | ||
| }); | ||
|
|
||
| it('calls `globby` with the correct parameters', async () => { | ||
| getTranslationPaths({ cwd: '/some/cwd', nested: false }); | ||
|
|
||
| expect(globbyMock).toHaveBeenCalledTimes(1); | ||
| expect(globbyMock).toHaveBeenCalledWith('.i18nrc.json', { cwd: '/some/cwd' }); | ||
|
|
||
| globbyMock.mockClear(); | ||
|
|
||
| await getTranslationPaths({ cwd: '/other/cwd', nested: true }); | ||
|
|
||
| expect(globbyMock).toHaveBeenCalledTimes(1); | ||
| expect(globbyMock).toHaveBeenCalledWith('*/.i18nrc.json', { cwd: '/other/cwd' }); | ||
| }); | ||
|
|
||
| it('calls `readFile` for each entry returned by `globby`', async () => { | ||
| const entries = [join('pathA', '.i18nrc.json'), join('pathB', '.i18nrc.json')]; | ||
| globbyMock.mockResolvedValue(entries); | ||
|
|
||
| const cwd = '/kibana-extra'; | ||
|
|
||
| await getTranslationPaths({ cwd, nested: true }); | ||
|
|
||
| expect(readFileMock).toHaveBeenCalledTimes(2); | ||
|
|
||
| expect(readFileMock).toHaveBeenNthCalledWith(1, resolve(cwd, entries[0]), 'utf8'); | ||
| expect(readFileMock).toHaveBeenNthCalledWith(2, resolve(cwd, entries[1]), 'utf8'); | ||
| }); | ||
|
|
||
| it('returns the absolute path to the translation files', async () => { | ||
| const entries = ['.i18nrc.json']; | ||
| globbyMock.mockResolvedValue(entries); | ||
|
|
||
| const i18nFileContent = { | ||
| translations: ['translations/en.json', 'translations/fr.json'], | ||
| }; | ||
| readFileMock.mockResolvedValue(JSON.stringify(i18nFileContent)); | ||
|
|
||
| const cwd = '/cwd'; | ||
|
|
||
| const translationPaths = await getTranslationPaths({ cwd, nested: true }); | ||
|
|
||
| expect(translationPaths).toEqual([ | ||
| resolve(cwd, 'translations/en.json'), | ||
| resolve(cwd, 'translations/fr.json'), | ||
| ]); | ||
| }); | ||
|
|
||
| it('throws if i18nrc parsing fails', async () => { | ||
| globbyMock.mockResolvedValue(['.i18nrc.json']); | ||
| readFileMock.mockRejectedValue(new Error('error parsing file')); | ||
|
|
||
| await expect( | ||
| getTranslationPaths({ cwd: '/cwd', nested: true }) | ||
| ).rejects.toThrowErrorMatchingInlineSnapshot( | ||
| `"Failed to parse .i18nrc.json file at /cwd/.i18nrc.json"` | ||
| ); | ||
| }); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.