Skip to main content
Gábor Hojtsy on Drupal

Main navigation

  • Blog posts
    • Configuration schema cheat sheet
    • Drupal 11
    • Drupal 10
    • #DrupalCares
    • Drupal 9
    • DDD 2014 report
    • Multilingual Drupal 8
    • Multilingual Drupal 7
    • Drupal 6
  • About Gábor

Breadcrumb

  1. Home
  2. Multilingual Drupal 8

Drupal 8 multilingual tidbits 19: content translation development

By Gábor Hojtsy , 11 November, 2015

Up to date as of March 14th, 2017.

Now that we covered how content translation workflow works in Drupal 8, its time to look a bit at the API side. In Drupal 7 this meant dealing with scary seemingly infinitely nested arrays with language codes, field names, deltas, etc. Drupal 8 makes this a whole lot simpler.

Translation basics on an entity

Drupal 8's entity API handles entities as full fledged objects. Translations of entities may be requested from this object and returned as a reusable entity object as well, which can be treated the same way as the original entity that we loaded. Here are some quick examples:

<?phpuse Drupal\node\Entity\Node;// Load node 4. In terms of language, this will get us an entity// in the original submission language.$node = Node::load(4);// Get a list of all translations and collect titles.$titles = [];$languages = $node->getTranslationLanguages();foreach ($languages as $langcode => $language) {  // The object returned by getTranslation() behaves the same way as $node.  $translation = $node->getTranslation($langcode);  $titles[$langcode] = $translation->title;}// If the node has no Hungarian translation, add one.if (!$node->hasTranslation('hu')) {  $translation = $node->addTranslation('hu', array('title' => 'Hungarian title'));  $translation->save();}// If the node has a Spanish translation, update the title. In case of a missing// Spanish translation this will throw an InvalidArgumentException.$node->getTranslation('es')->setTitle('Spanish title')->save();// Remove the Hungarian translation.$node->removeTranslation('hu');$node->save();?>

Loading the right translation

Drupal 8 also comes with several supporting systems for dealing with content language. For example, a content language is selected for the page to be used for translated content display. You can retrieve that language from the language manager and use to deal with the right translation.

<?phpuse Drupal\Core\Language\LanguageInterface;use Drupal\taxonomy\Entity\Term;$term = Term::load(23);$langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT->getId());$translation = $term->getTranslation($langcode);// Now deal with the right language from here on.?>

A possible problem with that is getTranslation() will return an exception if the translation requested does not exist. If you want to be sure to load only from existing translations and apply a fallback mechanism for loading suitable translations when something is not available, you need entity language negotiation. That is provided by the entity repository and helps you figure out which translation to load.

<?phpuse Drupal\user\Entity\User;$account = User::load(15);$langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();// Get the user profile with language fallback, if it did not have a translation for $langcode.$translation = \Drupal::service('entity.repository')->getTranslationFromContext($account, $langcode);?>

The entity repository uses the language manager and possibly contributed modules to figure out which language to load for the entity. Ideally $langcode would be that language, but if that is not available, modules get a chance to provide fallback schemes for which other language may be best suitable. See LanguageManager::getFallbackCandidates() as well as hook_language_fallback_candidates_alter() and hook_language_fallback_candidates_OPERATION_alter() for more information.

That's it for a short summary of how the much improved content translation API works in Drupal 8. There is of course a lot more to this topic from creating translatable entity types, new field types to dealing with revisions and entity field queries. For some of those deeper details on how content translations are stored and queried, see Francesco Placella's (unfortunately already slightly outdated) articles in Drupal Watchdog titled Wait, $langcode? What the Heck? and Entity Storage, the Drupal 8 Way.

Next up, I plan to look at complex core use cases where interface, content and configuration translation meet. If you know how the pieces map together, you can really translate practically anything!

Issues to work on

None at the moment.

Tags
Drupal 8
Drupal
D8MI
multilingual

David Park (not verified)

9 years 5 months ago

Get content language

You say that calling $langcode = \Drupal::languageManager()->getLanguage(LanguageInterface::TYPE_CONTENT); would give the language object of the current content language? How is that? In my site i'm calling that and getLanguage will only return a language object if the LanguageInterface::TYPE_CONTENT witch is string "language_content" from the list of all languages that this content is translated with, ie array('en', 'sv', 'und', 'zxx') for a site with Swedish and English. I don't se that the array in getLanguage, that $languages = $this->getLanguages(LanguageInterface::STATE_ALL); is returning will include a object with key "language_content". I guess I'm missing something here?

Gábor Hojtsy

9 years 5 months ago

In reply to Get content language by David Park (not verified)

my mistake!

My mistake, sorry for wasting your time. It should be \Drupal::languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT), fixing.

Christophe Jossart (not verified)

9 years 1 month ago

get / set title

Thanks for this nice introduction, it really helped to get started quickly. I am working with the 8.1.x branch and noticed that $translation->title; becomes $translation->getTitle() (or $translation->get('title')->value;). Probably the same for $node->getTranslation('es')->title->value = 'Spanish title'; that should be replaced by $node->getTranslation('es')->setTitle('Spanish title');.

Gábor Hojtsy

8 years 4 months ago

In reply to get / set title by Christophe Jossart (not verified)

thanks, updated

Thanks, updated. That also allowed us to inline the save() since setTitle() returns the object. Also I've been notified via https://www.drupal.org/node/2840299 that the invalid Spanish translation will not get created anymore.

<?php// If the node has a Spanish translation, update the title. In case of a missing// Spanish translation this will throw an InvalidArgumentException.$node->getTranslation('es')->setTitle('Spanish title')->save();?>

Nehal Rupani (not verified)

8 years 10 months ago

problem for adding translation with code

Hello Gábor,

Thanks for sharing multilingual functionality in detail. I am trying to import nodes automatically from excel file.

if ($node->hasTranslation($fields_values['langcode']) != 1) {
$translation = $node->addTranslation($fields_values['langcode']);

foreach($fields_values as $key => $value) {
$translation->set($key, $value);
}
$translation->save();
}
else {
$node->save();
}
With above code I was able to get node translated in to other language then english but What i found is when try to view those pages in admin/content area all my earlier path alias with english language are gone and it showing me nodes with other language which is not default language of my website. It should show English nodes by default. Do you think anything else is missing in above code.

What I can guess from admin screen is may be i need to add content_translation_source with is field in DB. how to do that while updating node.

Gábor Hojtsy

8 years 9 months ago

In reply to problem for adding translation with code by Nehal Rupani (not verified)

looks fine

Adding node translations should not affect path aliases. Check out http://cgit.drupalcode.org/multilingual_demo/tree/multilingual_demo.ins… for an example of creating nodes and translations programatically.

Sam Mortenson (not verified)

8 years 6 months ago

In reply to problem for adding translation with code by Nehal Rupani (not verified)

You're not crazy!

This is a real problem, I filed an issue for it to explain what I'm seeing: https://www.drupal.org/node/2824669

Greg Sims (not verified)

8 years 4 months ago

getTranslation() creates new translation

A possible problem with that is getTranslation() as we have seen above will just create a new translation if there is none, so you may as well just get back the original language values without a different translation loaded.

I don't believe this is correct based on: https://www.drupal.org/node/2840299

Thanks, Greg

Gábor Hojtsy

8 years 1 month ago

In reply to getTranslation() creates new translation by Greg Sims (not verified)

indeed, thanks for the correction

Updating the post. I think this used to be the original behavior but it got changed later.

nicrodgers (not verified)

8 years 3 months ago

$langcode correction

Thanks for the really useful tips, as always!
I think the langcode stuff has updated since you wrote this, as instead of:

<?php $langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT); ?>

I think it should be:
<?php $langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); ?>

Gábor Hojtsy

8 years 1 month ago

In reply to $langcode correction by nicrodgers (not verified)

You are right, fixed

Fixed, thanks and sorry.

Marc Isaacson (not verified)

8 years 2 months ago

getCurrentLanguage() returns an object not an ID

You've got the following code in your term snippet.

$langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT);
$translation = $term->getTranslation($langcode);

I believe you should actually be using
$translation = $term->getTranslation($langcode->getId());

I think maybe you overlooked this because you did it differently above

$languages = $node->getTranslationLanguages();
foreach ($languages as $langcode => $language) {
  // The object returned by getTranslation() behaves the same way as $node.
  $translation = $node->getTranslation($langcode);
  $titles[$langcode] = $translation->title;
}

Gábor Hojtsy

8 years 1 month ago

In reply to getCurrentLanguage() returns an object not an ID by Marc Isaacson (not verified)

you are right, fixing

You are right, fixing this now.

Kari Kääriäinen (not verified)

7 years 11 months ago

Use $translation->getTitle(); to get title string

In the first example "... collect titles... $titles[$langcode] = $translation->title...", $translation->title is an object (Drupal\Core\Field\FieldItemList). If you want to get the actual title ('About us'), use $translation->getTitle();

Dhara (not verified)

7 years 8 months ago

Not working with update

I want to update my title field
using this code : $node->getTranslation('es')->setTitle('Spanish title')->save();

It is not working, execution gets stopped. Please help

Gábor Hojtsy

7 years ago

In reply to Not working with update by Dhara (not verified)

what exactly is stopped?

What exactly is stopped?

Greg Sims (not verified)

7 years 1 month ago

Language of Node

This is a great reference page -- thanks!

Is it possible to determine the current language of $node? Please consider the following:

$node = Node::load($nid);
foreach(['en','es'] as $lang) {
$node = $node->getTranslation($lang);
service_function($node);
}
function service_function($node) {
$lang = $node->?????; // determine the language of the node from the caller
}

The alternative is to pass the $lang of the $node as a parameter.

Thanks, Greg

Gábor Hojtsy

7 years ago

In reply to Language of Node by Greg Sims (not verified)

language method

$node->language()

a.genchev (not verified)

6 years 4 months ago

Nice article, but how to use this to draw the content ?

I mean in D8 (at least my version) the things are getting drawn not with PHP but with sth. called twig. Hense, the multilang problems are not limited to the PHP code but extend over this (newly?) invented language . If I need something drawn I need to fetch the lang code and translations in twig, not in php (unless I went headless mode). Do you have an idea how to do that in the right way ?

Gábor Hojtsy

6 years 4 months ago

In reply to Nice article, but how to use this to draw the content ? by a.genchev (not verified)

twig should be concerned with markup

Twig was not (newly) invented in Drupal, we just adopted it. Twig should be receiving the content in the proper translation based on other environment factors (eg. URL, views filters, etc). You should not place logic about language loading in your twig templates.

Multilingual Drupal 8

  • Introduction
  • Language first
  • Core modules
  • Simple language setup, optional English
  • Highly flexible detection options
  • Almost limitless language assignment
  • Easier right to left styling
  • Blocks and views
  • Transliteration
  • Deployment friendly automated downloads
  • Context specific text translation APIs
  • String customizations tracked
  • English can now be translated to
  • Much improved software translation UI
  • Intro to content and configuration
  • Configuration translation basics
  • Configuration translation development
  • Content translation basics
  • Content translation development
  • Core content translation workflow
  • Combination use cases with content and menus
RSS feed

Content, unless noted otherwise is licensed under Creative Commons BY-NC 4.0. Feel free to copy, remix, redistribute and build upon my content, with proper attribution. Hosted at Acquia

Powered by Drupal