Информация о числах


The goal of this module is to encapsulate the whole «translations repository» logic from the rest of the code.

It may be dirty with some preprocessor rules or may need to be generated on the fly depending on the build type (e.g. with or without code splitting – i.e. with separate or built in language file). However, it would be good if it was defined in the repository so for dev purposes we wouldn’t have to do anything. It’d just return the passed string.

Development mode implementation:

export function translate( lang, str ) {
	// Remove the ` ` suffix.
	return str.replace(  \]+\]$, '' );

For the bundles, we have two cases:

  • where only one language is needed,
  • where there are multiple languages.

In case of just one language, it’ll be best to simply replace the param of calls
with the proper value (without the ctx now). This will allow for code splitting and hot-loading plugins without any tricks. It may happen, though, that some strings will then repeat multiple times – e.g. the ones from the core package. While this is going to make an un-gzipped package bigger, there shouldn’t be a difference in case of a gzipped code. Besides, this will be just a few strings anyway and we’ll save some space having such a simple implementation too.

export function translate( lang, str ) {
	return str;

In case of multiple languages we need to have some registry. The implementation will be again simple:

export function translate( lang, str ) {
	return translations;
	// Let this be objects, not maps, because we control the values of lang and str
	// and it will be a bit easier to generate a source of an object programatically.
	// Objects may also be better in terms of code size.

What’s the in this case? In order to ensure that we don’t have name collisions (important for bundle splitting) I’d say that this should be either:

  • A totally unique string – like typical uid, but preferably shorter, using a wider range of unicode characters. A 5 chars long string using a range of all Unicode chars will give us comparable complexity to :

    The thing which worries me is whether there won’t be any issues with encoding – we’ve seen people sourcing CKEditor in some weird encodings and it was always blowing up. But one could use a minifier which encodes special characters and it was be working again.

    Another thing is ability to debug such code. With unreadble ids it may be tricky.

    Finally – creating objects in which these uids are keys may be tricky. I can’t find now whether there are any characters which needs to be escaped (other than the closing quote).

    Anyway, this solution may create unpredictable and stupid issues.

  • Therefore, we may just use sequential ids from a short range (e.g. ). What about code splitting? There are two cases:

    • Code splitting was done when building the whole setup – then there won’t be any problems in using non-conflicting ids, because it’s done by one bundler at one time.
    • Someone, first built e.g. and then, separately (might be a different person), built . In this case, the preset bundle will use normal, short, ids (because it’s the main bundle) and the image feature bundle will use prefixed ids (e.g. ). The idea is that a developer releasing his/her package will use a special bundler setup which configures CKEditor plugin for Webpack to use prefixed ids.

Anyway, this is nothing we have to worry today, because, most likely, we’ll work on releasing standalone package bundles after 1.0.0.

Another thing to notice is that support for multiple languages and code splitting is an optional feature, so we can implement it for just one bundler, i.e. Webpack.

Let’s say, that we want to split a package to some preset X and packages Y and Z.
This will make for 3 files – , , .

The idea is that each files will have an accompanying language(s) files defining a translations needed for this file. So there will be:

  • , , , …
  • , , , …
  • , , , …

In order to run an the X preset with plugins with Polish translations one will need to load:, , , , , .

The language files will be built using entry points like this:

import { define } from '@ckeditor/ckeditor5-utils/src/translation-service';

define( 'pl', {
	'a' 'OK',
	'b' 'Anuluj',
	// ...
} );

And the function will merge these translations into other that it already has.


Plugin decoupling

Right now the plugin requires the plugin. This means that you wouldn’t be able to implement your own engine part and use it with our UI (which is the use case if you want to heavily modify feature behaviour). It also creates odd inheritance-like chains of events where is just an extension of .

For a long time we’ve been discussing to decouple this (see ). The new rule of thumbs are:

  • Create independent plugins. Plugins communicate through the editor (using commands, schema, model, events, etc.) so they don’t need to additionally require one another. As long as a plugin implements a similar signature (e.g. uses the same element names and exposes the same commands) it can replace the one it mimics.
  • Create end-user plugins which glue the sub-plugins, making them easier to enable. The glue should contain minimal (ideally – zero) logic.
  • All plugins should contain minimal code.

So, as @szymonkups showed, the bold feature can be refactored to be build out of completely independent and parts which are glued by the plugin.

Some plugins will also need to refactored in a similar way to that proposed for the link feature () because the third rule means that a plugin like shouldn’t implement itself the whole UI because it will make it non-configurable and non-reusable (plugins have no configuration of their own).


  • Fired when a panel is added to the document.


    The element wrapping the panel.

  • Fired when the object reference changes. This may
    happen when setting the focus on different editor instances in the page.

  • Event fired when a dialog definition is about to be used to create a dialog in
    an editor instance. This event makes it possible to customize the definition
    before creating it.

    Note that this event is called only the first time a specific dialog is
    opened. Successive openings will use the cached dialog, and this event will
    not get fired.



    The name of the dialog.

    The dialog definition that
    is being loaded.

    A dialog instance that the definition is loaded
    for. Introduced in CKEditor 4.13.0.

    The editor instance that will use the dialog.

  • Event fired when a CKEDITOR instance is created, but still before initializing it.
    To interact with a fully initialized instance, use the
    event instead.


    The editor instance that has been created.

  • Event fired when a CKEDITOR instance is destroyed.


    The editor instance that has been destroyed.

  • Event fired when CKEDITOR instance’s components (configuration, languages and plugins) are fully
    loaded and initialized. However, the editor will be fully ready for interaction
    on .


    This editor instance that has been loaded.

  • Event fired when a CKEDITOR instance is created, fully initialized and ready for interaction.


    The editor instance that has been created.

  • Fired when a CKEDITOR core object is fully loaded and ready for interaction.

  • since 4.5.4

    Fired by and methods.
    Default listener logs provided information to the console.

    This event can be used to provide a custom error/warning handler:



    Log type. Can be or .

    Error code describing the reported problem.

    Additional data associated with this log event.

  • Fired when the last instance has been destroyed. This event is used to perform
    global memory cleanup.

PS. PO file generation

We’ve been changing the idea which part of call is , and twice already, so let’s clarify this:

For the following calls and :

t( 'OK' );
t( 'button' );
t( 'button ' );
	"OK" "Label for OK button in a dialog."
	"button" "Name of a clickable form control, e.g. 'OK button'."
	"button " "Button as used in clothes."

The file would look like this:

msgid "OK"
msgstr "OK"
msgctxt "Label for OK button in a dialog."

msgid "button"
msgstr "button"
msgctxt "Name of a clickable form control, e.g. 'OK button'."

msgid "button"
msgstr "button"
msgctxt "Button as used in clothes."

In this case the can be empty because then will use . In fact, in the samples I found in it’s empty.

Regarding :

We ignore this issue. It’s very rare situation.

Feedback needed

There are couple of questions that we’d like to ask community:

  • The image size is saved as inline style applied to element in pixels. We’re wondering whether there are use cases when you’d like to use percents rather than pixels? If so please, let us know the use cases.
  • By default . It doesn’t feel right to be limited to that when using the resize feature, so we decided to add a class to the once image is resized — in which case it doesn’t have this limitation. It also gives an easy option to customize it in case developer decides that such images should be in fact limited. We don’t see any downsides here, but feel free to share your opinion.

Naturally if you’d like to share any thought beyond that, feel free to do so!

Server side vs headless

I think that we were more focused on allowing running CKEditor in Node.js while this isn’t a super clear use case yet. A far more clear use case is a headless editor which should allow building your own UI for the editor easily.

As @djanosik reported in :

This clearly shows that the division (repeated in other packages) is unsatisfactory for this case. The typing/enter/keystroke customisations should be separated from the UI part so you’re able to build a headless editor will all the editing features (except UI).

Now, should we move them to the engine part or will we break the potential server side support? It depends on what kind of APIs the engine part will need to use. If our keystroke handler, or engine’s view events, then it should be fine. This code would just be dead on the server side (we wouldn’t register ‘s observers in a potential ).

Widget Features

Since this particular feature will be reused over other widgets it has to be implemented with a reusable API. We can think of it as a widget feature that should not be limited to images only.

There are several candidates like that:

  • resize
  • drag and drop
  • upload
  • overlay

So it would make sense to create a common API called widget features, that would serve as a framework for such drop-in self-contained feature implementations.


An example API might look something like that:

import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
import { ResizeFeature } from '@ckeditor/ckeditor5-widget-feature-resize/src/resize';

// Simplest, hardcoded usage:

// (…)
return toWidget( viewElement, writer, {
		label labelCreator
	} );

// More realistic, configurable usage:
// (…)
let features = [];

if ( config.image.resize !== false ) {
	features.push( ResizeFeature( config.image.resize ) );

if ( config.image.dragAndDrop ) {
	features.push( DragAndDropFeature( config.image.dragAndDrop ) );

return toWidget( viewElement, writer, {
		label labelCreator,
		features features
	} );

Note this is just a concept, I haven’t yet verified it with any PoC.


The keystrokes (and other beyond-engine logic) is implemented in the Feature part which creates the problem that we discussed in the previous comments. It’s simple – this bit of functionality needs to be reused by headless editors, so the most common reason for feature split. Therefore, a bit different feature split needs to be proposed:

  • FeatureEditing
  • FeatureUI

We could go deeper and divide FeatureEditing into FeatureModel, FeatureViewController and FeatureKeystrokes and what else, but:

  • We don’t gain much. Perhaps the only more common case would be to replace the FeatureViewController layer (so to render the feature differently in the view) but:
    • this is a pretty big change and indicates a deep integration (and, hence, ability to maintain a customized code),
    • is possible (up to some limits) without replacing any code thanks to our event system and pluggable conversion.
  • The other use case would to run the editor in Node.js but this can be done with FeatureEditing anyway if a «deaf-mute» (one which doesn’t render anything to DOM and does not plug any observers).
  • We actually do that because commands are always separated and in more complex features converters are separated too. So, reimplementing a feature means gluing together a couple lines of code.

Problematic keystroke configuration

I’d also like to take this occasion to mention that we have more than one type of key-handlers:

  • Some keystrokes are handled on the FeatureViewController level – there, we define low-level, strictly editing oriented keystrokes such as Enter or Backspace. To implement them we listen directly on the view events (to be more precise, we use and ) which makes those listeners inconfigurable. We’ve been considering trying to reimplement these listeners using (which is meant to be configurable) but we didn’t find a way to do that in a reasonable way. The reason was that keys like Enter are handled by multiple features at the same time so there’s no single mapping. We’d need to introduce an intermediate, predefined layer which would allow doing and mappings. It’s seems like an overkill.
  • Other keystrokes are registered through and should be defined as mappings. Here we define things like assigning Ctrl+B to bold command. Those are meant to be reconfigurable (https://github.com/ckeditor/ckeditor5-core/issues/8).

This topic was discussed in (I think) . Back then it was a monster and I hoped that the time will clarify this for us, but it didn’t — it’s still convoluted. There’s some code which doesn’t match what I described above (listeners which aren’t configurable but which use – e.g. Tab handling).

However, we can deliberately overlook this for a little longer as it should be relatively simple to clean up in the future.


I propose to merge the ctx name into the string in order to keep the same params in every possible environment . If it was a separate param, then in the build version (in which we replace strings with ids) it would have to be left unused, or, we’d need to change the implementation of on the fly.

The CKE_LANG will be defined by the bundler. It will be the editor class that’s going to set it which means that it will only need to be set when bundling for the browser environment. Or we could go a bit further than that and define utils/global object which would retrieve the global scope depending on the env. In the browser that would be a window, in Node.js that would… something which is global there. That would allow this code to work without the bundler needing to preset this value.

PS. we already have , but I think that it makes sense to keep them separated.

this.config.define( 'lang', global.CKE_LANG || 'en' );

this.locale = new Locale( lang );

const t = this.locale.t;

// Usage:
t( 'OK' );
t( 'button' );
t( 'button ' );

Working with the ckeditor4 repostiory

Attention: The code in this repository should be used locally and for development purposes only. We do not recommend using it in a production environment because the user experience will be very limited.

Code installation

There is no special installation procedure to install the development code.
Simply clone it to any local directory and you are set.

Available branches

This repository contains the following branches:

  • – Development of the upcoming minor release.
  • – Development of the upcoming major release.
  • – Latest stable release tag point (non-beta).
  • – Latest release tag point (including betas).
  • (e.g. , ) – Release freeze, tests and tagging. Hotfixing.

Note that both and are under heavy development. Their code did not pass the release testing phase, though, so it may be unstable.

Additionally, all releases have their respective tags in the following form: , , etc.

Code structure

The development code contains the following main elements:

  • Main coding folders:
    • – The core API of CKEditor 4. Alone, it does nothing, but it provides the entire JavaScript API that makes the magic happen.
    • – Contains most of the plugins maintained by the CKEditor 4 core team.
    • – Contains the official default skin of CKEditor 4.
    • – Contains some developer tools.
    • – Contains the CKEditor 4 tests suite.

Building a release

A release-optimized version of the development code can be easily created locally. The script can be used for that purpose:

A «release-ready» working copy of your development code will be built in the new folder. An Internet connection is necessary to run the builder, for the first time at least.


Contexts for used messages.

Examples: https://github.com/ckeditor/ckeditor-dev/tree/master/dev/langtool/meta

	"OK" "Label for OK button in a dialog."
	"button" "Name of a clickable form control, e.g. 'OK button'."
	"button " "Button as used in clothes."

The button is first defined in the package without a context, because, e.g. historically, we could’ve used it there without a context (cause, in our case, button is a UI button most of the time). Then, while working on the CKEditor 5 Tailor plugin we realised that button is already used, but in a different context, so we can’t use as it will point to a wrong context definition (contexts are global for all packages). Instead, we’ll use and add its own definition in the .

Image Resize

First off, not only image requires resizing, there are few candidates that might use it:

  • image
  • media embed
  • table

Handlers (must-have)

  • multiple resize handlers
    • Google docs
    • Firefox/IE native contenteditable implementation
    • MS Word
    • TinyMCE
    • Froala
  • single handler
    • CKE4:
    • Prose mirror 3rd party contrib:
    • Redactor

Based on the trends we should start with multiple resize handlers. There are a few pros for that:

  • people are more familiar with it,
  • we don’t have to worry about bidi,
  • aspect ratio issue (see «Aspect ratio» section) is solved

The main issue is that it clutters the UI once the resized entity gets hovered/focused.

Handlers position

Often times we’re using captioned image. In that case it sounds only fair to bound handlers to the wrapper.

Aspect ratio

Should aspect ratio be locked? Should interface allow to lock unlock aspect ratio? Or maybe should it only be doable with keyboard modifier, e.g. holding a shift/alt or not.

Having multiple resize handlers, we can skip the feature and force corner handlers to keep the aspect ratio, while others will resize only along a single axis.


Any feature of the editor should be accessible, this includes resizing too.

As an example for shapes (this does not include image) MS Word support . It supports for rotating images.

Accessibly doesn’t necessairly needs be implemented as handling resizing using arrow. If we’d have some sort of accessible dialog that would allow for setting the size, that would be fine too.

Ссылка на основную публикацию