Fluent — гид по синтаксису

Базовая документация по синтаксису Fluent — новой спецификации для локализационных файлов, представленной Mozilla. Это фактически «язык программирования для переводчиков», который даёт большую свободу по оптимизации текстов на стороне локализатора (чтобы, например, решить проблему с родом и числом). Коротко о преимуществах и особенностях системы можно почитать в статье на Habr. Далее пойдёт перевод официальной документации для переводчиков, который я буду обновлять по мере необходимости. Техническую документацию можно найти на официальном сайте Fluent.

  1. Введение
  2. Hello, World!
  3. Написание текста
    1. Объекты
    2. Специальные символы
    3. Многострочный текст
  4. Переменные
  5. Ссылки на сообщения
  6. Селекторы
  7. Атрибуты
  8. Термины
  9. Комментарии
  10. Встроенные функции

Введение

Fluent — это локализационная парадигма, созданная для помощи переводчикам использовать все выразительные средства их языка без искусственных ограничений. Формат для описания переводческих ресурсов, который использует Fluent, называется FTL.

FTL создан быть простым для понимания, в то же время позволяя реализовывать сложные концепции в разных языках, например разные формы для слов, имеющих род, формы множественных чисел, формы спряжений и прочее.

Этот документ продемонстрирует, как использовать FTL для решения локализационных задач. В каждой главе содержится практический пример концепций FTL.

Hello, world!

В языке Fluent основная единица перевода называется сообщением. Сообщения — это контейнеры для информации. Сообщения используются для определения, хранения и вызова текстов в продукте. В самом простом случае сообщение выглядит так:

hello = Hello, world!

У каждого сообщения есть идентификатор, который позволяет разработчику привязывать его к конкретному месту в программе, где оно будет использоваться. Идентификатор сообщения из примера выше — hello.

В самом простом случае, сообщение обладает лишь одним текстовым значением. В примере выше это Hello, world!. Значение начинается с первого непустого символа после знака =, однако существуют редкие исключения в многострочных значениях. Следующая глава (Написание текста) разбирает это подробнее.

Большинство сообщений во Fluent выглядят примерно так же. Fluent старается не усложнять простые вещи. Но иногда сообщения могут выглядеть сложнее. Это руководство научит вас, как адаптировать сообщение к грамматическим особенностям языка и к локализационным требованиям конкретного продукта.

В следующей главе вы научитесь читать и писать во Fluent!

Написание текста

Fluent — это формат файлов, созданный для хранения переводов. Следовательно, текст — это самая важная часть любого файла Fluent. Все значения — сообщения, термины, варианты, атрибуты — хранятся в виде текста. Во Fluent, текстовые сообщения называются шаблонами.

Объекты

Текст может интерполировать значения из других сообщений, а так же внешние данные, переданные в перевод из приложения. Используйте фигурные скобки, чтобы открыть и закрыть выражение интерполирования внутри шаблона:

# $title (String) - Заголовок закладки для удаления.

remove-bookmark = Вы уверены, что хотите удалить { $title }?

Текст во Fluent может использовать специальный синтаксис для включения небольших частей программируемого интерфейса. Эти части заключаются в фигурные скобки { и } , и называются объектами.

Распространено использование объектов для интерполяции внешних переменные в перевод. Значения переменных предоставляются разработчиком, и они будут заданы во время выполнения. Они также могут динамически меняться, когда пользователь использует локализованный продукт.

# $title (String) - Заголовок закладки для удаления. remove-bookmark = Вы уверены, что хотите удалить { $title }?

Кроме того, возможно интерполировать сообщения и термины внутри текстовых значений.

-brand-name = Firefox installing = Установка { -brand-name }.

Наконец, объекты можно использовать для вставки специальных символов в текстовые значения. Например, поскольку объекты используют фигурные скобки { и } в качестве разделителя, вставка фигурной скобки в перевод потребует особой осторожности. Для этого подойдёт использование цитированного текста:

opening-brace = В этом сообщении есть открывающая фигурная скобка: {"{"}.
closing-brace = В этом сообщении есть закрывающая фигурная скобка: {"}"}.

Специальные символы

Текст является наиболее важной частью файла Fluent. Он не имеет специального синтаксиса: вы можете просто написать его без разделителей (например, кавычек) и использовать символы из всех плоскостей Юникода. Обычного текста должно быть достаточно для хранения подавляющего большинства переводов. В редких случаях, когда это не так, можно использовать другой тип текста: цитируемый текст.

Текст в кавычках

Текст в кавычках использует двойные кавычки в качестве разделителей и не может содержать разрывы строк. Как и другие типы выражений, он может использоваться внутри помещаемых объектов (например, { «абвгд»}). Это редко требуется, но может использоваться для вставки символов, которые иначе считаются специальными в данном контексте. Например, поскольку объекты используют фигурные скобки { и } в качестве разделителя, вставка фигурной скобки в перевод потребует особой осторожности. Для этого подойдёт использование текста в кавычках:

opening-brace = В этом сообщении есть открывающая фигурная скобка: {"{"}. closing-brace = В этом сообщении есть закрывающая фигурная скобка: {"}"}.

В примере выше синтаксис {«{«} может быть интерпретирован как текст в кавычках «{«, интерполированны в обычный текст через синтаксис объекта: {…}. Как видно из этого примера, фигурные скобки не имеют особого значения в цитируемом тексте. В результате цитируемый текст не может содержать никаких интерполяций.

Та же стратегия, что и выше, может быть использована для сохранения пустого пространства в переводах:

blank-is-removed = Это сообщение начинается без пробелов.

blank-is-preserved = {" "}Это сообщение начинается с четырёх пробелов.

В очень редких случаях вам может понадобиться использовать цитируемый текст, чтобы использовать буквальную точку ( . ), звёздочку ( * ) или квадратную скобку ( ) когда они используются в качестве первого символа в новой строке. В противном случае они начнут новый атрибут или новый вариант.

leading-bracket =
⠀⠀⠀⠀⠀⠀⠀⠀Это сообщение имеет открывающую квадратную скобку
⠀⠀⠀⠀⠀⠀⠀⠀в начале третьей строки:
⠀⠀⠀⠀⠀⠀⠀⠀{"["}.

attribute-how-to =
⠀⠀⠀⠀⠀⠀⠀⠀Чтобы добавить атрибут к этим сообщениям, напишите
⠀⠀⠀⠀⠀⠀⠀⠀{".attr = Value"} на новой строке.
⠀⠀⠀⠀⠀⠀⠀⠀.attr = Фактический атрибут (а не часть значения текста выше)

Экранированные последовательности

Текст в кавычках поддерживает небольшое количество экранированных последовательностей. Символ обратной косой черты (\) запускает экранированную последовательность в тексте в кавычках. В обычном тексте обратная косая черта — это буквальный символ без особого значения.
\"⠀⠀⠀⠀⠀⠀⠀⠀Буквальная двойная кавычка.
\uHHHH⠀⠀⠀⠀⠀⠀⠀⠀Символ Юникода в диапазоне U+0000 до U+FFFF.
\UHHHHHH⠀⠀⠀⠀⠀⠀⠀⠀Любой символ Юникода.
\\⠀⠀⠀⠀⠀⠀⠀⠀Буквальная обратная косая черта.

Экранированные последовательности нужны редко, но Fluent поддерживает их ради кранийх случаев. В реальной жизни использование реального символа в обычном тексте должно быть предпочтительным.

# Это будет работать, но сложно для восприятия.
literal-quote1 = Текст в {"\""}двойных кавычках{"\""}.

# Это предпочтительнее. Просто используйте символ двойной кавычки.
literal-quote2 = Текст в "двойных кавычках".

Текст в кавычках следует использовать с осторожностью, чаще всего в сценариях, в которых требуется специальный символ, или при добавлении символов между и «}, чтобы их было легче заметить. Например, символ неразрывного пробела в большинстве текстовых редакторов выглядит как обычный пробел, и при переводе его легко пропустить. Экранирующая последовательность Unicode внутри цитируемого текста может использоваться, чтобы выделить его:

privacy-label = Политика{"\u00A0"}конфиденциальности

Подобный подход прояснит, какой символ тире используется в следующем примере:

# Символ тире - EM DASH, но в зависимости от шрифта
# может выглядеть как EN DASH
which-dash1 = Это тире—или нет?

# Использование экранирующей последовательности Юникод проясняет это.
which-dash2 = Это тире{"\u2014"}или нет?

Любой символ Юникода может использоваться в обычных текстовых значениях и в цитируемом тексте. Если удобочитаемость не требует использования экранирующей последовательности, используйте фактический символ Юникода.

# Это будет работать, но другие переводчики могут считать
# такую запись запутанной.
tears-of-joy1 = {"\U01F602"}

# Это наиболее предпочтительный вариант. Вы можете сразу увидеть, какой символ Юникода
# здесь используется.
tears-of-joy2 = 😂

Многострочный текст

Текст может занимать несколько строчек. В таких случаях Fluent вычислит общий отступ всех строчек с отступом и удалит его из окончательного текстового значения.

multi = Текст может занимать несколько строчек, пока каждая новая строчка обладает отступом хотя бы в один пробел. Поскольку все строчки в сообщении обладают одинаковым отступом, все отступы удаляются из финального значения.

indents = Отступы, одинаковые для всех строчек, удаляются из финального текстового значения. Эта строчка обладает двумя пробелами перед началом.

Для отступа можно использовать только символ пробела (U+0020). Fluent воспринимает знак табуляции как обычный текст.

single = Текст можно записать в одну строчку.

multi = Текст может занимать несколько строчек,
⠀⠀⠀⠀если новые строки имеют
⠀⠀⠀⠀отступ хотя бы в один пробел.

block =
⠀⠀⠀⠀Иногда многострочный текст
⠀⠀⠀⠀лучше форматировать блоками "block",
⠀⠀⠀⠀что означает начало с новой строчки. Все
⠀⠀⠀⠀строчки должны иметь отступ хотя бы в один пробел.

Почти во всех случаях шаблоны начинаются с первого непустого символа и заканчиваются последним непустым символом. Другими словами, начальные и конечные пробелы игнорируются. Из этого правила есть одно исключение, в силу другого правила, которое мы рассмотрим ниже пример multiline2).

leading-spaces = ⠀⠀⠀⠀Значение этого сообщения начнётся со слова "Значение".
leading-lines =
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀Значение этого сообщения начнётся со слова "Значение".
⠀⠀⠀⠀Пустые строчки под идентификатором игнорируются.

Разрывы и пустые строки сохраняются до тех пор, пока они располагаются внутри многострочного текста, то есть до и после них есть текст.

blank-lines =
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀Пустая строка над этой строкой игнорируется.
⠀⠀⠀⠀Это вторая строка значения.
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀Пустая строка над этой строкой сохраняется.

В многострочных шаблонах все общие отступы удаляются, когда текстовое значение распространяется на несколько строк с отступом.

multiline1 =
⠀⠀⠀⠀Это сообщение имеет 4 пробела для отступа
⠀⠀⠀⠀⠀⠀⠀⠀на второй строчке значения.

Мы можем визуализировать это поведение следующим образом и будем использовать его до конца главы:

# █ обозначает отступ, общий для всех строчек (удалённый из значения).
# · означает отступ, сохранённый в финальном значении.
multiline1 =
████Это сообщение имеет 4 пробела для отступа
████····на второй строчке значения.

Это поведение также применяется, когда первая строка текстового блока имеет отступ относительно следующих строк. Это единственный случай, когда последовательность начальных пустых символов может быть сохранена как часть текстового значения, даже если они технически находятся в начальной позиции.

multiline2 =
████··Это сообщение имеет 2 пробела для отступа
████на первой строчке значения. Первые 4 пробела для отступа
████удаляются из всех строчек.

В этом поведении участвуют только строки с отступом, составляющие многострочный шаблон. В частности, если текст начинается в той же строке, что и идентификатор сообщения, то эта первая строка не считается отступом и исключается из поведения отступа. В таких случаях первая строчка (без отступа) по-прежнему игнорирует начальные пробелы, поскольку шаблоны начинаются с первого непустого символа.

multiline3 = Это сообщение имеет 4 пробела для отступа
████····на второй строчке значения. Первая строчка
████не считается отступной.
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
# То же значение, что и в multiline3.
multiline4 =⠀⠀⠀⠀Это сообщение имеет 4 пробела для отступа
████····на второй строчке значения. Первая строчка
████не считается отступной.

Обратите внимание, что если многострочный шаблон начинается с той же строки, что и идентификатор, и состоит только из еще одной строки текста под ним, тогда отступ, общий для всех строчке с отступом будет равен отступу второй строчки, т.е. только отступной строчке. В этой ситуации все отступы удаляются.

multiline5 = У этого сообщения не будет отступов
████████на второй строчке значения.

Переменные

Переменные — это фрагменты данных, полученные из приложения. Они предоставляются разработчиком приложения и могут быть включены в перевод вместе с объектами. Переменные могут меняться динамически, в процессе использования продукта пользователем.

На переменные ссылаются с помощью синтаксиса $variable-name:

welcome = Здравствуй, { $user }!
unread-emails = У пользователя { $user } есть { $email-count } непрочитанных сообщений.

Например, если текущего пользователя зовут Мария, и у неё 5 непрочитанных сообщений, то перевод будет выглядеть так:

Здравствуй, Мария!
У пользователя Мария есть 5 непрочитанных сообщений.

Существует множество видов внешних данных, которые могут быть полезными при локализации: имя пользователя, количество непрочитанных сообщений, уровень заряда батареи, текущее время, время до будильника, и так далее. Fluent предлагает ряд функций, которые упрощают работу с переменными.

Варианты и селекторы

В некоторых языках использование числа в середине переведенного предложения потребует правильных множественных форм слов, связанных с числом. Fluent позволяет определить несколько вариантов перевода в зависимости от категории множественного числа.

Неявное форматирование

Цифры и даты форматируются автоматически по правилам вашего языка. Рассмотрим следующий перевод:

# $duration (Number) - Продолжительность в секундах.
time-elapsed = Time elapsed: { $duration }s.

Для переменной $duration со значением 12345.678, сообщение будет отображаться в продукте с учётом того, что выбран британский или американский вариант английского. Обратите внимание на запятую, которая используется в качестве разделителя тысяч.

Time elapsed: 12,345.678s.

А в канадском английском отображение произойдёт так:

Time elapsed: 12 345.678s.

Явное форматирование

В некоторых случаях локализатору может потребоваться больший контроль над форматированием чисел или дат. Fluent обладает встроенным функционалом для этих целей.

# $duration (Number) - Продолжительность в секундах.
time-elapsed = Time elapsed: { NUMBER($duration, maximumFractionDigits: 0) }s.

Для одного и того же значения переменной $duration, результат в британском и американском английском будет выглядеть так:

Time elapsed: 12,345s.

Ссылки на сообщения

Другой вариант использования для объектов — ссылка на одно сообщение в другом.

menu-save = Сохранить
help-menu-save = Нажмите { menu-save } чтобы сохранить файл.

Ссылки на другие сообщения обычно помогают поддерживать согласованность определенных переводов в интерфейсе и упрощают обслуживание.

Это также особенно удобно для отделения фирменного стиля от остальных переводов, чтобы его можно было легко изменить при необходимости, например, в процессе сборки приложения. Для этого сценария лучше всего подходит определение термина с помощью дефиса -, например -brand-name в примере ниже.

-brand-name = Firefox
installing = Установка { -brand-name }.

Использование термина здесь указывает инструментам и среде выполнения локализации, что -brand-name не предполагается использовать непосредственно в продукте, но на него следует ссылаться в других сообщениях.

Селекторы

emails =
⠀⠀⠀⠀{ $unreadEmails ->
⠀⠀⠀⠀⠀⠀⠀⠀[one] У вас одно непрочтитанное письмо.
⠀⠀⠀⠀⠀⠀⠀⠀*[other] У вас { $unreadEmails } непрочитанных писем.
⠀⠀⠀⠀}

Один из наиболее распространенных случаев, когда локализатору необходимо использовать объект, это когда существует несколько вариантов строки, которые зависят от некоторой внешней переменной. В приведённом выше примере сообщение emails зависит от значения переменной $unreadEmails.

FTL обладает выражением select, которое позволяет определять несколько вариантов перевода и выбирать один из них в зависимости от значения селектора. Индикатор * обозначает вариант по умолчанию. Вариант по умолчанию нужен всегда.

Селектор может быть строкой, в этом случае он будет сравниваться непосредственно с ключами вариантов, определённых в выражении выбора. Для селекторов, которые являются числами, ключи вариантов должны либо точно совпадать с номером, либо соответствовать категории множественных чисел CLDR. Возможные категории: zero, one, two, few, many и other. К примеру, в английском есть две множественные категории: one и other.

Если для перевода требуется, чтобы число форматировалось определённым не по умолчанию способом, селектор должен использовать те же параметры форматирования. Отформатированный номер затем будет использоваться для выбора правильной категории множественного числа CLDR, которая для некоторых языков может отличаться от категории неформатированного номера:

your-score =
⠀⠀⠀⠀{ NUMBER($score, minimumFractionDigits: 1) ->
⠀⠀⠀⠀⠀⠀⠀⠀[0.0] Вы набрали ноль очков. Что случилось?
⠀⠀⠀⠀⠀⠀⠀⠀*[many] Вы набрали { NUMBER($score, minimumFractionDigits: 1) } очков.
⠀⠀⠀⠀}

Атрибуты

login-input = Предопределённое значение
⠀⠀⠀⠀.placeholder = [email protected]
⠀⠀⠀⠀.aria-label = Значение для входа
⠀⠀⠀⠀.title = Введите адрес электронной почты для входа

Элементы пользовательского интерфейса часто содержат несколько переводимых сообщений на один виджет. К примеру, форма ввода в HTML может иметь и значение, и атрибуты placeholder, aria-label, и даже title.

Другой пример — окно подтверждения Web Component с кнопками OK, Отмена и сообщением.

Чтобы избежать необходимости определять несколько отдельных сообщений для представления разных строк в одном элементе, FTL позволяет добавлять атрибуты к сообщениям.

Эта функция особенно полезна при переводе более сложных виджетов, поскольку благодаря тому, что все атрибуты хранятся в одном блоке, упрощается работа и идентификация сообщений с точки зрения редакторов, комментариев и инструментов.

Атрибуты можно так же использовать для определения грамматических свойств терминов. Атрибуты терминов являются закрытыми и не могут быть получены средой выполнения локализации. Они могут быть использованы только в качестве селекторов.

Термины

Термины похожи на обычные сообщения, но их можно использовать только как ссылки в других сообщениях. Их идентификаторы начинаются с одного дефиса — как в примере выше: -brand-name. Среда выполнения не может получить термины напрямую. Их лучше всего использовать для определения словарного запаса и словаря, которые можно последовательно использовать при локализации всего продукта.

-brand-name = Firefox
⠀⠀⠀⠀
about = About { -brand-name }.
update-successful = { -brand-name } обновлён.

Параметризованные термины

Значения терминов следуют тем же правилам, что и значения сообщений. Они могут быть и простым текстом, и интерполировать другие выражения, включая переменные. Однако, хотя сообщения получают данные для переменных непосредственно из приложения, термины получают такие данные из сообщений, в которых они используются. Такие ссылки принимают форму -term(…), где переменные, доступные внутри термина, определены в скобках, например -term(param: «value»).

# Надуманный пример, чтобы продемонстрировать, как переменные
# могут быть переданы в термины.
-https = https://{ $host }
visit = Посетите { -https(host: "example.com") } для получения дополнительной информации.

Приведенный выше пример не очень полезен, и в этом конкретном случае гораздо лучше использовать полный адрес непосредственно сообщении visit. Однако есть еще один вариант использования, который в полной мере использует эту функцию. Передавая переменные в термин, вы можете определить выражение select с несколькими вариантами одного и того же значения термина.

-brand-name =
⠀⠀⠀⠀{ $case ->
⠀⠀⠀⠀⠀⠀⠀⠀*[nominative] Firefox
⠀⠀⠀⠀⠀⠀⠀⠀[locative] Firefoxa
⠀⠀⠀⠀}
⠀⠀⠀⠀
# "About Firefox."
about = Informacje o { -brand-name(case: "locative") }.

Этот шаблон может быть полезен для определения аспектов термина, которые могут быть связаны с грамматической или стилистической особенностью языка. Во многих флективных языках (немецком, финском, венгерском, и славянских языках), предлог о (об) определяет падеж дополнения. Это может быть винительный падеж (немецкий), абляционный падеж (латинский) или локатив (славянские языки). Грамматические падежи могут быть определены как варианты одной и той же темы и упоминаться посредством параметризации из других сообщений. Вот что происходит с сообщением about из примера выше.

Если в термин не переданы никакие параметры или если на термин ссылаются без скобок, будет использован вариант по умолчанию.

-brand-name =
⠀⠀⠀⠀{ $case ->
⠀⠀⠀⠀⠀⠀⠀⠀*[nominative] Firefox
⠀⠀⠀⠀⠀⠀⠀⠀[locative] Firefoxa
⠀⠀⠀⠀}
⠀⠀⠀⠀
# "Firefox has been successfully updated."
update-successful = { -brand-name } został pomyślnie zaktualizowany.

Термины и атрибуты

Иногда переводы могут отличаться в зависимости от некоторых грамматических особенностей термина в них. Термины могут хранить эту грамматическую информацию о себе в атрибутах. В приведённом ниже примере форма прошедшего времени has been updated зависит от грамматического рода -brand-name.

-brand-name = Aurora
⠀⠀⠀⠀.gender = feminine
⠀⠀⠀⠀
update-successful =
⠀⠀⠀⠀{ -brand-name.gender ->
⠀⠀⠀⠀⠀⠀⠀⠀[masculine] { -brand-name} został zaktualizowany.
⠀⠀⠀⠀⠀⠀⠀⠀[feminine] { -brand-name } została zaktualizowana.
⠀⠀⠀⠀⠀⠀⠀⠀*[other] Program { -brand-name } został zaktualizowany.
⠀⠀⠀⠀}

Используйте атрибуты для описания грамматических черт и свойств. Род, одушевлённость, начинается ли предложение с гласной, и так далее. Атрибуты терминов являются закрытыми и не могут быть получены средой выполнения локализации. Они могут быть использованы только как селекторы. При необходимости, они могут быть параметризированы с помощью синтаксиса -term.attr(param: «value»).

Комментарии

Комментарии в Fluent начинаются с символов #, ## или ###, и могут использоваться для документирования сообщений и определения структуры файла.

Комментарий с одним знаком решётки (#) может быть как самостоятельным, так и привязанным к сообщениям. Если комментарий располагается справа от сообщения, он считается его частью, и локализационные инструменты будут отображать его вместе с сообщением. В противном случае комментарий является независимым (что полезно для комментирования частей файла).

Комментарий с двойным знаком решётки (##) всегда является независимым. Такой вид комментариев позволяет разделять файл на небольшие группы сообщений, связанных друг с другом. Это комментарий уровня группы. Можете воспринимать их как заголовки с описанием. Групповые комментарии предназначены быть подсказкой для локализаторов и переводческих инструментов, объясняя структуру файла. Одна группа заканчивается там, где начинается новая группа, либо с окончанием файла.

Комментарии с тремя знаками решётки (###) всегда являются независимыми. Они могут использоваться для предоставления информации о цели или контексте всего файла.

# Эта форма исходного кода регулируется условиями Mozilla Public
# License, v. 2.0. Если копия MPL не была распространена с этим файлом,
# ее можно получить по адресу http://mozilla.org/MPL/2.0/
⠀⠀⠀⠀
### Локализация для серверных строк скриншотов Firefox
⠀⠀⠀⠀
## Глобальными ключевыми словами на всех страницах
⠀⠀⠀⠀
my-shots = мои снимки
home-link = домой
screenshots-description =
⠀⠀⠀⠀Скриншоты стали проще. Сделай снимок, сохрани и
⠀⠀⠀⠀поделись скриншотом, не выходя из Firefox.
⠀⠀⠀⠀
## Создание страницы
⠀⠀⠀⠀
# Примечание: { $title } это заглушка для заголовка страницы,
# запечатлённой на снимке экрана. По умолчанию для страниц без заголовков используется
# creation-page-title-default.
creating-page-title = Создаём { $title }
creating-page-title-default = страница
creating-page-wait-message = Сохраняем скриншот...

Встроенные функции

emails = У вас { $unreadEmails } непрочитанных писем.
emails2 = У вас { NUMBER($unreadEmails) } непрочитанных писем.
⠀⠀⠀⠀
last-notice =
⠀⠀⠀⠀Последняя проверка: { DATETIME($lastChecked, day: "numeric", month: "long") }.

В большинстве случаев Fluent автоматически выбирает правильный способ форматирования для аргумента и форматирует его в заданную локаль.

В других случаях разработчику придется аннотировать аргумент дополнительной информацией о том, как его отформатировать (см. Частичные аргументы)

Но в редких случаях для локализатора может быть целесообразно выбрать некоторые параметры форматирования, специфичные для данной локали.

Примеры включают: определение месяца как short или long в способе форматирования DATE (используя аргументы, определенные в Intl.DateTimeFormat) или использовать ли разделитель группировки при отображении большого числа.

Функции в FTL

Функции обеспечивают дополнительные возможности для локализаторов. Их можно использовать для форматирования данных в соответствии с правилами текущего языка или для предоставления дополнительных данных (например, платформа или время дня), которые локализатор может использовать для точной настройки перевода.

Реализации FTL должны поставляться с рядом встроенных функций, которые можно использовать из сообщений локализации.

Список доступных функций является расширяемым, и в среды можно добавить дополнительные функции, предназначенные для помощи локализаторам при написании переводов.

Использование функций

Функции FTL можно вызывать только внутри объектов. Используйте их для возврата значения, которое будет интерполировано в сообщении, или в качестве селектора в выражениях select.

Пример:

today-is = Сегодня - { DATETIME($date) }

Параметры функции

Функции могут принимать позиционные и именованные аргументы. Некоторые именованные аргументы доступны разработчикам, только если они предварительно отформатировали переменные, переданные в качестве аргументов для переводов (см. Частично отформатированные переменные).

Встроенные функции

Встроенные функции очень универсальны и должны быть применимы к любой среде перевода.

NUMBER

Форматирует число в строку в данной локали.

Пример:

dpi-ratio = Соотношение DPI равно { NUMBER($ratio, minimumFractionDigits: 2) }

Параметры:

currencyDisplay
useGrouping
minimumIntegerDigits
minimumFractionDigits
maximumFractionDigits
minimumSignificantDigits
maximumSignificantDigits

Параметры разработчика:

style
currency

См. подробные описания параметров в Intl.NumberFormat.

DATETIME

Форматирует дату в строку в данной локали.

Пример:

today-is = Сегодня { DATETIME($date, month: "long", year: "numeric", day: "numeric") }

Параметры:

hour12
weekday
era
year
month
day
hour
minute
second
timeZoneName

Параметры разработчика:

timeZone

См. подробные описания параметров в Intl.DateTimeFormat.

Неявное использование

Чтобы упростить большинство распространенных сценариев, FTL будет запускать некоторые функции по умолчанию при получении объектов.

NUMBER

Если переменная, переданная от разработчика, является числом и используется в объекте, FTL неявно вызовет функцию NUMBER для нее.

Пример:

emails = Количество непрочитанных сообщений: { $unreadEmails }
emails2 = Количество непрочитанных сообщений: { NUMBER($unreadEmails) }

Числа, используемые в качестве селекторов в выражениях выбора, будут точно соответствовать номеру или номеру текущей категории множественных чисел CLDR.

Оба следующих примера эквивалентны и будут работать одинаково. Второй пример может использоваться для передачи дополнительных параметров форматирования в NUMBER для выбора правильной категории множественного числа:

liked-count = { $num ->
⠀⠀⠀⠀[0] Лайков пока нет.
⠀⠀⠀⠀[one] Одному пользователю понравилось ваше сообщение
⠀⠀⠀⠀*[other] { $num } пользователям понравилось ваше сообщение
⠀⠀⠀⠀}
⠀⠀⠀⠀
liked-count2 = { NUMBER($num) ->
⠀⠀⠀⠀[0] Лайков пока нет.
⠀⠀⠀⠀[one] Одному пользователю понравилось ваше сообщение
⠀⠀⠀⠀*[other] { $num } пользователям понравилось ваше сообщение
⠀⠀⠀⠀}

DATETIME

Если переменная, переданная от разработчика, является датой и используется в помещаемом месте, FTL неявно вызовет функцию DATETIME.

Пример:

log-time = Время ввода: { $date }
log-time2 = Время ввода: { DATETIME($date) }

Частично форматированные переменные

Благодаря неявному форматированию, в большинстве случаев локализаторам не нужно вызывать функции явно. Если неявного форматирования недостаточно, функцию можно вызывать явно с дополнительными параметрами. Чтобы облегчить работу локализаторов, реализации Fluent позволяют разработчикам устанавливать параметры форматирования по умолчанию для переменных, которые они передают.

Другими словами, разработчики могут предоставлять переменные, уже обёрнутые в частичный вызов функции.

today = Сегодня { $day }
⠀⠀⠀⠀
ctx.format('today', {
⠀⠀⠀⠀day: Intl.MessageDateTimeArgument(new Date(), {
⠀⠀⠀⠀⠀⠀⠀⠀weekday: 'long'
⠀⠀⠀⠀})
})

Если локализатор желает изменить параметры, например, потому что строка не помещается в пользовательском интерфейсе, они могут передать переменную в ту же функцию и перегрузить параметры, установленные разработчиком.

today = Сегодня { DATETIME($day, weekday: "short") }

Подробности

Вы можете поиграть с синтаксисом в песочнице Fluent — интерактивном редакторе прямо в окне браузера. Если вы автор инструментов для локализации, вас может заинтересовать формальное описание синтаксиса, доступного в репозитории projectfluent/fluent.

___

Перевод документации от 28 апреля 2019 г.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *