Skip to content

Commit fb516e5

Browse files
committed
FEATURE: support text-direction
Set the `dir` attribute when setting a language. RTL languages are configured in the settings
1 parent 5697844 commit fb516e5

File tree

7 files changed

+182
-13
lines changed

7 files changed

+182
-13
lines changed

Configuration/Settings.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ Neos:
77
resource: resource://Prgfx.Neos.TextPartLanguage/Public/JavaScript/TextPartLanguage/Plugin.js
88
frontendConfiguration:
99
'Prgfx.Neos.TextPartLanguage:languages': ${Configuration.setting('Neos.Neos.userInterface.availableLanguages')}
10+
'Prgfx.Neos.TextPartLanguage:languageDirections':
11+
# ckeditor5 RTL_LANGUAGE_CODES
12+
# Arabic
13+
ar: rtl
14+
ara: rtl
15+
# Persian
16+
fa: rtl
17+
per: rtl
18+
fas: rtl
19+
# Hebrew
20+
he: rtl
21+
heb: rtl
22+
# Kurdish
23+
ku: rtl
24+
kur: rtl
25+
# Uighur, Uyghur
26+
ug: rtl
27+
uig: rtl
1028
userInterface:
1129
translation:
1230
autoInclude:

Resources/Private/JavaScript/TextPartLanguage/src/LanguageSelect.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { $transform } from 'plow-js';
77
import { selectors } from '@neos-project/neos-ui-redux-store';
88
import * as CkEditorApi from '@neos-project/neos-ui-ckeditor5-bindings';
99
import { commandName } from './command';
10+
import { parseLanguageAttribute } from './util';
1011

1112
export const sanitizeOptions = (options) =>
1213
Object.entries(options || {}).filter(tpl => !!tpl[1]);
@@ -43,7 +44,12 @@ export default class LanguageSelect extends PureComponent {
4344
const placeholderKey = this.props.inlineEditorOptions.textLanguages.placeholder
4445
|| 'Prgfx.Neos.TextPartLanguage:Editor:placeholder';
4546
const placeholder = this.props.i18nRegistry.translate(placeholderKey);
46-
const currentValue = this.props.formattingUnderCursor.textPartLanguage || null;
47+
const currentAttributeValue = this.props.formattingUnderCursor.textPartLanguage || null;
48+
let currentValue = null;
49+
if (currentAttributeValue) {
50+
currentValue = parseLanguageAttribute(currentAttributeValue).languageCode;
51+
}
52+
4753
return (
4854
<SelectBox
4955
options={options}

Resources/Private/JavaScript/TextPartLanguage/src/editing.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { Plugin } from 'ckeditor5-exports';
22
import TextPartLanguageCommand, { commandName } from './command';
3+
import {
4+
getLanguageDirection,
5+
parseLanguageAttribute,
6+
stringifyLanguageAttribute,
7+
} from './util';
38

49
export const attributeName = 'language';
510

@@ -10,9 +15,8 @@ export default class TextPartLanguageEditing extends Plugin {
1015

1116
constructor(editor) {
1217
super(editor);
13-
editor.config.define('language', {
14-
textPartLanguage: []
15-
});
18+
this.languageDirectionLookup = getLanguageDirection(editor.config.get('textPartLanguage').languageDirections);
19+
this.stringifyLanguageAttribute = stringifyLanguageAttribute(this.languageDirectionLookup);
1620
}
1721

1822
init() {
@@ -32,7 +36,11 @@ export default class TextPartLanguageEditing extends Plugin {
3236
conversion.for('upcast').elementToAttribute({
3337
model: {
3438
key: attributeName,
35-
value: viewElement => viewElement.getAttribute('lang')
39+
value: viewElement => {
40+
const languageCode = viewElement.getAttribute('lang');
41+
const textDirection = viewElement.getAttribute('dir');
42+
return this.stringifyLanguageAttribute(languageCode, textDirection);
43+
}
3644
},
3745
view: {
3846
name: 'span',
@@ -47,8 +55,11 @@ export default class TextPartLanguageEditing extends Plugin {
4755
return;
4856
}
4957

58+
const { languageCode, textDirection } = parseLanguageAttribute(attributeValue);
59+
5060
return writer.createAttributeElement('span', {
51-
lang: attributeValue,
61+
lang: languageCode,
62+
dir: textDirection || this.languageDirectionLookup(languageCode),
5263
});
5364
}
5465
});

Resources/Private/JavaScript/TextPartLanguage/src/manifest.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@ manifest('Prgfx.Neos.TextPartLanguage', {}, (globalRegistry, { frontendConfigura
1010
const config = ckEditorRegistry.get('config');
1111

1212
const defaultLanguages = frontendConfiguration['Prgfx.Neos.TextPartLanguage:languages'] || {};
13+
const languageDirections = frontendConfiguration['Prgfx.Neos.TextPartLanguage:languageDirections'] || {};
1314

1415
config.set('textPartLanguage', ckeEditorConfiguration => {
1516
ckeEditorConfiguration.plugins = ckeEditorConfiguration.plugins || [];
1617
ckeEditorConfiguration.plugins.push(TextPartLanguage);
18+
ckeEditorConfiguration.textPartLanguage = {
19+
languageDirections,
20+
};
1721
return ckeEditorConfiguration;
1822
});
1923

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @param {Record<string, 'rtl'|null>} lookupTable
3+
* @returns {function(string): string}
4+
*/
5+
export const getLanguageDirection = (lookupTable) =>
6+
/**
7+
* @param {string} languageCode
8+
* @returns {'rtl'|'ltr'}
9+
*/
10+
(languageCode) => lookupTable[languageCode] || 'ltr';
11+
12+
/**
13+
* Stringify language-code and text-direction to a ckeditor model attribute value
14+
* @param {ReturnType<getLanguageDirection>} languageDirectionLookup
15+
* @returns {(function(string, string?): string)}
16+
*/
17+
export const stringifyLanguageAttribute = (languageDirectionLookup) =>
18+
/**
19+
* @param {string} languageCode
20+
* @param {string} textDirection
21+
* @returns {string}
22+
*/
23+
(languageCode, textDirection) => {
24+
if (!textDirection) {
25+
const isoCode = languageCode.split(/[-_]/)[0];
26+
textDirection = languageDirectionLookup(isoCode);
27+
}
28+
return `${languageCode}:${textDirection}`;
29+
};
30+
31+
/**
32+
* Retrieves language properties from the attribute as stringified by {@link stringifyLanguageAttribute}.
33+
* @param {string} attributeString
34+
* @returns {{textDirection: string, languageCode: string}}
35+
*/
36+
export const parseLanguageAttribute = (attributeString) => {
37+
const [ languageCode, textDirection ] = attributeString.split(':');
38+
return { languageCode, textDirection };
39+
};

Resources/Public/JavaScript/TextPartLanguage/Plugin.js

Lines changed: 97 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Resources/Public/JavaScript/TextPartLanguage/Plugin.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)