Phrase and beyond

How to Use Phrase Strings’ In-Context Editor

Our in-context editor is a powerful tool that can massively enhance your translators' ability to get things done efficiently and effectively.
Phrase and beyond blog category featured image | Phrase

One of the most powerful and useful tools that Phrase Strings, the software localization platform within the Phrase Suite, offers is the in-context editor.

It is super easy to use and the functionality is well worth your time. Despite this, we still receive a lot of questions about how to properly use it.

Luckily I'm here to walk you through it, step by step.

So let's get started.

Installing the Phrase in-context editor

Because we love making your life easy, our editor already comes equipped with adapters for the most common web-frameworks.

And we'll be walking you through 4 of the most common.

You can also build your own custom integration if you can't find the framework you're looking for below.

Ruby on Rails (i18n)

AngularJS

Symfony 2.X

Django

Custom Integration

1. Ruby on Rails (i18n)

The first step is going to be to add the gem:

Add the phraseapp-in-context-editor-ruby gem to the environment you want to use the in-context editor with:

group :development do

  gem 'phraseapp-in-context-editor-ruby'

end

Then install it using the bundle command.

Initialize the gem

Generate the initializer file for the in-context editor using the generator that ships with the gem:

bundle exec rails generate phraseapp_in_context_editor:install --access-token=YOUR_ACCESS_TOKEN --project-id=YOUR_PROJECT_ID
Access token

You can create and manage access tokens in your profile settings or via the Authorizations API.

Project-ID

You can find the ID of your project in your project settings in the translation center.

This will create an initializer file with some configuration options.

Activate in-context editor

Next you'll want to enable the in-context editor in your phraseapp_in_context_editor.rb initializer file:

config.enabled = true

Add the JavaScript snippet

Add the JavaScript snippet to your application layout within the <head> section by using the phraseapp_in_context_editor_js helper:

<head>

  ...

  <%= phraseapp_in_context_editor_js %>

  ...

</head>

Done

Open your application in the browser to see the in-context editor applied to your app. You can sign in with any of your Phrase users.

Turbolinks

There is a little workaround involved with making the in-context editor work with turbolinks.

To solve this little problem, we typically recommend disabling turbolinks when using editor by adding the data-no-turbolink attribute to your body tag:

<body <%= PhraseApp::InContextEditor.enabled? ? "data-no-turbolink" : "" %>>

Ruby on Rails Gem configuration

You can easily enable & disable the in-context editor.

However, this setting is only available when you're using the phraseapp-in-context-editor-ruby-gem.

Enabling or disabling the in-context editor will affect the inclusion of your JavaScript snippet when you're using the view helper as well as the rendering of decorated key names in your views.

PhraseApp::InContextEditor.enabled = true|false

You can bind the flag to an environment variable:

PhraseApp::InContextEditor.enabled = (ENV["IN_CONTEXT_EDITING"] == "1")

*Please remember that the in-context editor is disabled by default.*

2. AngularJS

Here's a quick rundown of it, but we've also got a more detailed guide on how to translate AngularJS applications in context. Either way, before we get into it, first you'll need to install angular-phrase if you haven't already done so.

If you don't already have angular-phrase via Bower you can or download it manually by clicking this link.

bower install angular-phrase

Once that's settled we'll add the module to the AngularJS application

Add the angular-phrase module to your existing AngularJS application after loading the angular-translate module:

var myApp = angular.module("myApp", ['pascalprecht.translate', 'phrase']);

Configure the module:

myApp.value("phraseProjectId", "YOUR-PROJECT-ID");

myApp.value("phraseEnabled", true);

myApp.value("phraseDecoratorPrefix", "{{__");

myApp.value("phraseDecoratorSuffix", "__}}");

You can find the ID of your project in your project settings in the translation center.

Add the JavaScript snippet

Add the phrase-javascript directive inside the application. This is usually best within the <head>: tag.

<phrase-javascript></phrase-javascript>

And you're done!

Open your application in the browser again to see the in-context editor applied to your application.

You can now sign in with any number of users currently in your Phrase organization.

That wasn't so bad.

3. Symfony 2.X

The first thing you're going to need to do here is to create a new environment.

We recommend creating a new environment in which the in-context editor will run. Let's just call this brand new environment translation:

Start out by creating a new configuration file:

# app/config/config_translation.yml

imports:

    - { resource: config.yml }

parameters:

    translator.class: Acme\YourBundle\Translation\PhraseTranslator

The environment should be accessible in the browser, so next lets create a front controller for it:

# web/app_translation.php

<?php

require_once __DIR__.'/../app/bootstrap.php.cache';

require_once __DIR__.'/../app/AppKernel.php';

use Symfony\Component\HttpFoundation\Request;

$kernel = new AppKernel('translation', false);

$kernel->handle(Request::createFromGlobals())->send();

Extending the base translator

You need to override the standard translation method in order to expose the key names to the in-context editor:

# Acme/YourBundle/Translation/PhraseTranslator.php

<?php

namespace Acme\YourBundle\Translation;

use Symfony\Bundle\FrameworkBundle\Translation\Translator as BaseTranslator;

class PhraseTranslator extends BaseTranslator

{

    public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null)

    {

        $prefix = "{{__phrase_";

        $suffix = "__}}";

        if (!isset($locale)) {

            $locale = $this->getLocale();

        }

        if (!isset($this->catalogues[$locale])) {

            $this->loadCatalogue($locale);

        }

        if ($domain == 'routes') {

           // Return translated values for 'routes' domain

           return strtr($this->catalogues[$locale]->get((string) $id, $domain), $parameters);

        } else {

            // Return PhraseApp translation keys for all other domains

            return $prefix.$id.$suffix;

        }

    }

}

Add the JavaScript snippet

To load the in-context editor, you need to add the JavaScript snippet to your layout:

# Acme/YourBundle/Resources/views/layout.html.twig

{% if app.environment == 'translation' %}

<script>

window.PHRASEAPP_CONFIG = {

    projectId: "YOUR-PROJECT-ID"

};

(function() {

    var phraseapp = document.createElement('script'); phraseapp.type = 'text/javascript'; phraseapp.async = true;

    phraseapp.src = ['https://', 'phraseapp.com/assets/in-context-editor/2.0/app.js?', new Date().getTime()].join('');

    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(phraseapp, s);

})();

</script>

{% endif %}

Done

Start translating your app by accessing your front controller in the browser!

(The code for this step was generously provided by Malte Marx of marxbeck.)

4. Django

If you're using Django, this one's going to be super simple to implement.

Adding the package

Of course you'll first need to install the django-phrase package with pip:

pip install django-phrase

And add phrase to the list of installed apps:

INSTALLED_APPS = (

    'phrase',

)

Next, extend the template

You can now use the phrase_i18n template tag in your templates:

{% load phrase_i18n %}

Note: You have to load phrase_i18n after you load ì18n in order to let django-phrase override the translation methods.

Add the JavaScript snippet

Add the JavaScript snippet to your base layout file. This should go inside the <head> section of your template file:

{% phrase_javascript %}

Configuration

You can configure the in-context editor in your settings by using these options:

PHRASE_ENABLED = True

PHRASE_PROJECT_ID = 'YOUR_PROJECT_ID'

PHRASE_PREFIX = '{{__'

PHRASE_SUFFIX = '__}}'

And you're done

Open your application in the browser to see the in-context editor applied to your application. Once again, you can sign in with any of your Phrase users.

Other frameworks

Custom Integration

 If you weren't able to find the framework you're currently working with, don't despair! We've got instructions right here on how you can easily implement our in-context editor manually with your own system with custom integrations.

Configuring the editor

Now that you've finished installing the in-context editor on your framework it's time to configure everything.

You've got several options at this point that will help you to customize things so that it works smoothly with whatever software you're using.

Ignoring keys

This setting is likewise only available when using the ruby-gem.

Let's say that you don't want to include certain keys from being rendered in by the editor. If you're working with Rails those keys could include error messages or date/time formats.

If you want to exclude these keys from being handled automatically by the phraseapp-in-context-editor-ruby gem, all you have to do is add an array of keys to your phraseapp_in_context_editor.rb initializer.

You can also use wildcards:

PhraseApp::InContextEditor.configure do |config|

  config.ignored_keys = ["date.*", "forbidden.key", "errors.messages*"]

end

Keys that match one of these patterns won't be made accessible to the in-context editor but will instead be rendered normally.

Customize key decorators

If you're using in-context editor you would usually wrap the key name with "decorators" (by default they're those curly brackets) in order to to generate unique identification keys in context with your document:

{{__phrase_YOUR.KEY__}}

However, this can cause conflicts with other libraries such as client-side template engines like AngularJS or Ember.js) using similar syntax.

If you happen to find yourself having this problem, you may consider changing the Phrase in-context editor decorator prefix and suffix in your setup.

In order to tell the in-context editor which decorators the parser should be on the lookout for, all you have to do is add the following configuration values before the JavaScript snippet:

window.PHRASEAPP_CONFIG = {

  prefix: '[[__',

  suffix: "__]]"

}

These configuration values will tell the editor to look for tags that begin with [[__ and end with __]].

For example:

[[__phrase_YOUR.KEY__]]

If you’re using the phraseapp-in-context-editor-ruby gem to provide in-context editor functions, you'll need to be sure that you have to configured your decorators there too:

PhraseApp::InContextEditor.configure do |config|

  config.prefix = "[[__"

  config.suffix = "__]]"

end

If you're not using the gem, you'll need to make sure that you remember to adjust the key name exposure pattern in your custom code.

Debug mode

If for some reason you need to enable debug mode, all that is required is to add the following configuration before the JavaScript snippet:

window.PHRASEAPP_CONFIG = {

debugMode: true

}

Ajax mode

The Phrase in-context editor also supports Ajax and DOM manipulation (i.e. via JavaScript) out of the box.

In order to achieve this, we use mutation observers. Keep in mind, though, that mutation observers are only supported by modern browsers.

Ajax mode can sometimes cause problems that can result in severe performance issues with the in-context editor. If you want to disable Ajax mode, all you have to do is add this configuration before the JavaScript snippet:

window.PHRASEAPP_CONFIG = {

  ajaxObserver: false

}

Auto lowercase

As a default setting, the in-context editor document parser will automatically convert all of your keys into lowercase.

If you’re experiencing issues with this behavior and would prefer to instead use case-sensitive keys with the in-context editor, you can disable this auto lowercase feature with this:

window.PHRASEAPP_CONFIG = {

  autoLowercase: false

}

Force locale

If your goal is to set a specific locale when starting up the in-context editor you can use the forceLocale setting to do so.

This would certainly make sense if you want to pre-select the locale that is currently displayed in your web application in the in-context editor as well.

window.PHRASEAPP_CONFIG = {

  forceLocale: "pt-BR"

}

Direct links to your translated content

Our context-view add-on is an integration that makes itself available automatically once the in-context editor is set up and has been used recently.

The add-on provides links from Phrase that are sent back to your application and then provides a translation.

Our context-view collects key information about which are being rendered and on which URL in your web application. It then stores this information directly on Phrase.

The in-context editor keeps track of the position of translatable content within your application.

Once set up we offer you backlinks to the in-context editor with the translation center interface, provided that you've added your in-context editor URL in your project's settings.

Configure context-view

Setting up context-view is straightforward:

  1. Activate the in-context editor by setting up a staging environment that uses it.
  2. Add the URL to the staging environment in your project settings (in-context editor URL)
  3. Browse your page with the now installed editor

While you are working and browsing with the in-context editor, all of the information is collected automatically and will be stored, safe and sound, in Phrase.

Working with context-view

Working with context-view is super easy:

Once we have collected enough information to create a key, we will then automatically display the link to the correct URL that is next to the key in our translation view.

When the user clicks on the link he or she will be redirected to the URL of that project and taken directly to the correct page that displays the key. The browser will then automatically scroll to wherever that exact location is before the in-context editor will load the specified key.

You can now edit your translation immediately!

Make an environment for your translators

In order to use our in-context editor you're probably going to need to have a dedicated environment for your translators.

There are two ways that we can do this.

1. Use a dedicated environment

This is the method we recommend most strongly.

You can use a clone of one of your existing staging environments to create a dedicated environment for your translators.

If you decide to go with this route, the editor will always be enabled and your translators will be able to browse your website and make edits wherever they may be required.

While this may require a little bit more effort on your part in order to set up, we feel strongly that it will not only lead to a more streamlined workflow but will, in general, give your translators a much easier time.

2. Use an existing environment with a feature switch

If you really, really insist on not setting up a brand new, dedicated environment you can always use an existing one that comes equipped with a feature enabled or disabled.

If you're using a Ruby gem (our favorite, can't you tell?) it could end up looking something like this:

before_filter :toggle_in_context_editor

...

def toggle_in_context_editor

  PhraseApp::InContextEditor.enabled = (params[:in_context_editor_enabled] == "1")

end

Note, however, that if you're using our Ruby gem that this setting will impact the current thread. This might end up causing some strange behavior if you're working with multiple users in the same staging environment.

Again, we really do recommend the first option, but it's ultimately your call.

Conclusion

That wasn't too bad, was it?

Our in-context editor is a really powerful tool that, if set up properly, will massively enhance the ability of your translators to get things done efficiently and effectively.

We hope that this guide answers some of your questions and makes your life a bit simpler.