7

We have a plugin with custom Element Types that allows a user to create a field layout using custom fields.

We understand how to setup a localized site and add translation files. This question is specifically about how to add support for the user to be able to add content in multiple languages via the custom fields as they add new Entries to a Custom Element Type.

It would be nice to see a list of all the steps we need to consider. Ideally, the answer could double as the page we would have read in the documentation instead of posting this question =)

Update:

For reference, these are the tables that appear to have a locale column in the default Craft database.

  • craft_categorygroups_i18n
  • craft_content
  • craft_elements_i18n
  • craft_entrydrafts
  • craft_entryversions
  • craft_locales
  • craft_matrixblocks (ownerLocale)
  • craft_matrixcontent_body (and any other Matrix field's content table)
  • craft_routes
  • craft_searchindex
  • craft_sections_i18n
  • craft_templatecaches
2
+50

It all starts in Your ElementType where you need to return true on the isLocalized function (Line 62 in BaseElementType.php).

Setting this to true will tell Craft that data is stored per-locale, and it's local choices are based on your Element Model's getLocales function. You can get a basic example from Line 231 of BaseElementModel.php.

Craft's Entry Element actually allows you to choose which locale's are used on a per section basis. You can see an Entry gather its locales on Line 54 in EntryModel.php and Line 57 in SectionModel.php. A section stores its locale options in a related table via SectionLocaleRecord.

The rest should be handled by Craft so long as you set localeEnabled to true and locale to a locale ID in your model before passing it along to saveElement.

The rest of the work is templating, which you can reference how P&T do it in templates/entries/_edit.html.

  • Thanks Bryan. This is a good start. It seems a custom element type would also have to create/manage a second table for the locale such as the craft_sections_i18n or craft_categorygroups_i18n table. These tables appear to manage information that is specific to the element type and how the locales need to behave for it. For example, the craft_sections_i18n table has an enabledByDefault setting and the category groups table doesn't. – Ben Parizek Apr 6 '16 at 7:33
  • Hrm... Then I will dig deeper and correct my answer. – Bryan Redeagle Apr 6 '16 at 13:12
  • More topics worth explaining for a canonical answer would be what javascript is being used and how the element data is being handled when saving Elements. craft.js has a Craft.ElementEditor which includes a switchLocale method and calls elements/getEditorHtml and an updateForm method. How does the element save data for multiple locales? Does the saveEntry method happen once per locale? Or do all the fields and custom field inputs get namespaced when an entry is submitted? When I use the locale switcher for Entries, it appears the title field for both locales is name="title" – Ben Parizek Apr 6 '16 at 16:00
1

I don't believe myself to be an authority on this topic, nor this answer to be complete, but below I try to pull together a summary of several of the things you'll want to consider when adding localization to your plugins. Please correct me where I'm wrong and I'll try to make updates to this as I learn more until proper documentation exists.


Adding localization to your plugin will require addressing at least three different scenarios in the Control Panel:

  1. How are you saving your data in a localized way?
  2. How are you retrieving your data in a localized way?
  3. How are you managing the UI around switching between locales?

While there may be related settings, the primary thing that gets localized in Craft is the Element. As you localize your plugin you may need to ask: how does what I am trying to add localization support for relate to the Elements in the system?

Craft-managed tables that relate to localization

I believe Craft will manage the locale-specific behavior for several tables in the database for you:

  • craft_elements_i18n
  • craft_locales
  • craft_routes
  • craft_searchindex
  • craft_templatecaches

Element-specific tables that relate to localization

Each respective Element Type will need to manage and make sure locale-related data is saved to the tables that relate to that specific Element Type.

In the case of Entries, Entries manage locale-specific data in the following tables:

  • craft_content
  • craft_entrydrafts
  • craft_entryversions
  • craft_sections_i18n

Categories manage locale-specific data with different requirements:

  • craft_categorygroups_i18n
  • craft_content

Localization and Field Types

Many fields get added directly to Elements via Field Layouts. By default, fields store the data they save as a field-specific column on the content table that relates to the Element the field has been added to. An Entry With a Plain Text field named stuff will store it's data in the craft_content table with the column name field_stuff.

For fields that use this default method of storing their data, you do not need to add additional localization support. The craft_content table already has a locale column and populating that value will be handled by the Element that your field is assigned to.

For custom fields that are advanced enough to require a field-specific database table to store their data (consider an Address Field, perhaps), the plugin developer will need to add locale support (discussed in more detail below).


How are you saving your data in a localized way?

Elements

Craft does not do much to help you save your Element data in a locale-friendly way. You'll need to manage a lot of this on your own.

To start off, you'll want to give your Element an isLocalized method and return true. One class to read up on to understand a bit more about how localization works on Element Types is the IElementType Class.

While Craft appears to manage saving the locale data to craft_elements_i18n for you, it appears Elements that support locales also have a secondary i18n table that relates to something that I don't know what to call but tend to refer to as the Element Group. The Element Group for Entries is Sections, for Categories is Category Groups, for Craft Commerce Products is Product Types.

Entries

  • craft_sections
  • craft_sections_i18n

Categories

  • craft_categorygroups
  • craft_categorygroups_i18n

Commerce Products

  • craft_commerce_producttypes
  • craft_commerce_producttypes_i18n

It would seem that if your Element has URL support, and those URL Format settings are managed on the Element Group for that Element, you will need a similar i18n table in your plugin for your Element.

Advanced Fields

Advanced fields will need to:

  • Add a locale column to the database table that stores their field info
  • Populate that locale column while saving data to their custom table in the Field Types onAfterElementSave method. The locale ID can be found via $this->element->locale value in your Field Type class.

How are you retrieving your data in a localized way?

Elements

When Elements are retrieved from the database using an Element Query, the Element Types modifyElementsQuery() method will be called. This gives the Element a chance to make changes to the query and is where you will need to update the query to ensure that the locale is being considered.

This is discussed in this question here and seems to make sense, however, it doesn't appear to be used in the Entry or Category Elements and I cannot explain why.

Advanced Fields

  • Retrieve the locale-specific record from the database when retrieving the field data in the getInputHtml method. The locale ID can be found via $this->element->locale value in your Field Type class.

How are you managing the UI around switching between locales?

When a user switches between locales while editing an Entry a few things need to happen. Craft appears to manage this with entries in part by reloading the page which probably also means they are re-querying the database for the content specific to the locale of the page being loaded.

http://happylager.dev/admin/entries/news/slug-1/en_us
http://happylager.dev/admin/entries/news/slug-1/es

The switch may actually be triggered by javascript. craft.js has a class called Craft.ElementEditor which includes a switchLocale method and calls elements/getEditorHtml and an updateForm method. I don't know much more than that!

The input field values for the locale will need to be updated to the locale ID of the locale you want to save your data for:

<input type="hidden" name="locale" value="en_us">

You will want to give visual indications of which locale is in use as well. This may include the controls the user uses to switch between languages as well as pass the locale value to any form macros that should display a locale indicator.

{{ forms.textField({
    label: "Title"|t,
    locale: element.locale,
    name: 'title',
    value: element.title,
    errors: element.getErrors('title'),
}) }}

The locale attribute in the above macro will output a locale specific indicator in the label tag of the form: <span class="locale">{{ locale }}</span>.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.