So far in this chapter, we've mostly talked about how to ensure that our modules output only text that can also be translated. The Drupal best practice is to always use these techniques regardless of whether the site is multilingual. You never know if you'll ever need to add a new language.
In this section, we are going to talk a bit about how we can interact with the language system programmatically and work with entity translations.
A potentially important thing you'll often want to do is check the current language of the site. Depending on the language negotiation in place, this can either be determined by the browser language, a domain, a URL prefix, or others. The LanguageManager is the service we use to figure this out. We can inject it using the language_manager key or use it via the static shorthand:
$manager = \Drupal::languageManager();
To get the current language, we do this:
$language = $manager->getCurrentLanguage();
Where $language is an instance of the Language class that holds some information about the given language (such as the language code and name). The language code is probably the most important as it is used everywhere to indicate what language a given thing is.
There are other useful methods with this service that you can use. For example, we can get a list of all the installed languages with getLanguages() or the site default language with getDefaultLanguage(). I encourage you to check out the LanguageManager for all the available API methods.
When it comes to content entities, there is an API we can use to interact with the data inside them in different languages. So, for example, we have figured out the current language with the previous method, so we can now get some field values in that language. The way this works is that we ask for a copy of the entity in the respective language:
$translation = $node->getTranslation($language->getId());
$translation is now almost the same as $node, but with the default language set to the one we requested. From there, we can access field values normally. However, not all nodes have to have a translation, so it's better to first check whether one exists:
if ($node->hasTranslation($language->getId())) { $translation = $node->getTranslation($language->getId()); }
Since we can configure entity translatability at the field level (allowing only the fields that make sense to be translated), we can also check which of these fields can have translated values:
$fields = $node->getTranslatableFields();
Finally, we can also check which languages there are translations for:
$languages = $node->getTranslationLanguages();
Since it's up to the editors to add translations to an entity, we cannot guarantee in code that one exists.
Programmatically, we can also create a translation to an entity really easily. For example, let's imagine we want to translate a Node entity and specify its title to be in French:
$node->addTranslation('fr', ['title' => 'The title fr']);
The second parameter is an array of values that needs to map to the entity fields just like when creating a new entity. Now the respective node has the original language (let's say EN) but also a French translation. It should be noted that the values of all the other fields apart from the title, even in the French translation, remain in the original language because we did not pass any translated values when creating the translation.
And just as we add a translation, we can also remove one:
$node->removeTranslation('fr');
If we want to persist the addition or removal of a translation, we need to save the entity like we are used to. Otherwise, it's stored only in memory. And with Drupal 8.3, content entities implement the Drupal\Core\TypedData\TranslationStatusInterface which allows us to inspect the status of the translations. So for example, we can do this:
$status = $node->getTranslationStatus('fr');
Where $status is the value of one of three constants from the TranslationStatusInterface class:
- TRANSLATION_REMOVED
- TRANSLATION_EXISTING
- TRANSLATION_CREATED