Drupal 8 Multilingual APIs - Lingotek

Transcription

2017Drupal 8Multilingual APIsBuilding MultilingualWebsites for the Entire WorldTable of ContentsDrupal 8 Multilingual APIs. 2Improving Upon Drupal 7. 3Drupal 8 APIs. 4The Four Drupal 8 Core Pillars for Multilingual. 5Language Setup. 5Interface. 9Content. 11Configuration Translation. 14Conclusion. 17

TH E T R ANSL AT IO N NE T W O R KDrupal 8 Multilingual APIsDrupal 8 offers better support formultilingual websitesLocalization strategy is gaining momentum becausecreating site content in a customer’s languagepersonalizes experience and improves engagement.That’s leading more organizations to prioritizemaking their Drupal websites multilingual.One big reason for its popularity is that Drupal8 provides better support for multilingual sites.The Drupal 8 Multilingual Initiative made that apriority for this release. The power of Drupal 8 liesin its ability to assign languages to any contentor configuration, along with the improvementsin the APIs and user interfaces for localizing andtranslating your site.In his State of Drupal presentation at DrupalConNew Orleans, Dries Buytaert listed all the newfeatures that Drupal 8 offers that are contributingto its success: Mobile optimizationModern PHP standardsConfiguration managementBetter cachingWeb services APIs100 languagesand 200 more features2

TH E T R ANSL AT ION NE T W O R KImproving Upon Drupal 7Drupal 8 simplifies multilingualmanagement. It’s more deeplyintegrated, which leads to moreautomation, more seamlessworkflows, & better publicationmanagement.Creating a truly multilingual site in Drupal 7 has beena real challenge for Drupal developers. It requiresnavigating several options and building a strategy forrolling out a multilingual site that has proven to be anextremely time-intensive process for developers.For language support in Drupal 7, if you haveseveral modules and several languages, youneed the localization update module for avoidingdownloading and importing several PO files forproviding interface translations. And if you want totranslate a full site, you soon will end up requiringa large number of extra modules, and the potentialcombination of different settings and installedmodules can be hard to build and maintain.Drupal 7 is a very stable and well-used platformand it supports a vast array of use cases, but itwasn’t built with multilingual in mind, so it earneda reputation for being complex for multilingualsites. The core has limited support for localization,so you need a lot of contributed modules to makea site multilingual ready. Locale is in core and youcan translate your interface with it, but soon youneed other contributed modules like localizationupdate (l10n update), the internationalizationsuite (i18n), the variable module, or entity translation,to name a few.The Drupal community went to work to rebuildlanguage support in Drupal, so that everything inDrupal 8 understood language from the beginning.The result is that most custom or contributed modulesor themes don’t really have to think about languagesupport – it’s already built in or really easy to support.DRUPAL 7 MULTILINGUAL:3

TH E T R ANSL AT IO N NE T W O R KDrupal 8 is a great platform to work with,not only because it is multilingual capableout-of-the-box, but also because youcan easily expand while maintaining thetranslatability of your data.Drupal 8 is a modern platform, and thanks tosemantic versioning, it is quickly iterating with thelatest technology trends. You won’t have to waitseveral years for a new feature release. Contenton Drupal 8 is centered around entities, whichsimplifies multilingual management. Multilingualis more deeply integrated with core, which pavesthe way for more automation, more seamlessworkflows, and better publication management.Four pillars support building multilingual sites–language setup, interface translation, contenttranslation, and configuration translation–makingmultilingual sites in Drupal easier than ever.Drupal 8 is a great platform to work with, notonly because it is multilingual capable out-of-thebox, but also because you can easily expand whilemaintaining the translatability of your data.Drupal 8 APIsEvery Drupal developer who works withcontributed or custom modules must be awareof the requirements for providing a solutionthat is multilingual ready. Drupal themers alsoneed to be aware of what is needed for makingtheir templates translation-ready.In this guide, we’ll look at the most importantAPIs and best practices to ensure that yourproject is multilingual-ready.4

TH E T R ANSL AT ION NE T W O R KThe Four Drupal 8 Core Pillarsfor MultilingualLanguage Setup, Interface Translation,Content Translation & Configuration TranslationFOUR PILLARS IN DRUPAL 8:LANGUAGEINTERFACECONTENTCONFIGBase services for allmodules dealing with data.Not just multilingual.Interface translation hasbuilt-in update feature,improved usability.Field translation in builtin API for all entities.Content translation moduleprovides user interface.Common configurationsystem handles blocks,views, field settings.Unified translation.Language SetupIn Drupal 7, using the locale module allows you toadd languages to your site and show a translatedinterface. This functionality was split in Drupal 8,so the language module only provides the ability ofmanaging languages, and Drupal core already takescare of dealing with data and which language yourdata is in. It’s useful not only for multilingual sites,but also for monolingual sites that can install inanother language, track which language your sitedata is on, and you can add more languages later.DEALING WITH LANGUAGEIn Drupal 8, there is already a LanguageManagerservice in core without the need forinstalling the language module. Calling the \Drupal::languageManager() method returns a5

TH E T R ANSL AT ION NE T W O R KLanguageManager object–a service implementingLanguageManagerInterface that you can use fortracking and managing your language. If you installthe language module, this same call will returna ConfigurableLanguageManager service. Theyshare the same interface, so you can call the samemethods on them. You don’t need to know if youhave a multilingual site or a monolingual site whenyou are dealing with APIs from your modules.DEALING WITH LANGUAGESo for knowing which languages are available on asite, our LanguageManager has the getLanguages()method. If you don’t have the language moduleenabled, this call will return three languages: thedefault which in Drupal 8 is English (EN: English(Default)), and two special languages: UND: NotSpecified, and ZXX: Not Applicable. In Drupal 7,we had a special language–UND–which wasconfusing for users and it was not clear where itshould be used. In Drupal 8, to make things clearer,there is an additional one–Not Applicable.If you navigate to the Languages list in theDrupal 8 administration interface, you will findout that the languages Not Specified and NotApplicable are not listed, even when you canpick them in the language selection of contentcreation, for example. As special languages,they are locked, so you cannot edit them via theinterface. When you export your configurationincluding your configurable languages, you willsee that they are exported and have a “locked”property set to TRUE; for your regular languages,this property will be FALSE.If you don’t know the language of the data you aredealing with, you can assign Not Specified. Forexample, this can be useful if you are importingdata from external sites that don’t track languages.If the language doesn’t really apply, you can useNot Applicable. For example, you may be uploadingan image and the image has no text, so it doesn’tmake sense to assign a language in that case.In Drupal 8, if you install the language module, youcan use more languages.If you install the language module, languages onyour site will be configurable, so you can edit them,add them, or edit their attributes. Configurablelanguages are config objects, so you can exportthem and import them between different sites orenvironments like you would do with any otherconfiguration element in Drupal 8. They are savedlike any other config entities in the system and youcan export them to a YAML file.6

TH E T R ANSL AT IO N NE T W O R KHOW TO CREATE LANGUAGESIf you have the language module installed, youcan use the ConfigurableLanguage class, whichhas a create method. If you want to use the 100or so standard languages that come with Drupalyou don’t want to provide their details, you onlyhave to provide the language code for creatingthem, by using the ConfigurableLanguage::createFromLangcode method, which will return aConfigurableLanguage instance object with properdefault names and writing direction for thatlanguage, and then you only need to call save() onthat instance. But if you want to define your ownlanguages or locales, you can use the constructorof this class providing your custom attribute valuesand save it the same way.DEALING WITH ��fr’)- save()TRANSLATING THE USER INTERFACEIn Drupal 8, interface translation has a built-inupdate feature and usability has been improved alot when compared to Drupal 7. In terms of APIs,you are probably familiar with the t()-function usedin Drupal 7. It is still there and in short–that’s allyou need to know about how to translate your userinterface. There has been no change externally, butinternally, it’s very different. There are better waysfor making your user interface texts localizable whilehaving a proper testable and object-oriented code.In Drupal 8, you can perform any CRUD (Create,Read, Update, Delete) operation over yourlanguages using the APIs. As configurablelanguages are config entities, the API will lookexactly the same as the API you would use whendealing with, e.g., node types.At this point, we need to introduce the conceptof Dependency Injection, which is widely used inDrupal 8. While coding Drupal 7 modules, yourcode will contain logic that may need to knowthe language preference of a user visiting yoursite, accessing the site settings for knowing thelanguages available, and translate your userinterface messages. For that, you could call methodsfor dealing with users, the configuration of the site,and translating texts into the right language.If you need to know which language is currentlybeing used, you can use the LanguageManagerservice’s getCurrentLanguage() method, and it willreturn the language object being used. This allowsthe developer to ignore any details about howlanguage negotiation is configured at this point,making it simpler to deal with multilingual sites.To sum it up: in Drupal 8, languages can beconfigurable. You can manage them via the APIs,performing CRUD operations on them. You canalso get the language that is being negotiatedon the page without knowing the details of howit happened, and you can use the standardlanguages provided by Drupal core or createyour own custom languages.7

TH E T R ANSL AT ION NE T W O R KDEPENDENCY INJECTIONWith the Dependency Injection pattern, instead ofcalling a globally available method, you can injectand pass through whatever dependencies areneeded. You can keep in your services a referenceto other service instances your code depends on.The good thing about Dependency Injection is thatyou can switch them if you have an interface. Whenyou need to know some data about the user, thereis a method for that. You don’t need to know thebackend system being used. For example, whenyou are integrating with an external system andit provides the user, you don’t need to know aboutthe details. You know that there is a contract andthe service will use that contract; you know whichmethods there will be, and you don’t need to knowwhat they are doing.8

TH E T R ANSL AT ION NE T W O R KInterfaceYou don’t need to write different code based onwhether there is a translation system in place ornot. The LanguageManager can be different ifyou are on an English only site or if you have thelanguage module installed.INTERFACE LANGUAGE this- t(‘English text’)- getOption(‘langcode’);There needed to be a way to provide thistranslation service; a way that it can be switched.For that, you will use this t()-method, and theswitchability is hidden behind that. If you arecreating a controller or a form, you are probablyextending from base classes that are available inDrupal 8 core, like FormBase. It will already havea t()-method that you can use. It will work with theproper translation service in turn.This t()-function is internally quite different fromDrupal 7. When you call t() in Drupal 7, youget a string translated just in time. In Drupal8, the translation itself is deferred and youare actually getting an object. This object is aTranslatableMarkup object that wraps the originalstring and you can call methods on it. You canretrieve the langcode option (- getOption) usedto set up the object and several further methodsare available to gather data about this translationobject. This way, text can be translated later,before the output is actually created.INTERFACE LANGUAGEformat plural(.) this- formatPlural(.)In Drupal 7, if you had a t() call to return atranslation, if a form alter removed it later, weare not really outputing that string. But Drupalalready spent time translating it. Here in Drupal8, if it’s not going to be rendered, you are notreally translating it, so at the end, you may evenmake the performance of your site better. Thetranslation is only done when you are going torender the page.If you are creating your own class that doesn’textend from a base class that already has translation,you can apply the use StringTranslationTraitstatement and this trait will automatically provideyou the t()-method and the setter method for thetranslation service.In addition, format plural() from Drupal 7 is nowgone in Drupal 8. There is this- formatPlural(.)for that and it will work a similar way. You get anobject that you can actually alter to do whateveryou need.INTERFACE LANGUAGEclass Foo {use StringTranslationTrait;.}9

TH E T R ANSL AT ION NE T W O R KJAVASCRIPT APIIf you are doing JavaScript, it didn’t change much fromDrupal 7 for translation. You still have the Drupal t()method and the Drupal formatPlural() method.Drupal.t(.)Drupal.formatPlural()TWIG TEMPLATESFor templates, Drupal 8 uses Twig. In the PHPtemplates of Drupal 7, you would call t() on yourtranslatable strings, but in Drupal 8, that isnot available anymore. There are two differentmethods for translating your strings in yourtemplates. The first one is the trans filter. If youhave a string, you can pass it through the transfilter, and it will provide the proper translation.*LINKS.MENU.YML FILESAnother change is around menu items. In Drupal7, you had your hook menu() where you had yourroute, your menu links, your tasks–everything was inthere. Now in Drupal 8, it has been split. These areall located in YAML files. They are separated, so youhave your menu links in one place, and your routingis in a different place. It’s a more flexible system.In Drupal 7, you couldn’t call the t() function foryour title and description in your hook menu().This lets you cache your links and translate themlater to other languages.LOCALE.LINKS.MENU.YMLlocal.translate page:title: ‘User interface translation’description: ‘Configure the import .’route name: locale.translate pageparent: system.admin config regionalweight: 15Like in the t() function in Drupal 7, you can passthe context. If you want to request a translationinto a specific language other than the interfacelanguage, you need to specify that language.It is easiest to do that with the trans block. Allthe text between the trans and endtrans tagswill be used for calling the t() function internally.You can also have variables similar to those inyour PHP code.INPUT – SEARCH.HTML.TWIG input {{ attributes }}placeholder ”{{ ‘Search’ trans }}” / NODE.HTML.TWIG div{{ author attributes }} {% trans % }Submitted by {{ author name }} on {{ date }}{% endtrans %} /div locale.translate status:title: ‘Available translation updates’route name: locale.translate statusdescription: ‘Get a status report .’parent: system.admin reportsIn Drupal 8, it’s pretty much the same. They areYAML files, so you cannot call any function onthem, but there are some special keys (in this casetitle and description) that Drupal 8 will take careof translating. The potx.module will take care ofextracting them and making them available fortranslation on localize.drupal.org as well.In summary, the interface translation moduleprovides the ability to translate from English intoany other language.10

TH E T R ANSL AT ION NE T W O R KContentThe content translation module allows you totranslate all of your content, not only nodes andtaxonomy terms, but also any kind of entity that youcan have on your site. If you have Drupal commerce,it can translate your products and provides the userinterface for actually translating them.DEFINING TRANSLATABLE CONTENTThe content translation module is not based oncopies of your nodes or your entities. Drupal 8 hasclever objects that know about them. If you arecreating a ContentEntityTypes, Drupal 8 will makeit easier to translate them.NODE.PHP (SNIPPET)/*** Defines the node entity class.* @ContentEntityType(* id “noted”,* label @Translation(“File”),* translatable TRUE,* entity keys {* “id” “nid”,* “label” “title”,* “langcode” “langcode”,* “label” “title”,* }*)in. You need to define the langcode in the entitykeys. By doing this, you are telling Drupal thatthese entities may be configured as translatable.That doesn’t mean that all of your nodes can betranslated, it means that the user can go to thecontent translation configuration interface and setup if they want to translate them or not.DEFINE THE LANGCODE BASE FIELDIn Drupal 8 as in Drupal 7, we have two differentkind of fields: the first one are the fields that aredefined as part of the entity, and the second arethe fields that you can configure (add and remove)on the interface of the entity.If you are starting from ContentEntityBase, whencreating the content entity (which you should do),you have to define the base field definitions. Itprovides the base properties that every entity ofthis kind should have.CONTENTENTITYBASE.PHP (SNIPPET)function baseFieldDefinitions( entity type) {// .if ( entity type- hasKey(‘langcode’)) { fields[ entity type- getKey(‘langcode’)] BaseFieldDefinition::create(‘language’)- setLabel(new TranslatableMarkup(‘Language’))- setDisplayOptions(‘view’, [‘type’ ‘hidden’,For creating a ContentEntityType, you need to createa class, in a special namespace in your module andadd an annotation, which describes the metadata.This defines your content entity.])}}In annotations, you can use @Translation() so yourlabel will be extracted and you can translate yourlabels on your UI. For making the content possibleto configure as translatable, you only need to specifytranslatable TRUE. If you want to translate yourentities, you need to know which language they are11

TH E T R ANSL AT IO N NE T W O R KDEFINE TRANSLATABLE BASE FIELDSIt already has the langcode for creating the field foryou if you defined the langcode ket in the entity keys.For your additional base fields, ensure that you callthe parent::baseFieldDefinitions() method to get thecommon fields and then add your own fields. Foryour own fields, you also have a method for makingthem translatable, or rather as a field possible toconfigure for translation.Like images, other fields may be composed of differentproperties. You may also want to provide differenttranslatability options for them. In the case of animage, you may not want to translate the image itself,but you do want to translate the alternative text in thetitle. To make this possible, you can create groups ofMULTICOLUMN FIELD SNIPPET* @FieldType(* id “image”,* column group {* “file” {*“label” @Translation(“File”),*“columns” {*“target id”, “width”, “height”*},* },* “alt” {*“label” @Translation(“Alt”),*“translatable” TRUE* },* }NODE.PHP (SNIPPET)function baseFieldDefinitions( entity type) { fields parent::baseFieldDefinitions( entity type);// . fields[‘title’] BaseFieldDefinition::create(‘string’)- setLabel(t(‘Title’))- setRequired(TRUE)- setTranslatable(TRUE);}If you call setTranslatable(TRUE) method on a basefield, Drupal will know about its translatability. Theuser can also configure it differently then. If youmake an entity translatable, any field that has thisflag will be translatable by default.data columns. In this example, a file group is createdand the columns of this group–”target id”, “width”,“height”– are part of this group. An alternativetext group is also created where we are saying“translatable” TRUE.FIELD TYPES THEMSELVESIf you are creating your own field types, how dowe make them translatable? There is nothing youneed to do. They will be translatable automatically.Drupal 8 knows how to make them translatable,but for some kind of fields you may want to makemore granular options.This provides sane defaults for when you areconfiguring your site, so when you check translatingan image file and an image field in an entity, youwill see by default that you can provide differentconfigurations for the file itself, for alternative textand for the title (not represented here for brevity).And by default, the alternative text and the title willbe translatable, but not the file itself. You can make ittranslatable if you want.MULTICOLUMN FIELDSTo define an image, you will have a reference toa file entity. You will need to know the width andthe height of the image, then you can define thealternative text and the title of the image.12

TH E T R ANSL AT IO N NE T W O R KENTITY LANGUAGE APIThe Entity Language API is quite straightforward.You have a node class for dealing with yournodes. You have the same kind for any entityyou create. You have a load() method on it, anda getTranslation() method to retrieve specifictranslations.ENTITY LANGUAGE API node Node::load(42); node node- getTransation(‘hu’); node entityRepository- getTranslationFromContext( node);You may not know which translation you want. Ifall you know is you want a translation appropriatefor this request, use the entity repository. Call thegetTranslationFromContext() method and pass thenode. What you get back is a node in the currentnegotiated language.There are a lot useful methods that we can usein our entities: node- getUntranslated()–gets us the sourcetranslation node- language()–the language of the currentloaded translation node- getTranslationLanguages()–the list ofthe language objects for translations of the entity node- hasTranslation(‘hu’)–is there atranslation for a specific langcodeIf you call the getTranslation() method, you canrequest which translation you want to work withand you will get a reference to the translation.It’s the same node, so you can do any operationsyou want with it and then save it or edit the fieldsas you would with nodes in any other situation.Language here is quite easy to use and translationsfrom these nodes are quite easy to access. node- addTranslation(‘hu’)–for creating it node- removeTranslation(‘hu’)–for removing itThe best thing is this does not only apply to nodesbut also other content entity types, such as users,comments, taxonomy terms, etc.ENTITY LANGUAGE API node- getUntranslated() node- language() node- getTranslationLanguage() node- hasTranslation(‘hu’) node- addTranslation(‘hu’) node- removeTranslation(‘hu’)13

TH E T R ANSL AT ION NE T W O R KVIEWSViews has two key purposes: Creating queries onthe system, and then render it. You can see thisseparation in the first two columns in the Views UI.Language is integrated in both.You can filter by language. You can filter yourviews by a specific language, or you can filterby the language that was negotiated for thepage. On the rendering side, you can pick whichlanguages you want to render the found entitiesin. With Views, you can say give me all the nodesthat are in English and then render the Germantranslations. These are the two different ways todeal with language in Views. It’s quite powerful;you may not need the PHP APIs at all.Content translation provides the ability totranslate from any language to any otherlanguage. Your entities are intelligent objectsso you can actually call the methods on them–for getting the translations, for dealing withtranslations, editing translations, etc.Configuration TranslationWe have two kinds of configuration: configurationobjects for global settings and configurationentities for things that can have multiple instances,like configurable languages. Configuration is storedin YAML files with a langcode property.CORE.DATA TYPES.SCHEMA.YMLFor making our config translatable, you need toprovide a schema. It was introduced in Drupal 8 forknowing how to translate or deal with languagesin your configuration, but it is now used for a lotof other things. Drupal defines some base typessuch as config object which has a langcode. Globalconfiguration is usually typed as config object.There is another key data type for translatability,which is text. It is a string that is translatable.When these data types are properly used, a bigadvantage is Drupal knows to type-cast data to theright types, such as integers to numbers.14config object:type: mappingmappinglangcode:type: stringlable: ‘Language code’.text:type: stringlabel: ‘Text’translatable: true

TH E T R ANSL AT ION NE T W O R KTo create your own schemas, you need to create aschema YML file and then define the types of yourconfiguration elements. Here we are defining thesystem maintenance configuration. We are sayingthat it is a config object. This means that it willhave a langcode and you can define its furtherattributes. We know it’s a message of type text, sowe know it will also be a translatable string.CONFIGURATION API manager \Drupal::languageManager(); hu manager- getLanguage(‘hu’); original manager getConfigOverrideLanguage(); manager- setConfigOverrideLanguage( hu); config \Drupal::config(‘system.maintenance’);// . manager- setConfigOverrideLanguage( intenance:LANGCODEtype: config objectlabel: ‘Maintenance mode’mapping:TRANSLATABLEmessage:STRINGtype: textlabel: ‘Message to display.’Here is the message and langcode. When it’stranslated in configuration storage, there will be alanguage folder for each language with a langcodeas a name. There will also be a file with the samename. Only the translatable elements may show upin these files.SYSTEM.MAINTENANCE.YMLmessage: ‘@site is currently undermaintenance. We should be backshortly. Thank you for your patience.’langcode: enTo access the configuration of your site, use theDrupal config service. You pass the name of theconfiguration object that you want to retrieve andthen you can access the properties with the - get()method so we can get the message from the systemmaintenance object with - get(‘message’).If you have language overrides, they will apply asappropriate. If you have a settings PHP overridethat will also apply.Configuration with overrides applied is the mostcommon for settings used at runtime. Drupaluses the negotiated language for the page toinitialize overrides. If you know what languageoverrides you want to apply instead, you needto use the Language Manager and you can call getConfigOverrideLanguage() for storing thelanguage Drupal is currently using. Then you can seta different one M.YMLmessage: ‘@site karbantartås alatt all.’LANGUAGES/IT/SYSTEM.YMLmessage:’@site .’15

TH E T R ANSL AT ION NE T W O R KThis can be quite useful if you are sending emails.An admin may be visiting your site–your admininterface in English–but you want the usersthey are sending emails to to get emails in thelanguage they prefer. You can get the languagefrom the user object, set the config overridelanguage, then use the configuration with theappropriate language overrides applied forsending the email. Then you can set it back to theoriginal language Drupal was using before.We have three different ways for dealing withlanguages with Configuration.CONFIGURATION IDES APPLYAS APPROPRIATE\Drupal::configFactory() getEditable(‘system.maintenance’);NO OVERRIDESAPPLY\Drupal::languageManager()- getLanguageConfigOverride(‘hu’, ‘system.maintenance’)- set(‘message’, ‘Karbantartás.’)- save();THE e’) providesyour configuration with every override applied. Ifthere are competing overrides for a value, Drupal’spriority system is used to decide which override keyprevails. \Drupal::configFactory() getEditable isfor getting the current configuration in the originalraw format, so absolutely no overrides applied.This is useful for actually making configurationchanges. Finally \Drupal::languageManager() hasa method for getting a concrete config overrideitself: - getLanguageConfigOverride(). So you canwork with the overrides themselves, the raw configwithout overrides, or we can ask the system toapp

The Drupal 8 Multilingual Initiative made that a priority for this release. The power of Drupal 8 lies in its ability to assign languages to any content or configuration, along with the improvements in the APIs and user interfaces for localizing and translati