Интернационализация вашего плагина

Применимость

Это руководство относится к JIRA 5.0 и более поздним версиям и Confluence 4.0 и более поздних версий.

Уровень опыта

Intermediate. Наши учебники классифицируются как «новичок», «промежуточный(intermediate)» и «продвинутый». Этот уровень находится на «промежуточном» уровне. Если вы еще никогда не разрабатывали плагин, вы можете найти это немного сложнее.

Источник плагина

Исходный код плагина, используемого в этом учебнике, доступен в общедоступном репозитории Atlassian. Вы можете проверить исходный код с Bitbucket: https://bitbucket.org/atlassian_tutorial/jira-i18n.

Краткий обзор учебника

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

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

  • Java-классы, инкапсулирующие логику плагина.
  • Ресурсы для отображения пользовательского интерфейса плагина.
  • Дескриптор плагина (файл XML), чтобы включить модуль плагина в приложение Atlassian.

Все эти компоненты будут содержаться в одном JAR-файле. Каждый компонент далее обсуждается в примерах ниже.

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

Необходимые знания

Чтобы завершить этот учебник, вы уже должны понять основы разработки Java: классы, интерфейсы, методы, способы использования компилятора и т. д.

Вы также должны понимать:

  • Как создать проект плагина Atlassian с помощью Atlassian Plugin SDK.
  • Как открыть проект плагина в вашей среде IDE, например Eclipse или IDEA.
  • Как скомпилировать ваш проект и создать файл JAR с помощью Maven.

Шаг 1. Создайте проект плагина

Используйте соответствующую команду atlas-create-application-plugin для создания своего плагина. Например, atlas-create-jira-plugin или atlas-create-confluence-plugin.

При появлении запроса введите следующую информацию, чтобы определить ваш плагин:

  • group-id: сom.example.tutorial.plugins
  • artifact-id: tutorial-i18n-plugin
  • version: 1.0
  • package: com.example.tutorial.plugins

Шаг 2. Добавление метаданных плагина к POM

Теперь вам нужно отредактировать свой POM (файл определения объектной модели проекта), чтобы добавить некоторые метаданные о вашем плагине и вашей компании или организации.

  1. Измените файл pom.xml в корневой папке вашего плагина.
  2. Добавьте название вашей компании или организации и ваш сайт в элемент <organization>:

<organization>
    <name>Example Company</name>
    <url>http://www.example.com/</url>
</organization>

  1. Обновите элемент <description>:

<description>This plugin demonstrates how to implement i18n into your plugins.</description>

  1. Сохраните файл.

Шаг 3. Добавьте модули плагинов в дескриптор плагина

Теперь вы добавите модуль плагинов в свой дескриптор плагина в src / main / resources / atlassian-plugin.xml. Дескриптор плагина представляет собой XML-файл, который идентифицирует плагин для JIRA и определяет функциональные возможности, которые требует плагин.

Вот базовый дескриптор плагина, в котором еще не определены модули плагинов:


<atlassian-plugin key="${project.groupId}.${project.artifactId}" name="${project.artifactId}" plugins-version="2">
    <plugin-info>
        <description>${project.description}</description>
        <version>${project.version}</version>
        <vendor name="${project.organization.name}" url="${project.organization.url}" />
    </plugin-info>
</atlassian-plugin>

Обратите внимание, что часть информации из POM передается в дескриптор плагина с использованием имен переменных, таких как $ {project.artifactId}.

Вам понадобятся следующие модули плагинов (или доступные ресурсы):

  • Импорт компонентов - для SAL I18nResolver и ATR VelocityTemplateRenderer.
  • Сервлет -для отображение шаблона Velocity.
  • Ресурс - для свойств i18n. (Это не тип модуля плагина per se - только доступный ресурс в вашем плагине.)
  • Веб-ресурс - для ресурса JavaScript, который мы добавим позже в учебнике.

Давайте используем генератор модуля плагинов, часть Atlassian Plugin SDK, для создания заглушек для наших модулей.

  1. Откройте командное окно и перейдите в корневую папку плагина (где находится pom.xml).
  2. Запустите модуль atlas-create-jira-plugin-module

Подробнее об этой команде см. в модуле atlas-create-jira-plugin.

  1. Выберите Импорт компонентов (№ 1).

 

  1. При появлении запроса введите следующую информацию, чтобы описать свой модуль плагина:
  • Введите полностью квалифицированный интерфейс: com.atlassian.sal.api.message.I18nResolver
  1. Примите значения по умолчанию для следующих двух параметров. (Они упоминаются только здесь, поэтому вы можете увидеть, как указанные здесь значения добавляются в ваш файл дескриптора плагина.)
  • Ключ модуля: I18nResolver
  • Фильтр (не требуется): просто нажмите клавишу «Ввод».
  1. Когда будет предложено добавить дополнительный модуль плагина, выберите «Y» (для «Да»).
  2. Еще раз выберите Импорт компонентов (№ 1).
  3. При появлении запроса введите следующую информацию:
  • Введите полностью квалифицированный интерфейс: com.atlassian.templaterenderer.velocity.one.six.VelocityTemplateRenderer
  1. Примите значения по умолчанию для следующих двух параметров:
  • Ключ модуля: speedTemplateRenderer
  • Фильтр (не обязательно): снова нажмите клавишу «Ввод».
  1. Когда будет предложено добавить дополнительный модуль плагина, выберите «Y».
  2. Теперь выберите Servlet (№ 21).

 

  1. При появлении запроса введите следующую информацию для описания модуля плагина:
  • Введите новое имя класса: RenderingServlet
  1. Примите значение по умолчанию для следующего параметра:
  • Введите имя пакета: com.example.tutorial.plugins.servlet
  1. При появлении запроса с «Показать расширенную настройку» выберите «N» (для «Нет»).
  2. Когда будет предложено добавить дополнительный модуль плагина, выберите «N».

 

Проверьте файл atlassian-plugin.xml и убедитесь, что генератор модуля дал правильную конфигурацию:


<component-import key="i18nResolver"
    interface="com.atlassian.sal.api.message.I18nResolver"/>
<resource type="i18n" name="i18n" location="atlassian-plugin"/>
<component-import key="velocityTemplateRenderer"
    interface="com.atlassian.templaterenderer.velocity.one.six.VelocityTemplateRenderer"/>
<servlet name="Rendering Servlet" i18n-name-key="rendering-servlet.name"
         key="rendering-servlet"
         class="com.example.tutorial.plugins.servlet.RenderingServlet">
    <description key="rendering-servlet.description">The Rendering Servlet Plugin</description>
    <url-pattern>/renderingservlet</url-pattern>
</servlet>

Шаг 4. Добавьте зависимость к POM вашего проекта

Хотя JIRA связывает Atlassian Template Renderer как часть  платформы разработки плагинов, она не включена по умолчанию в список зависимостей Maven JIRA. Поэтому нам нужно будет добавить его вручную в файл pom.xml нашего плагина.

Откройте файл pom.xml в корневом каталоге вашего плагина и найдите элемент jira-api <dependency />:


<dependency>
  <groupId>com.atlassian.jira</groupId>
  <artifactId>jira-api</artifactId>
  <version>${jira.version}</version>
  <scope>provided</scope>
</dependency>

Вставьте следующий элемент Atlassian Template Renderer <dependency /> чуть ниже этого:


<dependency>
  <groupId>com.atlassian.templaterenderer</groupId>
  <artifactId>atlassian-template-renderer-api</artifactId>
  <version>1.3.1</version>
  <scope>provided</scope>
</dependency>

Это приведёт публичный API-интерфейс рендеринга в наш путь к классам.

 

Указание, предоставленное provided в элементе <scope /> зависимости, указывает Maven, чтобы сделать эту библиотеку доступной во время выполнения, но не связать ее с нашим финальным плагином.

Шаг 5. Разработайте плагин

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

  • Откройте командное окно и перейдите в корневую папку плагина (где находится pom.xml).
  • Запустите atlas-run (или atlas-debug, если вы захотите запустить отладчик в своей среде IDE).

С этого момента вы можете использовать QuickReload для переустановки своего плагина за сценой, когда работаете, просто восстановив свой плагин.

FastDev и атлас-cli устарели. Вместо этого используйте автоматическую переустановку плагинов с помощью QuickReload.

Переустановка с помощью QuickReload.

Чтобы запустить переустановку вашего плагина:

  1. Внесите изменения в модуль плагина.
  2. Откройте панель инструментов разработчика.

РИСУНОК

  1. Нажмите значок FastDev.

РИСУНОК

Система перестраивает и перезагружает ваш плагин:

РИСУНОК

Используйте живую перезагрузку для просмотра обновлений в реальном времени для шаблонов и других ресурсов:

  1. Откройте панель инструментов разработчика.
  2. Нажмите значок живой перезагрузки.

Значок начинает вращаться, показывая, что он включен.

  1. Отредактируйте ресурсы проекта.
  2. Сохраните изменения:

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

Вернитесь в браузер. Обновленный плагин был установлен в приложение, и вы можете проверить свои изменения.

Полные инструкции приведены в руководстве SDK.

Теперь, когда сделаны предварительные замечания, давайте продолжим код.

Мы начнем со следующего:

  1. Создайте шаблон скорости для последней страницы;
  2. Извлеките сервлет, который мы только что создали, чтобы отобразить шаблон;
  3. Добавьте некоторые элементы в atlassian-plugin.properties, чтобы отобразить в шаблоне.

Шаг 5а. Создание шаблона Velocity

Создайте базовый файл шаблона Velocity на странице src / main / resources / templates / page.vm и дайте ему следующее содержание:


<html>
    <head>
        
    </head>
    <body>
    Hi, I'm a Velocity template!
    </body>
</html>

В дальнейшем мы добавим что-то интересное в этот файл.

Шаг 5b. Извлеките сервлет

Откройте файл класса RenderingServlet Java (созданный генератором модуля плагинов на шаге 3 выше), который должен находиться в файле src / main / java / com / example / tutorial / plugins / servlet / RenderingServlet.java и введите следующее:

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


package com.example.tutorial.plugins.servlet;

import com.atlassian.templaterenderer.TemplateRenderer;  // new!
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

public class RenderingServlet extends HttpServlet {
    private static final Logger log = LoggerFactory.getLogger(RenderingServlet.class);
    private static final String TEMPLATE_PATH = "/templates/page.vm";  // new!

    private final TemplateRenderer templateRenderer;  // new!

    // new!
    public RenderingServlet(TemplateRenderer templateRenderer) {
        this.templateRenderer = templateRenderer;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {
        Map<String, Object> context = Maps.newHashMap();  // new!

        resp.setContentType("text/html;charset=utf-8");  // modified!
        templateRenderer.render(TEMPLATE_PATH, context, resp.getWriter());  // new!
    }
}

Этот код является минимальным, необходимым для создания шаблона Velocity.

Сохраните и вернитесь в окно терминала.

Если вы еще этого не сделали, запустите atlas-run, чтобы запустить JIRA.

После запуска JIRA перейдите к http: // localhost: 2990 / jira / plugins / servlet / renderingservlet (при необходимости войдите в систему), и вы увидите дружелюбное приветствие из только что виализируемого шаблона. Убедитесь, что вы  поддерживаете открытой эту вкладку в браузере, поскольку мы будем использовать ее с этого момента.

Шаг 5с. Получите некоторые интернационализированные значения из atlassian-plugin.properties

Откройте файл src / main / resources / atlassian-plugin.properties и добавьте следующую строку:


internationalized.greeting=I came from the properties file!

Теперь вернитесь к классу RenderingServlet Java и добавьте объект I18nResolver к конструктору:


package com.example.tutorial.plugins.servlet;

import com.atlassian.sal.api.message.I18nResolver;
import com.atlassian.templaterenderer.TemplateRenderer;
import com.google.common.collect.Maps;  // new!
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

public class RenderingServlet extends HttpServlet {
    private static final Logger log = LoggerFactory.getLogger(RenderingServlet.class);
    private static final String TEMPLATE_PATH = "/templates/page.vm";

    private final TemplateRenderer templateRenderer;
    private final I18nResolver i18nResolver;  // new!

    public RenderingServlet(TemplateRenderer templateRenderer,
            I18nResolver i18nResolver) {
        this.templateRenderer = templateRenderer;
        this.i18nResolver = i18nResolver;  // new!
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {
        Map<String, Object> context = Maps.newHashMap();
        context.put("greeting",
            i18nResolver.getText("internationalized.greeting"));  // new!

        resp.setContentType("text/html;charset=utf-8");
        templateRenderer.render(TEMPLATE_PATH, context, resp.getWriter());
    }

}

Интерфейс I18nResolver предоставляется общим уровнем доступа Atlassian (SAL).

Каждый продукт реализует этот интерфейс таким образом, что любой плагин может считывать корректно локализованное значение из своих свойств. Здесь мы используем его для получения значения, которое мы только что добавили для internationalized.greeting, и сохраним его в контексте Velocity под символом «приветствие».

Наконец, откройте основной шаблон Velocity, который вы создали выше, и добавьте в него символ приветствия:


<html>
    <head>

    </head>
    <body>
    Hi, I'm a Velocity template! $greeting  ## new!
    </body>
</html>

Во время рендеринга Velocity заменит $ greeting значением, содержащимся под этим именем в его контексте.

Перестройте свой плагин и вернитесь в свой браузер и обновите его.Функция QuickReload будет замечать изменения, внесенные вами в код вашего плагина, и выполнять за кадром работу, необходимую для обновления с этими изменениями. Когда страница заканчивается загрузкой, вы должны увидеть такое же дружелюбное приветствие, как и новое,  с новым объяснием, откуда оно взялось.

Шаг 5d. I18n внутри шаблона Velocity

Возможно, вы думаете, что выбор значений i18n из кода Java немного лучше, чем жесткое кодирование самих значений, и вы были бы правы. Нет никаких веских оснований для того, почему сервлет должен предоставлять значения через контекст Velocity, особенно если шаблон не имеет доступа к ним сам по себе!

Следовательно, теперь удалите следующую строку из метода doGet () сервлета:


context.put("greeting", i18nResolver.getText("internationalized.greeting"));

Перестройте свой плагин, который снова будет переустановлен с помощью QuickReload, и обратите внимание, что второе приветствие отсутствует, заменено только «$ greeting».

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

Повторно запустите шаблон Velocity и измените строку приветствия на следующее:


Hi, I'm a Velocity template! $i18n.getText("internationalized.greeting")

Переключитесь на перезагрузку страницы своего браузера, и вы увидите, что приветствие вернулось! Как это возможно? Откуда взялась ссылка $ i18n?

TemplateRenderer имеет немного магии: SAL I18nResolver автоматически добавляется в контекст Velocity под именем i18n, поэтому он всегда доступен в любом шаблоне, который создает TemplateRenderer. Помимо полезности, это позволяет нам локализовать проблемы.

Шаблон - единственное место, где нам нужно беспокоиться о интернационализации. Хотя, безусловно, можно использовать сервлет для обеспечения удобочитаемого текста, мы можем избавиться от этого контекста «посредника» и сделать намерение совершенно ясным.

Шаг 5e. I18n на клиенте

До сих пор мы видели, как реализовать i18n из кода «back-end». Теперь пришло время посмотреть, как это сделать с «front-end».

Библиотека Atlassian User Interface (AUI) содержит ресурсы CSS и JavaScript, предназначенные для упрощения придания атласским приложениям и плагинам последовательного, привлекательного внешнего вида и помощников для очень общих задач переднего плана. Одна из этих задач - i18n, а текущий метод AUI для реализации этого - через объект JavaScript AJS.params.

Вернитесь к шаблону Velocity и введите следующий код внутри элемента <head>:


<meta name="decorator" content="atl.general">

Переключитесь на перезагрузку страницы  своего браузера, и вы увидите, что наша скучная старая страница была полностью преобразована во что-то, что соответствует другим страницам JIRA. JIRA использует SiteMesh во время рендеринга страницы, чтобы окружать содержимое страницы стилем JIRA; добавленный тег <meta> указывает SiteMesh, какой из его настроенных декораторов использовать.

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

Применение этого декоратора делает еще одну важную вещь: он применяет стандартные интерфейсные ресурсы JIRA к странице и делает их доступными для нашего плагина. Сюда относится AUI, поэтому теперь доступен объект AJS.params.

Вернитесь к atlassian-plugin.properties и добавьте следующую строку:


javascript.greeting=I will wind up in JavaScript!

Вернитесь к шаблону Velocity и введите следующий код сразу после существующей строки приветствия:


<form id="parameterContainer">
  <fieldset class="hidden parameters">
    <input type="hidden" title="greeting" value="$i18n.getText('javascript.greeting')">
  </fieldset>
</form>

Ключевым здесь является параметр class класса элемента <fieldset>: скрытые параметры hidden parameters:

  • Класс hidden не позволяет отображать поле на странице, что является преднамеренным, потому что мы не собираемся показывать форму на странице.
  • Класс parameters является сигналом для AJS.params, что элементы <input> должны быть собраны и добавлены как свойства AJS.params.

Перезагрузите страницу браузера и просмотрите источник страницы.

Имейте в виду, что пока вы не увидите никаких изменений, виализированных на странице.

В представлении источника страницы найдите элемент <fieldset> и убедитесь, что он выглядит так:


<form id="parameterContainer">
  <fieldset class="hidden parameters">
    <input type="hidden" title="greeting" value="I will wind up in JavaScript!">
  </fieldset>
</form>

Мы видим, что Velocity оказала нам такую же пользу, как и в этом уроке, заменив значение в файле свойств для ссылки $ i18n.getText ().

После того, как Velocity завершает рендеринг страницы - и пока страница отображается в браузере - AUI сканирует HTML страницы для <fieldset> со классами скрытыми параметрами hidden parameters и добавляет их как свойства в AJS.params. В этом случае свойство AJS.params.greeting теперь определено и имеет значение «Я завершу JavaScript!».

Шаг 5f. Использование AJS.params в JavaScript

Теперь мы увидим, как использовать это значение в ресурсах JavaScript плагина.

Вернитесь к шаблону Velocity и добавьте следующее сразу под элементом <form />:


<p id="greeting-element"></p>

Это будет тем куда мы поместим наш текст.

В разделе src / main / resources создайте каталог, includes/js, а затем создайте новый файл в этом каталоге с именем i18n.js.

Добавьте в этот файл следующее:


AJS.toInit(function() {
    var element = AJS.$("#greeting-element").text(AJS.params.greeting);
});

В этом фрагменте JavaScript используется jQuery (который включен в AUI), чтобы найти элемент на странице с greeting-element ID и заполнить его значением AJS.params.greeting.

Теперь нам нужно сообщить JIRA, что наш плагин требует этого ресурса JavaScript через модуль плагина веб-ресурса. Для этого мы снова воспользуемся компонентом генератора модуля плагина Atlassian Plugin SDK:

  1. Откройте командное окно и перейдите в корневую папку плагина (где находится pom.xml).
  2. Запустить atlas-create-jira-plugin-module

Подробнее об этой команде см. в atlas-create-jira-plugin-module.

  1. Выберите веб-ресурс (№ 28).
  2. При появлении запроса введите следующую информацию, чтобы описать свой модуль плагина:
  • Введите имя модуля плагина: примеры веб-ресурсов Example Web Resources
  • Введите имя ресурса: i18n.js
  • Введите тип ресурса: Примите значение по умолчанию для загрузки download.
  • Введите местоположение: /includes/js/i18n.js
  1. Примите оставшиеся значения по умолчанию («N» для «Нет») при появлении запроса:
  • Добавить параметр ресурса
  • Добавить ресурс
  • Показать расширенную настройку
  • Добавить другой модуль плагина

Убедитесь, что ваш файл atlassian-plugin.xml теперь содержит следующее:


<web-resource name="Example Web Resources" i18n-name-key="example-web-resources.name" key="example-web-resources">
    <description key="example-web-resources.description">The Example Web Resources Plugin</description>
    <resource name="i18n.js" type="download" location="/includes/js/i18n.js"/>
</web-resource>

Мы создали модуль веб-ресурсов, который указывает на наш JavaScript и сообщает системе плагинов, где он будет доступен. Мы можем обратиться к этому модулю по имени, которое мы будем делать дальше.

Вернитесь к шаблону Velocity и добавьте следующее внутри элемента <head />:


$webResourceManager.requireResource("com.example.tutorial.plugins.tutorial-i18n-plugin:example-web-resources")

$ webResourceManager - это еще один объект, включенный в контекст Velocity по умолчанию. Он позволяет странице запрашивать ресурсы - CSS, JavaScript и т. д. - для включения в страницу во время рендеринга. Эта строка запрашивает ресурсы под так называемым полным ключом модуля. Когда эта страница будет отображаться, наш JavaScript будет включен.

 

Переключитесь на перезагрузку страницы    своего браузера, и вы увидите оригинальное приветствие, а затем наше новое, второе, вставленное JavaScript приветствие.

Резюмирование

В этом учебнике показано, как добавить поддержку интернационализации в ваш плагин:

  1. Определение файла свойств.
  2. Получение текста непосредственно из этих свойств.
  3. Использование Atlassian Template Renderer для вставки текста непосредственно в ваши шаблоны Velocity.
  4. Использование объекта AUI AJS.params для получения текста в вашем JavaScript-коде.

Поздравляю, вот и все

Есть шоколад!

 

По материалам Atlassian JIRA  Server Developer Internationalizing your plugin