Skip to content

Commit

Permalink
[I18n] Add one-time binding to angularjs i18n (elastic#23499)
Browse files Browse the repository at this point in the history
* Add one-time binding to angularjs i18n

* Add watcher for values property

* Watch values field only if it is provided

* Fix ci
  • Loading branch information
LeanidShutau committed Oct 17, 2018
1 parent 3673012 commit ea5e908
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 42 deletions.
7 changes: 3 additions & 4 deletions packages/kbn-i18n/GUIDELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The following types are supported:
- ToggleSwitch
- LinkLabel and etc.

There is one more complex case, when we have to divide a single expression into different labels.
There is one more complex case, when we have to divide a single expression into different labels.

For example the message before translation looks like:

Expand Down Expand Up @@ -221,8 +221,8 @@ For example:

```js
<button
aria-label="{{'kbn.management.editIndexPattern.removeAriaLabel' | i18n: {defaultMessage: 'Remove index pattern'} }}"
tooltip="{{'kbn.management.editIndexPattern.removeTooltip' | i18n: {defaultMessage: 'Remove index pattern'} }}"
aria-label="{{ ::'kbn.management.editIndexPattern.removeAriaLabel' | i18n: {defaultMessage: 'Remove index pattern'} }}"
tooltip="{{ ::'kbn.management.editIndexPattern.removeTooltip' | i18n: {defaultMessage: 'Remove index pattern'} }}"
>
</button>
```
Expand Down Expand Up @@ -333,4 +333,3 @@ it('should render normally', async () => {
});
// ...
```

4 changes: 2 additions & 2 deletions packages/kbn-i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ import { i18n } from '@kbn/i18n';
i18n.init(messages);
```

One common use-case is that of internationalizing a string constant. Here's an
One common use-case is that of internationalizing a string constant. Here's an
example of how we'd do that:

```js
Expand Down Expand Up @@ -399,7 +399,7 @@ In order to translate attributes in Angular we should use `i18nFilter`:
```html
<input
type="text"
placeholder="{{'KIBANA-MANAGEMENT-OBJECTS-SEARCH_PLACEHOLDER' | i18n: {
placeholder="{{ ::'KIBANA-MANAGEMENT-OBJECTS-SEARCH_PLACEHOLDER' | i18n: {
defaultMessage: 'Search'
} }}"
>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`i18nDirective inserts correct translation html content with values 1`] = `"default-message word"`;

exports[`i18nDirective inserts correct translation html content with values 2`] = `"default-message anotherWord"`;
13 changes: 9 additions & 4 deletions packages/kbn-i18n/src/angular/directive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ angular

describe('i18nDirective', () => {
let compile: angular.ICompileService;
let scope: angular.IRootScopeService;
let scope: angular.IRootScopeService & { word?: string };

beforeEach(angular.mock.module('app'));
beforeEach(
angular.mock.inject(
($compile: angular.ICompileService, $rootScope: angular.IRootScopeService) => {
compile = $compile;
scope = $rootScope.$new();
scope.word = 'word';
}
)
);
Expand All @@ -62,19 +63,23 @@ describe('i18nDirective', () => {
test('inserts correct translation html content with values', () => {
const id = 'id';
const defaultMessage = 'default-message {word}';
const compiledContent = 'default-message word';

const element = angular.element(
`<div
i18n-id="${id}"
i18n-default-message="${defaultMessage}"
i18n-values="{ word: 'word' }"
i18n-values="{ word }"
/>`
);

compile(element)(scope);
scope.$digest();

expect(element.html()).toEqual(compiledContent);
expect(element.html()).toMatchSnapshot();

scope.word = 'anotherWord';
scope.$digest();

expect(element.html()).toMatchSnapshot();
});
});
39 changes: 25 additions & 14 deletions packages/kbn-i18n/src/angular/directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,37 @@ import { IDirective, IRootElementService, IScope } from 'angular';

import { I18nServiceType } from './provider';

export function i18nDirective(i18n: I18nServiceType): IDirective {
interface I18nScope extends IScope {
values?: Record<string, any>;
defaultMessage: string;
id: string;
}

export function i18nDirective(i18n: I18nServiceType): IDirective<I18nScope> {
return {
restrict: 'A',
scope: {
id: '@i18nId',
defaultMessage: '@i18nDefaultMessage',
values: '=i18nValues',
values: '<?i18nValues',
},
link($scope: IScope, $element: IRootElementService) {
$scope.$watchGroup(
['id', 'defaultMessage', 'values'],
([id, defaultMessage = '', values = {}]) => {
$element.html(
i18n(id, {
values,
defaultMessage,
})
);
}
);
link($scope, $element) {
if ($scope.values) {
$scope.$watchCollection('values', () => {
setHtmlContent($element, $scope, i18n);
});
} else {
setHtmlContent($element, $scope, i18n);
}
},
};
}

function setHtmlContent($element: IRootElementService, $scope: I18nScope, i18n: I18nServiceType) {
$element.html(
i18n($scope.id, {
values: $scope.values,
defaultMessage: $scope.defaultMessage,
})
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
data-test-subj="editIndexPattern"
class="kuiViewContent"
role="region"
aria-label="{{'kbn.management.editIndexPattern.detailsAria' | i18n: { defaultMessage: 'Index pattern details' } }}"
aria-label="{{::'kbn.management.editIndexPattern.detailsAria' | i18n: { defaultMessage: 'Index pattern details' } }}"
>
<!-- Header -->
<kbn-management-index-header
Expand Down Expand Up @@ -103,9 +103,9 @@
<input
class="kuiSearchInput__input"
type="text"
aria-label="{{'kbn.management.editIndexPattern.fields.filterAria' | i18n: {defaultMessage: 'Filter'} }}"
aria-label="{{::'kbn.management.editIndexPattern.fields.filterAria' | i18n: {defaultMessage: 'Filter'} }}"
ng-model="fieldFilter"
placeholder="{{'kbn.management.editIndexPattern.fields.filterPlaceholder' | i18n: {defaultMessage: 'Filter'} }}"
placeholder="{{::'kbn.management.editIndexPattern.fields.filterPlaceholder' | i18n: {defaultMessage: 'Filter'} }}"
data-test-subj="indexPatternFieldFilter"
>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<a
data-test-subj="indexPatternFieldEditButton"
ng-href="{{ kbnUrl.getRouteHref(field, 'edit') }}"
aria-label="{{'kbn.management.editIndexPattern.editFieldButton' | i18n: { defaultMessage: 'Edit' } }}"
aria-label="{{::'kbn.management.editIndexPattern.editFieldButton' | i18n: { defaultMessage: 'Edit' } }}"
class="kuiButton kuiButton--basic kuiButton--small"
>
<span aria-hidden="true" class="kuiIcon fa-pencil"></span>
Expand All @@ -12,7 +12,7 @@
ng-if="field.scripted"
ng-click="remove(field)"
class="kuiButton kuiButton--danger kuiButton--small"
aria-label="{{'kbn.management.editIndexPattern.deleteFieldButton' | i18n: { defaultMessage: 'Delete' } }}"
aria-label="{{::'kbn.management.editIndexPattern.deleteFieldButton' | i18n: { defaultMessage: 'Delete' } }}"
>
<span aria-hidden="true" class="kuiIcon fa-trash"></span>
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
<button
ng-if="setDefault"
ng-click="setDefault()"
aria-label="{{'kbn.management.editIndexPattern.setDefaultAria' | i18n: { defaultMessage: 'Set as default index' } }}"
tooltip="{{'kbn.management.editIndexPattern.setDefaultTooltip' | i18n: { defaultMessage: 'Set as default index' } }}"
aria-label="{{::'kbn.management.editIndexPattern.setDefaultAria' | i18n: { defaultMessage: 'Set as default index' } }}"
tooltip="{{::'kbn.management.editIndexPattern.setDefaultTooltip' | i18n: { defaultMessage: 'Set as default index' } }}"
class="kuiButton kuiButton--basic"
data-test-subj="setDefaultIndexPatternButton"
>
Expand All @@ -32,8 +32,8 @@
<button
ng-if="refreshFields"
ng-click="refreshFields()"
aria-label="{{'kbn.management.editIndexPattern.refreshAria' | i18n: { defaultMessage: 'Reload field list' } }}"
tooltip="{{'kbn.management.editIndexPattern.refreshTooltip' | i18n: { defaultMessage: 'Refresh field list' } }}"
aria-label="{{::'kbn.management.editIndexPattern.refreshAria' | i18n: { defaultMessage: 'Reload field list' } }}"
tooltip="{{::'kbn.management.editIndexPattern.refreshTooltip' | i18n: { defaultMessage: 'Refresh field list' } }}"
class="kuiButton kuiButton--basic"
>
<span
Expand All @@ -45,8 +45,8 @@
<button
ng-if="delete"
ng-click="delete()"
aria-label="{{'kbn.management.editIndexPattern.removeAria' | i18n: { defaultMessage: 'Remove index pattern' } }}"
tooltip="{{'kbn.management.editIndexPattern.removeTooltip' | i18n: { defaultMessage: 'Remove index pattern' } }}"
aria-label="{{::'kbn.management.editIndexPattern.removeAria' | i18n: { defaultMessage: 'Remove index pattern' } }}"
tooltip="{{::'kbn.management.editIndexPattern.removeTooltip' | i18n: { defaultMessage: 'Remove index pattern' } }}"
class="kuiButton kuiButton--danger"
data-test-subj="deleteIndexPatternButton"
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="euiPage">
<div class="col-md-2 sidebar-container" role="region" aria-label="{{'kbn.management.editIndexPatternAria' | i18n: { defaultMessage: 'Index patterns' } }}">
<div class="col-md-2 sidebar-container" role="region" aria-label="{{::'kbn.management.editIndexPatternAria' | i18n: { defaultMessage: 'Index patterns' } }}">
<div class="sidebar-list">
<div class="sidebar-item-title full-title">
<h5 data-test-subj="createIndexPatternParent">
Expand Down Expand Up @@ -55,4 +55,4 @@ <h5 data-test-subj="createIndexPatternParent">
<div ng-transclude></div>
</div>
</div>
</div>
</div>
6 changes: 3 additions & 3 deletions src/core_plugins/metric_vis/public/metric_vis_params.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
kbn-accessible-click
aria-expanded="{{!!showColorRange}}"
aria-controls="metricOptionsRanges"
aria-label="{{'metricVis.params.ranges.toggleOptionsAriaLabel' | i18n: { defaultMessage: 'Toggle range options' } }}"
aria-label="{{::'metricVis.params.ranges.toggleOptionsAriaLabel' | i18n: { defaultMessage: 'Toggle range options' } }}"
class="kuiSideBarCollapsibleTitle__label"
ng-click="showColorRange = !showColorRange"
>
Expand Down Expand Up @@ -135,7 +135,7 @@
kbn-accessible-click
aria-expanded="{{!!showColorOptions}}"
aria-controls="metricOptionsColors"
aria-label="{{'metricVis.params.color.toggleOptionsAriaLabel' | i18n: { defaultMessage: 'Toggle color options' } }}"
aria-label="{{::'metricVis.params.color.toggleOptionsAriaLabel' | i18n: { defaultMessage: 'Toggle color options' } }}"
class="kuiSideBarCollapsibleTitle__label"
ng-click="showColorOptions = !showColorOptions"
>
Expand Down Expand Up @@ -219,7 +219,7 @@
kbn-accessible-click
aria-expanded="{{!!showStyle}}"
aria-controls="metricOptionsStyle"
aria-label="{{'metricVis.params.style.toggleOptionsAriaLabel' | i18n: { defaultMessage: 'Toggle style options' } }}"
aria-label="{{::'metricVis.params.style.toggleOptionsAriaLabel' | i18n: { defaultMessage: 'Toggle style options' } }}"
class="kuiSideBarCollapsibleTitle__label"
ng-click="showStyle = !showStyle"
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<p>{{ 'plugin_2.message-id' | i18n: { defaultMessage: 'Message text' } }}</p>
<p>{{ ::'plugin_2.message-id' | i18n: { defaultMessage: 'Message text' } }}</p>
12 changes: 12 additions & 0 deletions src/dev/i18n/extractors/__snapshots__/html.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ Array [
]
`;

exports[`dev/i18n/extractors/html extracts default messages from HTML with one-time binding 1`] = `
Array [
Array [
"kbn.id",
Object {
"context": undefined,
"message": "Message text with {value}",
},
],
]
`;

exports[`dev/i18n/extractors/html throws on empty i18n-id 1`] = `"Empty \\"i18n-id\\" value in angular directive is not allowed."`;

exports[`dev/i18n/extractors/html throws on missing i18n-default-message attribute 1`] = `"Empty defaultMessage in angular directive is not allowed (\\"message-id\\")."`;
33 changes: 32 additions & 1 deletion src/dev/i18n/extractors/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,45 @@ function trimCurlyBraces(string) {
return string.slice(2, -2).trim();
}

/**
* Removes parentheses from the start and the end of a string.
*
* Example: `('id' | i18n: { defaultMessage: 'Message' })`
* @param {string} string string to trim
*/
function trimParentheses(string) {
if (string.startsWith('(') && string.endsWith(')')) {
return string.slice(1, -1);
}

return string;
}

/**
* Removes one-time binding operator `::` from the start of a string.
*
* Example: `::'id' | i18n: { defaultMessage: 'Message' }`
* @param {string} string string to trim
*/
function trimOneTimeBindingOperator(string) {
if (string.startsWith('::')) {
return string.slice(2);
}

return string;
}

function* getFilterMessages(htmlContent) {
const expressions = (htmlContent.match(ANGULAR_EXPRESSION_REGEX) || [])
.filter(expression => expression.includes(I18N_FILTER_MARKER))
.map(trimCurlyBraces);

for (const expression of expressions) {
const filterStart = expression.indexOf(I18N_FILTER_MARKER);
const idExpression = expression.slice(0, filterStart).trim();
const idExpression = trimParentheses(
trimOneTimeBindingOperator(expression.slice(0, filterStart).trim())
);

const filterObjectExpression = expression.slice(filterStart + I18N_FILTER_MARKER.length).trim();

if (!filterObjectExpression || !idExpression) {
Expand Down
11 changes: 11 additions & 0 deletions src/dev/i18n/extractors/html.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ describe('dev/i18n/extractors/html', () => {
expect(actual.sort()).toMatchSnapshot();
});

test('extracts default messages from HTML with one-time binding', () => {
const actual = Array.from(
extractHtmlMessages(`
<div>
{{::'kbn.id' | i18n: { defaultMessage: 'Message text with {value}', values: { value: 'value' } }}}
</div>
`)
);
expect(actual.sort()).toMatchSnapshot();
});

test('throws on empty i18n-id', () => {
const source = Buffer.from(`\
<p
Expand Down

0 comments on commit ea5e908

Please sign in to comment.