Contribute to this guide

guide本地化

# 简介

所有 CKEditor 5 WYSIWYG 编辑器功能都支持消息本地化。这意味着任何功能的用户界面都可以根据用户的偏好翻译成各种语言和地区。

CKEditor 5 翻译系统对第三方插件开放。您可以引入的任何自定义功能都可以本地化。该系统还提供了一种方法来添加缺失的翻译或覆盖现有的翻译,并支持翻译复数形式。

确保使用最新的 CKEditor 5 开发工具包。v19.0.1 之前的工具版本不支持本指南中描述的功能。

# 开放翻译 API

CKEditor 5 本地化系统重点关注以下方面

  • 支持第三方插件的本地化。
  • 使传递您自己的翻译成为可能,以修复缺失或无效的本地化。
  • 生成确定性构建。
  • 公开易于使用的 API 用于提供翻译和编写可本地化的内容。
  • 在本地化系统的每个步骤中支持复数形式,以获得更好的翻译。

# 术语表

在开始之前,让我们解释一下对翻译过程至关重要的术语的含义

  • 消息 – 应该翻译的字符串或对象。
    字符串版本作为 { id: message, string: message } 对象形式的快捷方式。
  • 消息 ID – 用于区分消息的属性。
    它对于可能发生冲突的简短消息很有用,例如 %0 images
  • 消息字符串 – 消息的默认(英语)形式。
    当消息支持复数版本时,这是默认的单数版本。
  • 消息复数 – 消息的可选复数(英语)版本。
    此属性的存在表明消息应同时支持单数和复数形式。
  • PO 文件 (.po) – 包含语言翻译的文件,采用 PO 格式.
    所有可本地化的 CKEditor 5 包都在 lang/translations/ 目录中包含此类文件。
  • POT 文件 (.pot) – 包含将要翻译的源消息(英文句子)的文件。
  • 翻译资源 – JavaScript 文件或包含为一种语言生成的翻译的文件的一部分。

# 编写可本地化的 UI

所有需要本地化的消息都应该传递给 CKEditor 5 的特殊 t() 函数。此函数可以从编辑器的 Locale 实例中检索:const { t } = editor.locale; 或从任何视图方法中检索:const t = this.t;

作为第一个参数,t() 函数接受字符串字面量或包含 idstringplural(可选)属性的对象字面量。字符串字面量将同时用作消息 ID消息字符串

作为第二个参数,翻译函数接受一个值或一个值的数组。这些值将用于填充更高级翻译场景中的占位符。如果指定了 plural 属性,第一个值将用作确定复数形式的数量。

由于在翻译过程中使用静态代码分析器,您必须使用名为 t() 的函数。它不应在 Locale 实例上调用,并且不能具有不同的名称。代码分析器仅在 t() 函数调用中查找可本地化的消息

出于同样的原因,第一个参数只能是字符串字面量或对象字面量。不能传递变量。

使用 t() 函数时,您可以创建您自己的可本地化的消息或重用项目依赖的 CKEditor 5 包中创建的消息。在重用消息的情况下,您无需担心翻译它们,因为所有工作都将由 CKEditor 5 团队完成。显然,您在翻译方面的帮助仍然会受到赞赏!

对于简单的可本地化的消息,出于简单性考虑,使用字符串形式

const emojiName = 'cat';

// Assuming that the English language was picked:
t( 'insert emoji' ); // "insert emoji"
t( 'insert %0 emoji', emojiName ); // "insert cat emoji"
t( 'insert %0 emoji', [ emojiName ] ); // "insert cat emoji"

对于更高级的场景,使用纯对象形式

const quantity = 3;

// Assuming that the English language was picked:
t( { string: '%0 emoji', id: 'ACTION_EMOJI' }, 'insert' ); // "insert emoji"
t( { string: '%0 emoji', plural: '%0 emojis', id: 'N_EMOJIS' }, quantity ); // "3 emojis"
t( { string: '%1 %0 emoji', plural: '%1 %0 emojis', id: 'ACTION_N_EMOJIS' }, [ quantity, 'Insert' ] ); // "Insert 3 emojis"

# 示例:本地化插件 UI

此示例演示如何创建插件的可本地化用户界面。以下是如何创建插入微笑表情符号的按钮。该按钮将具有可本地化的工具提示。

// Custom plugin configuration, including necessary imports.
// The code below should be put into a custom plugin class extending the Plugin class.
// ...

editor.ui.componentFactory.add( 'smilingFaceEmoji', locale => {
    const buttonView = new ButtonView( locale );

    // The translation function.
    const { t } = editor.locale;

    // The localized label.
    const label = t( 'Insert smiling face emoji' );

    buttonView.set( {
        label,
        icon: emojiIcon,
        tooltip: true
    } );

    buttonView.on( 'execute', () => {
        editor.execute( 'insertSmilingFaceEmoji' );
        editor.editing.view.focus();
    } );
} );

// The rest of the custom plugin configuration.
// ...

请参阅 如何创建完整的插件,以便更好地了解如何创建 CKEditor 5 插件。

# 示例:本地化挂起的操作

挂起的操作 用于告知用户某个操作正在进行,并且如果他们在当前时刻退出编辑器,他们将丢失数据。以下是本地化它们的方法

class FileRepository {
    // More methods.
    // ...

    updatePendingAction() {
        const pendingActions = this.editor.plugins.get( PendingActions );

        const t = this.editor.t;
        const getMessage = value => t( 'Upload in progress (%0%).', value ); // Upload in progress (12%).

        this._pendingAction = pendingActions.add( getMessage( this.uploadedPercent ) );
        this._pendingAction.bind( 'message' ).to( this, 'uploadedPercent', getMessage );
    }
}

# 添加翻译和本地化编辑器 UI

首先,如果您在任何 CKEditor 5 功能中发现了缺失的翻译或错误的翻译,请查看如何为项目做出贡献。CKEditor 5 是一个开源项目,供世界各地的人们使用,因此您的帮助将受到其他人的赞赏。

可以通过三种方式将翻译添加到编辑器,以满足各种需求。

# 使用 add() 函数

添加翻译的第一个选项是通过 翻译服务的 add() 助手。此实用程序通过扩展全局 window.CKEDITOR_TRANSLATIONS 对象来添加翻译。由于它需要导入,因此它仅在构建编辑器之前起作用。

从 CKEditor 5 v19.0.0 版本开始,add() 方法现在接受一个可选的 getPluralForm() 函数作为第三个参数。此函数仅在为特定语言没有加载语言文件时才需要定义复数形式。它还接受针对消息的翻译数组,如果该消息应支持单数和复数形式。

add( 'pl', {
    'Add space': [ 'Dodaj spację', 'Dodaj %0 spacje', 'Dodaj %0 spacji' ]
} );

// Assuming that the Polish language was picked:
t( { string: 'Add space', plural: 'Add %0 spaces' }, 1 ) // "Dodaj spację"
t( { string: 'Add space', plural: 'Add %0 spaces' }, 2 ) // "Dodaj 2 spacje"
t( { string: 'Add space', plural: 'Add %0 spaces' }, 5 ) // "Dodaj 5 spacji"

# 使用 window.CKEDITOR_TRANSLATIONS 对象

第二个选项是通过全局 window.CKEDITOR_TRANSLATIONS 对象添加翻译。

对于应该支持的每种语言,此对象的 dictionary 属性都应该扩展,并且如果缺少 getPluralForm() 函数,则应提供该函数。

dictionary 属性是一个 消息 ID ⇒ 翻译 映射,其中 翻译 可以是字符串,也可以是包含给定语言复数形式的翻译数组,如果消息也应该支持复数形式。

getPluralForm() 属性应该是一个函数,用于返回给定数量的复数形式索引。请注意,在使用 CKEditor 5 翻译时,此属性将由CKEditor 5 翻译资源定义。

以下示例演示了 window.CKEDITOR_TRANSLATIONS 对象的一部分,其中包含波兰语翻译,用于 CancelAdd space 消息 ID

{
    // Each key should be a valid language code.
    pl: {
        // A map of translations for the 'pl' language.
        dictionary: {
            'Cancel': 'Anuluj',
            'Add space': [ 'Dodaj spację', 'Dodaj %0 spacje', 'Dodaj %0 spacji' ]
        },

        // A function that returns the plural form index for the given language.
        // Note that you only need to pass this function when you add translations for a new language.
        getPluralForm: n => n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && ( n % 100 < 10 || n % 100 >= 20 ) ? 1 : 2
    }
    // Other languages.
    // ...
}

重要的是扩展 window.CKEDITOR_TRANSLATIONS 对象中的现有属性,以避免丢失其他翻译。这可以使用 Object.assign()|| 运算符轻松实现。

// Make sure that the global object is defined. If not, define it.
window.CKEDITOR_TRANSLATIONS = window.CKEDITOR_TRANSLATIONS || {};

// Make sure that the dictionary for Polish translations exist.
window.CKEDITOR_TRANSLATIONS[ 'pl' ] = window.CKEDITOR_TRANSLATIONS[ 'pl' ] || {};
window.CKEDITOR_TRANSLATIONS[ 'pl' ].dictionary =  window.CKEDITOR_TRANSLATIONS[ 'pl' ].dictionary || {};

// Extend the dictionary for Polish translations with your translations:
Object.assign( window.CKEDITOR_TRANSLATIONS[ 'pl' ].dictionary, {
    'Save': 'Zapisz'
} );

如果您添加了新的语言,请记住设置 getPluralForm() 函数,该函数应返回一个数字(或对于像英语这样的简单复数规则的语言,返回一个布尔值),以指示应使用哪种形式来表示给定的值。

# 创建 .po 文件

第三种添加插件的选项最适合拥有包含许多可本地化消息的插件的插件所有者。使用此选项,您需要在 lang/translations/ 目录中为每种语言代码创建一个 .po 文件,其中包含遵循 PO 文件格式 的翻译。

# lang/translations/es.po

msgid ""
msgstr ""
"Language: es\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "Align left"
msgstr "Alinear a la izquierda"

请注意,默认情况下,CKEditor 5 翻译插件 配置为仅解析 CKEditor 5 源代码,以便查找可本地化消息并生成翻译资源

如果您在 CKEditor 5 生态系统之外开发自己的插件,并通过创建PO 文件对其进行本地化,则应覆盖 sourceFilesPatternpackageNamePattern 选项,以允许 CKEditor 5 webpack 插件分析代码并查找包含相应翻译的消息。您还应该在软件包 README 中提及这些 webpack 插件更改,以使其他用户能够正确构建包含您的插件的本地化 CKEditor 5 编辑器。当本地化功能变得越来越流行时,这个问题可能会在将来得到简化。

要构建和配置本地化编辑器,请按照 设置 UI 语言指南 中的步骤进行操作。

# 重用其他软件包中的翻译

如果您想重用另一个软件包中已存在的消息,您应该对 Locale 实例使用 t() 方法,并使用更改后的名称,而不是 使用 t() 作为函数。这是因为对 Locale 使用 t() 作为方法不会被静态代码分析器处理。因此,它允许使用在其他软件包中已翻译的消息

我们已经在 协作功能斜杠命令功能 中使用了这种方法。您可以在下面找到我们用于斜杠命令功能的 默认命令列表 中的一个示例。请注意使用 t()translateVariableKey() 之间的区别。translateVariableKey( 'Block quote' ) 将重用来自另一个软件包的翻译,而 t( 'Create a block quote' ) 将由静态代码分析器处理。因此,我们确保 title 的翻译来自 块引用 功能,其中消息“Block quote”已翻译。但对于 description,我们创建了一个新的翻译。

public getDefaultCommands() {
    const t = this.editor.t;
    const translateVariableKey = this.editor.locale.t;

    return [
        {
            id: 'blockQuote',
            commandName: 'blockQuote',
            icon: icons.quote,
            title: translateVariableKey( 'Block quote' ),
            description: t( 'Create a block quote' )
        },
        // More command definitions
        // ...
    ]
}

# 已知限制

  • 目前,无法在不破坏编辑器的情况下在运行时更改所选编辑器的语言。
  • 目前,无法使用 CKEditor 5 webpack 插件将多种语言添加到捆绑包中。如果应该将多个翻译资源添加到应用程序中,则应使用 <script> 标签或导入到生成的翻译资源中。