This guide discusses creating location pages in the Interface Builder. This is the preferred method of creating Location Pages using the latest templating technology available from MetaLocator.
MetaLocator's SEO solution includes Directory Pages and Location Pages. The directory represents the hierarchy of pages for each geographic area. E.g. city level pages, state level pages and so forth. The Location Pages are the single pages created for each location in your MetaLocator data.
Setup
Initial setup and provisioning of the Location Pages product is done by the MetaLocator Team. Setup of the custom domain, DNS instructions and deployment are handled as part of our Enterprise setup process. This article provides the reference material necessary for the HTML implementation and maintenance of the location and directory pages after setup is complete.
Templates
There are 3 Twig templates involved in the creation of location pages as follows.
The Location Page Template. This is the template used to display each individual location page. This template is used to create the page that displays a single location.
The Directory Page Template. This is the template used to display the hierarchy of geographic pages, e.g. the city page, the state page and so forth.
The Website Template. This optional template, if provided, is wrapped around the location and directory page templates.
The Location Page Template
This is the template used to display each individual location page. This template is used to create the page that displays a single location.
The template attributes available to the location page can be "dumped" as shown below:
<pre><!-- this is a raw dump of the template variables -->
{{ template_item | json_encode(constant('JSON_PRETTY_PRINT')) }}
</pre>
The template_item contains the entire location object, including all custom fields. Some special fields include:
template_item.interface_name //the name of the Interface.
template_item.canonical // the canonical tag for the location page.
template_item.page_title // the title of the location
template_item.page_url // the url of the location page.
template_item.year // the current year
The Directory Page Template
This is the template used to display the hierarchy of geographic pages, e.g. the city page, the state page and so forth.
The template attributes available to the location page can be "dumped" as shown below:
<pre><!-- this is a raw dump of the template variables -->
{{ template_item | json_encode(constant('JSON_PRETTY_PRINT')) }}
</pre>
The Website Template
This allows a single template for the website's common header and footer for both the directory and location pages. However, if the template needs to be different between the location and directory pages, the Website template may be omitted (empty) and the Location Page and Directory Page templates will be responsible for the full page content. The following code must be present and indicates where the system will insert the Location Page and Directory Page after "binding" with the data:
{{ include(template_from_string(template_item.twig_page_template)) }}
The Website template is typically comprised of the HTML found on the client Website. The typical process for configuring the website template is described below:
Identify a page on the target Website that should be used for the location pages' header and footer. This is typically a "standard" page which includes the site's normal header and footer and includes a MetaLocator interface installed in the main content body. The page should have clean and valid HTML markup and be free of JavaScript and CSS errors.
Visit the page in a browser and "View source". Find the MetaLocator installation code and select all source code above the MetaLocator installation code.
Copy and Paste this code into the Website Template setting.
Insert the Twig template code snippet shown above. This code is responsible for inserting the MetaLocator location and directory page content.
Select all source code below the MetaLocator installation code.
Copy and Paste this code into the Website Template setting.
Insert the following code block just before the closing
</head>
tag:
{{template_item.canonical|raw}}
{{template_item.localschema|raw}}
{% for meta in template_item.meta %} {{ meta|raw }} {% endfor %}
{% for stylesheets in template_item.stylesheets %} {{ stylesheets|raw }} {% endfor %}
{% for scripts in template_item.scripts %} {{ scripts|raw }} {% endfor %}{% for href_lang in template_item.href_langs %} {{ href_lang|raw }} {% endfor %}
Update all relative URLs in the template to absolute URLs.
Update references to the Page Title to {{ template_item.page_title }}
Update references to the Page URL to {{ template_item.page_url }}
Other Twig programming may be required on a per Website basis, but these steps cover the essentials.
Categories
Categories can be displayed on the directory page, or at the location level.
Directory Page Example Category Snippets
On the directory page, looping through all available categories can be done as shown:
<ul>
{% for tag in template_item.ml_taglist %}
<li>{{tag.id}} - {{tag.name}} - {{tag.tag_description}} </li>
{% endfor %}
</ul>
Limiting the loop above to a specific category group can be done as shown below, where the loop only includes those in the category group "Sample". The comparison to tag_group_raw ensures that the data value is used, as opposed to any translations.
<ul>
{% for tag in template_item.ml_taglist|filter(t => t.tag_group_raw == "Sample")%}
<li>{{tag.id}} - {{tag.name}} - {{tag.tag_description}} </li>
{% endfor %}
</ul>
On the directory pages, some of which display location details, category data can also be displayed. In those contexts, each location object includes a list of the category IDs (called tag IDs internally) associated with the location. To display category meta-data such as category links, descriptions and names, the category ID should be used to look up the category details as shown below:
<h3 class="text-center">Categories</h3>
{% for tagid in location.tagidlist %}
<div class="card h-100">
<div class="card-body">
<h6 class="card-title">{{ template_item.ml_taglist[tagid].name }}</h6>
{% if template_item.ml_taglist[tagid].tag_image %}
<img alt="{{ template_item.ml_taglist[tagid].name }}" class="card-img-top" src="{{ template_item.ml_taglist[tagid].tag_image }}" />
{% endif %}
</div>
<div class="card-footer">{% if template_item.ml_taglist[tagid].category_link %} <a class="btn btn-primary btn-sm" href="{{ template_item.ml_taglist[tagid].category_link|raw }}">More Info</a> {% endif %}</div>
</div>
{% endfor %}
Location Page Example Category Snippets
Location pages commonly display category details for each location. This is often in the form of products and services lists, which can include rich details from the category links and descriptions.
A typical category display follows:
<h3>Categories</h3>
{% for tagid in template_item.location.tagidlist %}
{% if template_item.ml_taglist[tagid].tag_image %}<img alt="{{ template_item.ml_taglist[tagid].name }}" class="card-img-top" src="{{ template_item.ml_taglist[tagid].tag_image }}" />{% endif %}
<h6 class="card-title">{{ template_item.ml_taglist[tagid].name }}</h6>
<div class="card-text">{{ template_item.ml_taglist[tagid].description|raw }}</div>
{% if template_item.ml_taglist[tagid].category_link %} <a class="btn btn-primary btn-sm" href="{{ template_item.ml_taglist[tagid].category_link|raw }}">More Info</a> {% endif %}
{% endfor %}
The above example loops over the "tagidlist". However to filter the loop to a specific category group, the following example shows the same lookup strategy to filter by "tag_group_raw", which is the untranslated, stored value of the category group
<h3>Installers</h3>
<ul>
{% for tagid in template_item.location.tagidlist|filter(t => template_item.ml_taglist[t].tag_group_raw == "Installers") %}
<li>{{ template_item.ml_taglist[tagid].name }}</li>
{% endfor %}
</ul>
Breadcrumbs
Breadcrumbs can be displayed on the Directory and Location pages.
An example breadcrumb for the directory pages:
<!-- typical breadcrumb markup for a directory page -->
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">All Facilities</a></li>
{% if template_item.current_level == "state" %}
<li class="breadcrumb-item">{{template_item.data[template_item.current_value].label}}</li>
{% endif %}
{% if template_item.current_level == "city" %}
<li class="breadcrumb-item"><a href="../">{{template_item.locations[0].ml_fullstatename}}</a></li>
<li class="breadcrumb-item">{{template_item.current_value}}</li>
{% endif %}
</ol>
</nav>
And the location pages:
<!-- typical breadcrumb markup for a location page -->
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">All Facilities</a></li>
<li class="breadcrumb-item"><a href="../../">{{template_item.location.ml_fullstatename}}</a></li>
<li class="breadcrumb-item"><a href="../">{{template_item.location.city}}</a></li>
</ol>
</nav>
Location Hours
<!-- this is the green/yellow/red status icon -->
<span data-metalocator-timezone="{{template_item.location.time_zone}}" data-metalocator-hours="{{template_item.location.hours}}"></span>
<!-- this is the word open or closed -->
<span data-metalocator-timezone="{{template_item.location.time_zone}}" data-metalocator-hours="{{template_item.location.hours}}"></span>
<!-- this is the "opens today at 9", or "open today from 9-5" etc. -->
<span data-metalocator-timezone="{{template_item.location.time_zone}}" data-metalocator-hours="{{template_item.location.hours}}"></span>
<!-- this is the full hours table -->
{{template_item.location.hours_formatted|raw}}
Location Reviews
This example includes the HTML and data elements required to construct a full reviews display similar to the below.
<!-- reviews -->
{% if template_item.location.review_count > 0 %}
<section class="ml-review-section">
<div class="ml-container">
<span data-element-name="review_1" class="ml_field_type_container_review ml_field_name_container_review ml_field_template_container_review_details ml_field_instance_container_review_1">
<div data-toggle="tooltip" data-placement="top" title="" class="ml_review ml_review ml_review_details " data-original-title="">
<h4 class="ml-review-title">REVIEWS</h4>
<span class="ml_review_details_wrapper">
<div class="ml_review_details_avgrating">
<div class="ml_review_avgrating">{{template_item.location.review_average|number_format(1)}}</div>
<div class="ml_review ml_review ml_review_stars">
{% for r in 1..template_item.location.review_average|round(0, 'floor') %}
<img src="star-full-primary.svg" alt="full star" />
{% endfor %}
{% if template_item.location.review_average - template_item.location.review_average|round(0, 'floor') > 0 %}
<img src="star-half-primary.svg" alt="star" />
{% endif %}
{% if template_item.location.review_average|round(0, 'ceil') < 5 %}
{% for r in template_item.location.review_average|round(0, 'ceil')..4 %}
<img src="star-empty.svg" alt="empty star" />
{% endfor %}
{% endif %}
</div>
<div class="ml_review_count" data-i8n="LOCATOR_REVIEWS">{{template_item.location.review_count}} Reviews</div>
</div>
<div class="ml_review_details_bars text-primary">
{% for r in template_item.location.ratingCountByRating %}
<div class="ml-progress-wrap">
<span class="ml_rating_slot">{{r.ratingSlot}} Stars</span>
<div class="progress">
<div class="progress-bar bg-primary" role="progressbar " style="width: {{r.ratingSlotRatio}}%;" aria-valuenow="{{r.ratingSlotRatio}}" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
{% endfor %}
</div>
</span>
</div>
</span>
</div>
</section>
{% endif %}
To display a list of individual reviews, loop over the reviews array as shown below
<!-- reviews -->
{% if template_item.location.reviews %}
<div class="row ml-profile-row">
<div class="col-sm-12 col-md-8 align-self-center">
<h2>Reviews</h2>
<div class="row">
{% for review in template_item.location.reviews %}
<div class="col-sm-12 border">
<!-- full stars -->
{% for r in 1..review.rating|round(0, 'floor') %}
<i class="fas fa-star"></i>
{% endfor %}
<!-- empty stars (no halfs to worry about in individual locations) -->
{% if review.rating|round(0, 'ceil') < 5 %}
{% for r in review.rating|round(0, 'ceil')..4 %}
<i class="far fa-star"></i>
{% endfor %}
{% endif %}
<h4>{{review.fullname}}</h4>
<p>{{review.date|date("m/d/Y g:ia")}}</p>
<p>{{review.comments}}</p>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
<!-- reviews -->
The Location Page Map & Marker
The location page often includes a static map image which also includes a marker. An example markup for the static map is shown below:
<img loading="lazy" class="ml_static d-block w-100" width="100" src="https://cdn.metalocator.com/index.php?option=com_locator&task=tools.createstaticimage&format=raw&url=pin-m-marker+e63b12({{template_item.location.lng}},{{template_item.location.lat}})&lng={{template_item.location.lng}}&lat={{template_item.location.lat}}&zoom=16&width=500&height=500&density=@2x&style=light-v10" alt="{{template_item.location.name}}"/>
The "src" attribute of this image tag calls our static image API endpoint.
The marker shown on the map can be specified as an icon (pin-m-marker
) with a color, as shown in bold below:
https://cdn.metalocator.com/index.php?option=com_locator&task=tools.createstaticimage&format=raw&url=pin-m-marker+FF6A67({{template_item.location.lng}},{{template_item.location.lat}})&lng={{template_item.location.lng}}&lat={{template_item.location.lat}}&zoom=16&width=500&height=500&density=@2x&style=light-v10"
The marker can also be specified as a full URL to a PNG file. The format of the url
parameter above is as follows:
&url=url-<iconurl>(<lng>,<lat>)
Where iconurl is the full path to the marker image beginning with https, and url-encoded and lng/lat indicates the position of the marker, which is usually the exact same as the location image, so a full url parameter with an image might look like:
&url=url-https%3A%2F%2Fd23g0hayoxy5dh.cloudfront.net%2Ful%2F156%2Fimages%2Fbulk%2Fgallery%2Fmap_marker_f04d80_34x34.png(-87.683066,30.375339)
The image itself can be uploaded to MetaLocator under Tools > File Uploader, or it can be uploaded in the Interface builder if it's the same marker used in the locator.
Internationalization
Providing a complete Location page solution in multiple languages involves consideration of:
Website Template - The HTML comprising the header and footer of the location and directory pages
URL - The address of each location and directory page.
Language Files & Constants - The MetaLocator language settings files which contain the translated language strings.
Language Constants
Language constants can be accessed via the ml_language object as shown below. As in other areas of MetaLocator the active language is defined by the language set in the interface settings, or as overridden by the lang parameter in the URL.
{{template_item.ml_language.LOCATOR_GET_DIRECTIONS}}
Some language constants include placeholders such as
LOCATOR_FOUND_RESULTS="%s emplacements trouvés correspondant à votre recherche."
Those can be specified in the page template as
{{ template_item.ml_language.LOCATOR_FOUND_RESULTS|format(200) }}
There are also numbered placeholders where multiple placeholders might be specified as
LOCATOR_DIRECTORY_PAGE_TITLE="%1$s locations in %2$s"
Those are handled similarly, as in this example
{{ template_item.ml_language.LOCATOR_DIRECTORY_PAGE_TITLE|format(region.current_value_count,region.label) }}
And this example
{{ template_item.ml_language.LOCATOR_DIRECTORY_PAGE_TITLE|format(nearby.value_count,nearby.value) }}
Language-Specific Website Templates
If the header and footer of your Website changes significantly from language to language, MetaLocator can store and manage a copy of your Website template for each language.
Under Location Pages the main Website Template is stored which contains the default header and footer used to render location and directory pages. This setting is followed by additional Website Template settings, one for each active language:
As shown above, when the system is set to load the fr-CA language via the "lang" URL parameter, MetaLocator will instead use the fr-CA HTML found in the fr-CA Website Template setting shown above.
MetaLocator pages can link to their foreign language counterparts. For example, in a typical multi-language page, it may be required to link to another translation of the same page, as in a language switcher:
<a href="{{template_item.page_language_urls['en-US (1)']}}">English</a>
<a href="{{template_item.page_language_urls['fr-CA (1)']}}">French</a>
Custom Language Variables for Navigation Elements and Website Header & Footer
If you plan to display your location pages in multiple languages using a single Website Template, the header & footer must include language constants in place of any user-facing English text. The same strategy can be used for links which might be different from language to language.
For example, if your Website Template includes a banner that says "Store Locator", it might include it as follows:
<h2>Store Locator</h2>
This will always display as English to the end user, regardless of the language passed to MetaLocator.
To display language-specific text,
Create a constant in the desired language files to store the "Store Locator" text in each language, e.g. LOCATOR_MYSITE_STORELOCATOR. In your en-US language file, it would still have "Store Locator", however, in your es-MX file, it might contain "Localizador de tiendas".
Replace the English text in the Website Template setting with
<h2>{{template_item.ml_language.LOCATOR_MYSITE_STORELOCATOR}}</h2>
Save the Interface and preview your Location Page, (or Directory Page) and include the "lang" parameter for the desired language file at the end of the URL. E.g.
&lang=es-MX (1)
at the end of the preview link or?es-MX (1)
for the real search-engine-friendly URL.
Language-specific links can be crafted in the same way in the language file. In the example above, if your "Store Locator" text was also a link, as in:
<h2><a href="/en">Store Locator</a></h2>
You would create the LOCATOR_MYSITE_STORELOCATOR just as described above and also another constant for the link itself, LOCATOR_MYSITE_STORELOCATOR_LINK="/en"
Then the code in the Website Template setting would be as shown below, replacing both the text and the link with the translation constants.
<h2><a href="{{template_item.ml_language.LOCATOR_MYSITE_STORELOCATOR_LINK}}">{{template_item.ml_language.LOCATOR_MYSITE_STORELOCATOR}}</a></h2>
Linking from the Locator
The location page typically replaces the Details window. To add the link to the results, add the field as shown below: