<blockquote>
  <p>The part that I'm not "getting" is I think there should be one Element in craft_elements and then for each localization, there should be one record in craft_elements_i18n yes? </p>
</blockquote>

<p>Yes.</p>

<blockquote>
  <p>And then for each localization, there should be a separate record in my craft_seomatic_meta table for each localized version yes?</p>
</blockquote>

<p>Only if the values in that table need to be stored on a per-locale basis, which to my knowledge has never been the case until now with SEOmatic.</p>

<blockquote>
  <p>When I call $result = craft()->elements->getElementById($metaId, 'Seomatic_Meta', $locale); how does it "know" which record to look up to load the returned Seomatic_MetaElementType with data?</p>
</blockquote>

<p>Any time you run an element query (which <code>getElementById()</code> does), the element type’s <a href="https://craftcms.com/classreference/elementtypes/IElementType#modifyElementsQuery-detail"><code>modifyElementsQuery()</code></a> method will be called, which gives the element type a chance to make changes to the query. In <a href="https://github.com/khalwat/seomatic/blob/master/elementtypes/Seomatic_MetaElementType.php#L156">yours</a>, you have the following code:</p>

<pre><code>$query
    -&gt;addSelect('seomatic_meta.*')
    -&gt;join('seomatic_meta seomatic_meta', 'seomatic_meta.elementid = elements.id')
    -&gt;andWhere(DbHelper::parseParam('seomatic_meta.locale', $criteria-&gt;locale, $query-&gt;params));
</code></pre>

<p>That condition you’re adding to the query on the <code>seomatic_meta.locale</code> column is what is ensuring that the correct localized row in your <code>seomatic_meta</code> table will be included in the result.</p>

<p>That’s just for <em>selecting</em> elements though. I might be missing something, but looking at your <a href="https://github.com/khalwat/seomatic/blob/master/services/SeomaticService.php#L683">SeomaticService::saveMeta()</a> method, it doesn’t look like you’re ever ensuring that all of the localized rows are actually getting saved. If you take a look through <a href="https://craftcms.com/classreference/services/ElementsService#saveElement-detail">ElementsService::saveElement()</a>, you will see that one of the things it does is loop through the locales returned by <code>$element-&gt;getLocales()</code> and make sure that there’s an ElementLocaleRecord (elements_i18n row) for the element in each of those locales. If you want to store rows for each locale in your own craft_seomatic_meta table, it’s the plugin’s responsibility to do that, not Craft’s.</p>

CC BY-SA 3.0
2016-09-01T23:41:59.627Z
@BrandonKelly - Above you say that we can add support for grabbing a particular locale using the `modifyElementsQuery()` method.  You point to the example coding adding an `andWhere` clause to do so. In looking at the `EntryElementType` and `CategoryElementType`, neither of those Elements seems to be limiting the result by locale in the `modifyElementsQuery()` method.  Why are the above example and the Entry and Category Elements different? What am I missing?
Ben Parizek
CC BY-SA 3.0
2016-09-02T12:29:03.887Z
@BenParizek Because none of the built-in element types need to store data on a per-locale basis, besides whatever’s in `elements_i18n` and `content` (and those tables are already joined in before modifyElementsQuery() is even called).
Brandon Kelly
CC BY-SA 3.0
2015-12-24T08:22:33.847Z
I was planning to store different data on a per-locale basis, just as I do with my regular old records.  You saying that no one else has needed/wanted to do this makes me think that I'm doing something wrong?

Should I not be storing this data in my own table?  The "normal" way to do this would be to make a FieldType, and just have Craft handle the gory details, storing it in `craft_content`?

I feel like perhaps I'm doing something wrong, if it's something no one else is doing.
andrew.welch
CC BY-SA 3.0
2015-12-24T15:46:55.510Z
@khalwat No to be clear I think you’re going about it the right way. This is just the first plugin (that I can think of) that needs to provide fields that store _content_, but not via custom fields.
Brandon Kelly
CC BY-SA 3.0
2015-12-26T05:32:48.387Z
@khalwat: If it makes you feel better, I've recently gotten the feature request to make Smart Map's "Address" field translatable... But Brandon's right, AFAIK you're the first plugin dev to actually _try_ it. This is all great info, thanks to both of you guys!
Lindsey D
CC BY-SA 3.0
2015-12-23T18:37:31.983Z
2015-12-23T18:37:31.983Z
Brandon Kelly
<p>My question is re: localization of ElementTypes in a plugin.  I have the records all working fine in multiple locales.</p>

<p>The part that I'm not "getting" is I think there should be one Element in <code>craft_elements</code> and then for each localization, there should be one record in <code>craft_elements_i18n</code> yes?  And then for each localization, there should be a separate record in my <code>craft_seomatic_meta</code> table for each localized version yes?</p>

<p>This is where in my controller it saves the ElementType: <a href="https://github.com/khalwat/seomatic/blob/master/controllers/SeomaticController.php#L173" rel="nofollow">https://github.com/khalwat/seomatic/blob/master/controllers/SeomaticController.php#L173</a></p>

<p>And here is the function is calls in my service to save the element &amp; record: <a href="https://github.com/khalwat/seomatic/blob/master/services/SeomaticService.php#L683" rel="nofollow">https://github.com/khalwat/seomatic/blob/master/services/SeomaticService.php#L683</a></p>

<p>When I call <code>$result = craft()-&gt;elements-&gt;getElementById($metaId, 'Seomatic_Meta', $locale);</code> how does it "know" which record to look up to load the returned <code>Seomatic_MetaElementType</code> with data?</p>

<p>I'm clearly missing something basic regarding how this should all work</p>

<plugin-development><localization>
Localized ElementTypes in a plugin
# Localized ElementTypes in a plugin

(Asked by: username_0 on 2015-12-23)

My question is re: localization of ElementTypes in a plugin. I have the records all working fine in multiple locales.

The part that I'm not "getting" is I think there should be one Element in `craft_elements` and then for each localization, there should be one record in `craft_elements_i18n` yes? And then for each localization, there should be a separate record in my `craft_seomatic_meta` table for each localized version yes?

This is where in my controller it saves the ElementType: <https://github.com/khalwat/seomatic/blob/master/controllers/SeomaticController.php#L173>

And here is the function is calls in my service to save the element & record: <https://github.com/khalwat/seomatic/blob/master/services/SeomaticService.php#L683>

When I call `$result = craft()->elements->getElementById($metaId, 'Seomatic_Meta', $locale);` how does it "know" which record to look up to load the returned `Seomatic_MetaElementType` with data?

I'm clearly missing something basic regarding how this should all work

**Tags:** plugin-development, localization

---

## 1 answers

### Answer by username_1 on 2015-12-23. Score: 5

The part that I'm not "getting" is I think there should be one Element in craft_elements and then for each localization, there should be one record in craft_elements_i18n yes? 

Yes.

And then for each localization, there should be a separate record in my craft_seomatic_meta table for each localized version yes?

Only if the values in that table need to be stored on a per-locale basis, which to my knowledge has never been the case until now with SEOmatic.

When I call $result = craft()->elements->getElementById($metaId, 'Seomatic_Meta', $locale); how does it "know" which record to look up to load the returned Seomatic_MetaElementType with data?

Any time you run an element query (which getElementById() does), the element type’s modifyElementsQuery() method will be called, which gives the element type a chance to make changes to the query. In yours, you have the following code:
$query
    ->addSelect('seomatic_meta.*')
    ->join('seomatic_meta seomatic_meta', 'seomatic_meta.elementid = elements.id')
    ->andWhere(DbHelper::parseParam('seomatic_meta.locale', $criteria->locale, $query->params));

That condition you’re adding to the query on the seomatic_meta.locale column is what is ensuring that the correct localized row in your seomatic_meta table will be included in the result.
That’s just for selecting elements though. I might be missing something, but looking at your SeomaticService::saveMeta() method, it doesn’t look like you’re ever ensuring that all of the localized rows are actually getting saved. If you take a look through ElementsService::saveElement(), you will see that one of the things it does is loop through the locales returned by $element->getLocales() and make sure that there’s an ElementLocaleRecord (elements_i18n row) for the element in each of those locales. If you want to store rows for each locale in your own craft_seomatic_meta table, it’s the plugin’s responsibility to do that, not Craft’s.

> **Comments:**
> **username_0**: I was planning to store different data on a per-locale basis, just as I do with my regular old records.  You saying that no one else has needed/wanted to do this makes me think that I'm doing something wrong?  Should I not be storing this data in my own table?  The "normal" way to do this would be to make a FieldType, and just have Craft handle the gory details, storing it in `craft_content`?  I feel like perhaps I'm doing something wrong, if it's something no one else is doing.  
> **username_1**: @khalwat No to be clear I think you’re going about it the right way. This is just the first plugin (that I can think of) that needs to provide fields that store _content_, but not via custom fields.  
> **username_3**: @khalwat: If it makes you feel better, I've recently gotten the feature request to make Smart Map's "Address" field translatable... But Brandon's right, AFAIK you're the first plugin dev to actually _try_ it. This is all great info, thanks to both of you guys!  
> **username_2**: @BrandonKelly - Above you say that we can add support for grabbing a particular locale using the `modifyElementsQuery()` method.  You point to the example coding adding an `andWhere` clause to do so. In looking at the `EntryElementType` and `CategoryElementType`, neither of those Elements seems to be limiting the result by locale in the `modifyElementsQuery()` method.  Why are the above example and the Entry and Category Elements different? What am I missing?  
> **username_1**: @BenParizek Because none of the built-in element types need to store data on a per-locale basis, besides whatever’s in `elements_i18n` and `content` (and those tables are already joined in before modifyElementsQuery() is even called).  

---

