Adding the language switcher in the navigation menu

SOLVE
FPC
Contributor

Hi guys,

I'm trying to add the language switcher as the last item of my navigation menu.

I'm a developer but I'm not familiar with HubL so I'm struggling to get it done. 

 

Here's where I'd like to have the switcher:

Screenshot 2021-03-16 at 15.57.56.png

Here's how I'm proceeding:

I created a copy of the language switcher module and grabbed the snipped generated:

 

{% module "module_161590389007024" path="/theme/language_switcher_1", label="language_switcher_1" %}

 

From here I tried to add it in different files to see where I should put it to render it in the place I'd like it to be.

Here is my custom theme's folder structure:

Screenshot 2021-03-16 at 16.02.39.png

Going into templates > partials > header.html

Screenshot 2021-03-16 at 16.26.39.png 

and adding it as follows (jump to "adding language switcher here" comment):

 

<!--
  templateType: global_partial
  label: Website header
-->
<header class="header">
  <div class="header__container content-wrapper">

    {# Header logo column #}
    <div class="header__logo header__logo--main">
      {% module 'site_logo' path='@hubspot/logo' %}
    </div>
    {# End header logo column #}

    {# Header navigation column #}
    <div class="header__column">


      {# Header navigation row two #}
      <div class="header__row-2">
        <div class="header--toggle header__navigation--toggle"></div>
        {% if content.translated_content|length %}
          <div class="header--toggle header__language-switcher--toggle"></div>
        {% endif %}
        <div class="header--toggle header__search--toggle"></div>
        <div class="header__close--toggle"></div>
        <div class="header__navigation header--element">
          {# adding the language switcher here #}
          {% module "module_161590389007024" path="/theme/language_switcher_1", label="language_switcher_1" %}
          {% module 'navigation-primary' path='../../modules/menu-section',
            label='Primary navigation'
          %}
          {# and here #}
          {% module "module_161590389007024" path="/theme/language_switcher_1", label="language_switcher_1" %}
        </div>
      </div>
      {# End header navigation row two #}

    </div>
    {# End header navigation column #}
    
  </div>
</header>

 

 I get this result:

Screenshot 2021-03-16 at 16.15.24.png

Meaning that to add it inside the actual menu I have to add it in theme > modules > menu-section,

Screenshot 2021-03-16 at 16.28.08.png

which looks as follows,  and where adding the module in any line, for example in the last <nav> element at the end of the file (jump to "adding language switcher here" comment):

 

{# Default menu item classes #}

{% macro defaultItemClasses() %}
 {{
  {
    'class': 'no-submenu menu-item'
  }|xmlattr
 }}
{% endmacro %}

{# Menu item classes if a menu item has a child menu #}

{% macro childClasses() %}
 {{
  {
    'class': 'has-submenu menu-item'
  }|xmlattr
 }}
{% endmacro %}

{# Menu item link attributes if the menu item is the current active page #}

{% macro activeNode() %}
 {{
  {
    'class': 'menu-link active-item',
    'aria-current': 'page'
  }|xmlattr
 }}
{% endmacro %}

{# Menu item link classes if menu item is the parent of the current active page #}

{% macro activeBranch() %}
 {{
  {
    'class': 'menu-link active-branch'
  }|xmlattr

 }}
{% endmacro %}

{# Menu item link attribute if the menu item is set to open in a new window #}

{% macro linkTarget() %}
{{
  {
    'target': '_blank'
  }|xmlattr
}}
{% endmacro %}

{# Navigation menu item macro #}

{% macro link(node) %}
  <li {{ childClasses() if node.children else defaultItemClasses() }}>
    <a href="{{ node.url if node.url else 'javascript&colon;;' }}" class="menu-link" {{ activeBranch() if node.activeBranch }} {{ activeNode() if node.activeNode }} {{ linkTarget() if node.linkTarget }}>{{ node.label }}</a>

    {% if node.children %}
      <input type="checkbox" id="{{ node.label }}" class="submenu-toggle">
      <label class="menu-arrow" for="{{ node.label }}">
        <span class="menu-arrow-bg"></span>
      </label>
      {{ renderNavigation(node) }}
    {% endif %}
  </li>
{% endmacro %}

{# Navigation submenu macro #}

{% macro renderNavigation(menuTree) %}
  {% set level = level + 1 %}
  <ul class="submenu level-{{ level }}" aria-hidden="{{ level != 1 }}">
    <div class="level-{{ level }}"><div class="level-{{ level }}"></div></div>
    {% for node in menuTree.children %}
      {{ link(node) }}
    {% endfor %}
  </ul>
{% endmacro %}

{# Main navigation wrapper #}

<nav aria-label="Main menu" class="navigation-primary">
  {{ renderNavigation(menu(module.primary_menu_field)) }}
  {# Adding language switcher here #}
  {% module "module_161590767184329" path="/theme/language_switcher_1", label="language_switcher_1" %}
</nav>

 

 Returns these two errors:

Screenshot 2021-03-16 at 16.22.00.png

This makes me think that I'm in the right file but I just don't know how to repurpose the module snippet into the right syntax. 

 

Any help on how to solve this would be extremely appreciated! 🙏

0 Upvotes
1 Accepted solution

Accepted Solutions
Anton
Solution
Key Advisor | Diamond Partner

Hi @FPC

welcome to hubl development.
first of all - why do you want to add the language switcher directly to the menu module? What do you want to achive?

second: It's not possible to put a module inside of a module. What you can do is copy/paste the sourcecode of a module inside another one. But this will make things "harder" compared to "drop two modules inside a partial". If you want to show different content based on the translation/language, set the partial as a global-partial and then create the translations in the global-module-editor(misleading naming, but it's how it's called)

I would assume that you want to display various language switcher in different menus(like english, french, other languages). You don't have to if you want to a) localize the namings of the languages b) show translations depending only on pages which maybe got a translation. 

If you use the default HS language switcher it will automaticly show up only if there is a translation of the page. If you want to modify it to show language flags you'll need to write your own language switcher (not that hard with hubl)

 

hope this helps, 

 

best, 

Anton

 

---
@Ntbrown man - your comments are always so informational. They really help the communtiy and enlighten the world. 😶




check
Did my post help answer your query? Help the Community by marking it as a solution



View solution in original post

7 Replies 7
Ntbrown
Contributor

As the error message implies you can't call a module inside another module hence the "disabled in this context" message.

0 Upvotes
Anton
Solution
Key Advisor | Diamond Partner

Hi @FPC

welcome to hubl development.
first of all - why do you want to add the language switcher directly to the menu module? What do you want to achive?

second: It's not possible to put a module inside of a module. What you can do is copy/paste the sourcecode of a module inside another one. But this will make things "harder" compared to "drop two modules inside a partial". If you want to show different content based on the translation/language, set the partial as a global-partial and then create the translations in the global-module-editor(misleading naming, but it's how it's called)

I would assume that you want to display various language switcher in different menus(like english, french, other languages). You don't have to if you want to a) localize the namings of the languages b) show translations depending only on pages which maybe got a translation. 

If you use the default HS language switcher it will automaticly show up only if there is a translation of the page. If you want to modify it to show language flags you'll need to write your own language switcher (not that hard with hubl)

 

hope this helps, 

 

best, 

Anton

 

---
@Ntbrown man - your comments are always so informational. They really help the communtiy and enlighten the world. 😶




check
Did my post help answer your query? Help the Community by marking it as a solution



View solution in original post

FPC
Contributor

Hi @Anton

Thank you very much for your answer.

 

The whole website has to be translated in french (and more other languages in the future) so I need the language switcher (always the same one) to be on every page. Having a geolocation feature that would switch the language automatically would help, but that's not available through hubspot, is it? Even if it was I'd like the user to have the chance to switch to their preferred language.

 

The menu I'm referring to is in the main header of my website:

Screenshot 2021-03-16 at 22.00.12.png

I'm not sure about where else I should put it if not right after "contact us". 

 

PS: Having the language switcher displaying flags instead of language names would be exactly my preferred outcome. Is there any code snippet from somebody that already did it that you could point me to?

 

Any input would be greatly appreciated, thank you very much!

 

0 Upvotes
Anton
Key Advisor | Diamond Partner

Hi @FPC

ok great. So let's start (this will be kind of a step-by-step-guide):

Info: I will use bootstrap4 classes for this tutorial - of course you can use your own

 

1. Base layout

if your base layout looks a bit like this:

 

<!doctype html>
<head>
...
{{ require_css(get_asset_url('../../css/main.css')) }} {# the "compiled" CSS #}
{{ require_css(get_asset_url('../../css/theme.css')) }} {# the "override" CSS with all the theme settings #}
{{ standard_header_includes }}
</head>
<body>
{# begin header #}
{% block header %}
...
{% endblock header %}
{# end header #}

{# start body #}
{% block body %}
...
{% endblock body %}
{# end body #}

{# start footer #}
{% block footer %}
...
{% endblock footer %}
{# end footer #}
{# start scripts #}
{# YOUR EXTENAL SCRIPTS #}
{{ standard_footer _includes }} {# best if they are loaded after your custom scripts #}
{# end scripts #}
</body>

 

 

 

awesome. If not - feel free to use this

 

2. Header Partial 

create a new global-partial(important) template and write your layout. For your provided screenshot I would write it like this:

 

{# recommend to put the next two lines at the top since you won't need to search for it and it will be used in several places later in the code #}
{% set currLang = content.language.languageTag %}
{% set codes = {'English': 'en', 'Deutsch': 'de', 'Español': 'es',  'Français': 'fr' ,'Italiano': 'it' , 'Nederlands' :'nl', 'Português' : 'pt'} %} 
{# here you set all the display names and language tags - you can modify, add/remove as you need - they have to be exactly! like en, en-gb, en-us...#}

<header class="header">
<div class="container">
<div class="row">
{# start logo #}
<div class="col-12 col-md-3 logo">
{{ logo }} {# this hubl will automaticly load the logo which was set in the hub-settings #}
</div>
{# end logo #}
{# start menu #}
<div class="col-12 col-md-{% if content.language.languageTag %}7{% else %}9{% endif %} navigation">

{% module "navigation" path="@hubspot/menu", label="Top Menu", no_wrapper=True %} 
</div>
{# end menu #}
{# start custom language switcher #}
<div class="col">
<ul class="language-switcher">

<li>

<a class="lang-select" href="#"><img class="lang-flag" src="FILE-URL-IN-THE-FILE-MANAGER/assets/flags/{{currLang|lower}}.svg" loading="lazy"><span>{{ currLang }}</span></a> {# this will be shown as "current language"; to make life easier i've called all the flags according to the language tag. German: de, English: en... - definitly recommend that #}

<ul class="menu-lang">
{% for key, val in codes.items() %}
{% set URL = "/"~content.translated_content[val].slug || val %}
<li>
<a class="lang-option" href="{{ URL }}"><img class="lang-flag" src="FILE-URL-IN-THE-FILE-MANAGER/assets/flags/{{ val }}.svg" loading="lazy">{{ key }}</a>
</li>
{% endfor %}
</ul>
</li>
</ul>
</div>
{# end custom language switcher #}
</div>
</div>
</header>

 

 

 

 

more detailed explanation/tipps:

  • you don't need to clone the default menu module unless you want/need to
  • always put a "no_wrapper=True" into your modules - this will cut some (optional) wrapping divs and you'll gain some page speed if you use a lot of modules
  • I've uploaded all of my flag icons to a folder structure I've created in the file-manager(FM) - just replace it with your own. The easiest possible way would be to upload them, open one, copy the path/URL from the FM, paste it once into this code and replace the name with either {{ currLang }} or/and {{ key }}. If they're the same - the images will be displayed.
  • I've put an if-loop into the menu-col. This will change the width of the menu and so that the language switcher has space if it will be shown(if there is a translation)

 

3. the page template (the exciting/easiest part 😁)

create a new template(not global) and set it up like this:

 

<!--
templateType: page
isAvailableForNewContent: true
label: Flexible Website
-->
{% extends '../../templates/layout/layout.html' %} {# replace this with the path to your base layout #}
{% block header %}
{% global_partial path="/THEME-NAME/templates/globals/header.html" name="Header", no_wrapper=True %}
{% endblock header %}

{% block body%}
{# write your dnd layout here #}
{% endblock body %}
{% block footer %}
{% global_partial path="/THEME-NAME/templates/globals/footer.html" name="Header", no_wrapper=True %} {# same procedure as for header if you like/need #}
{% endblock footer %}

 

 

4. Styling

In case you didn't stlyed it already apply all of your stylings to everything.

My recommendation is to create a seperate CSS file for everything(like in the boilerplate), load them into the main.css and override them with the theme.css

I like to create folder-structures like

- globals

- - buttons.css

- - fonts.css

- - footer.css

- - forms.css

- - header.css

- layout

- - animations.css

- - grid.css

...

 

the main.css looks a bit like this:

 

{# default #}
{% include '../css/layout/reset.css' %}
{% include '../css/layout/normalize.css' %}
{% include '../css/layout/grid.css' %}

{# globals #}
{% include '../css/globals/fonts.css' %}
{% include '../css/globals/header.css' %}
{% include '../css/globals/forms.css' %}
{% include '../css/globals/footer.css' %}

 

and the theme.css looks something like this:

 

{# fonts #}
{% set head_font_type             =   theme.fonts.headlines.font %}
{% set head_font_type_fallback    =   'Arial, sans-serif' %}
{% set head_font_variant          =   '500' %}
{% set h1_font_size               =   theme.fonts.font_sizes.h1.size + theme.fonts.font_sizes.h1.size_unit %}
{% set h1_color                   =   'rgb(' + theme.fonts.font_sizes.h1.color|convert_rgb + ')' %}
{% set h2_font_size               =   theme.fonts.font_sizes.h2.size + theme.fonts.font_sizes.h2.size_unit %}
{% set h2_color                   =   'rgb(' + theme.fonts.font_sizes.h2.color|convert_rgb + ')' %}
{% set h3_font_size               =   theme.fonts.font_sizes.h3.size + theme.fonts.font_sizes.h3.size_unit %}
{% set h3_color                   =   'rgb(' + theme.fonts.font_sizes.h3.color|convert_rgb + ')' %}
{% set h4_font_size               =   theme.fonts.font_sizes.h4.size + theme.fonts.font_sizes.h4.size_unit %}
{% set h4_color                   =   'rgb(' + theme.fonts.font_sizes.h4.color|convert_rgb + ')' %}
{% set h5_font_size               =   theme.fonts.font_sizes.h5.size + theme.fonts.font_sizes.h5.size_unit %}
{% set h5_color                   =   'rgb(' + theme.fonts.font_sizes.h5.color|convert_rgb + ')' %}
{% set small_font_size            =   theme.fonts.font_sizes.small_text.size + theme.fonts.font_sizes.small_text.size_unit %}
{% set small_font_color           =   'rgb(' + theme.fonts.font_sizes.small_text.color|convert_rgb + ')' %}
{% set body_text_type             =   theme.fonts.text.font %}
{% set body_text_variant          =   theme.fonts.text.variant %}
{% set body_text_type_fallback    =   theme.fonts.text_fallback.font %}
{% set body_text_line_height      =   theme.fonts.line_height + 'px' %}

...

body{
		font-family: '{{ body_text_type }}', {{ body_text_type_fallback }};
		font-weight:{{ body_text_variant }};
		font-size: 1em;
		line-height: {{ body_text_line_height }};
}
h1, h2, h3, h4, h5, h6{
		font-family:'{{ head_font_type }}', {{head_font_type_fallback }};
		font-weight:{{ head_font_variant }};
		margin: 0 0 1.5em;
		line-height:175%;
}
h1{
		font-size:{{ h1_font_size }};
		line-height: 1.5em;
}
h2{
		font-size:{{ h2_font_size }};
		line-height: 1.5em;
}
h3{
		font-size: {{ h3_font_size }};
		line-height: 1.5em;
}
h4{
		font-size: {{ h4_font_size }};
		line-height: 1.5em;
}
h5{
		font-size: {{ h5_font_size }};
		line-height: 1.5em;
}
h6, small, .text_small {
		font-size: {{ small_font_size }};
		line-height: 1.5em;
}
p{
		margin-bottom: 1rem;
}

 

 

5. last steps

You need some kind of (jQuery) toggle event for the language switcher.

Mine looks like this:

 

// toggle lang-switcher
$( '.lang-select' ).click(function() {
  $( '.menu-lang' ).slideToggle( );
});

 

and some stylings for the switcher

 

header ul.language-switcher img{
		height: 32px;
		width: 32px;
		margin-right: 1rem;
		position: relative;
		top: 0.75em;
}
header .lang-option img{
		height:24px;
		width:24px;
}
header .lang-flag span{
		margin-top:-3px;
}
header .menu-lang{
		display:none;
		padding: 2rem;
		position: relative;
		left: -2rem;
		top: 3.25rem;
}

 

 

 

hope this helps. If you have further questions - feel free to ask

 

 

best,

Anton

 

 

eidt: the language switcher was originaly created by @piersg (kudos to him); I've modified it a bit 




check
Did my post help answer your query? Help the Community by marking it as a solution



FPC
Contributor

Thank you very much @Anton.
I'll try to implement it later and see how it goes.

Since we are here, can you confirm that there is no way to have the language switcher switch automatically based on visitors IP or browser language? 

Anton
Key Advisor | Diamond Partner

Hi @FPC

you're welcome.

 

I know that some of my colleagues did a "automatic IP-redirect" in the past, but I'm not 100% sure how they did that. I think they've done it by setting up some DNS settings at the domain-provider. Or you could try a javascript redirect like described here.

You'll need to modify the code a bit to work in HubSpot - it could look like this:

 

{{ require_js }}
<script type="text/javascript">
{% set currLang = content.language.languageTag %}
{% set codes = {'English': 'en', 'Deutsch': 'de', 'Español': 'es',  'Français': 'fr' ,'Italiano': 'it' , 'Nederlands' :'nl', 'Português' : 'pt'} %} 
    {% for key, val in codes.items() %}
{% set URL = "/"~content.translated_content[val].slug || val %}
$( document ).ready(function(){
        var userLang = navigator.language || navigator.userLanguage;
        if (userLang == "{{ val }}") {
            break;
        }
        else {
            window.location.href = "{{ URL }}"
        }
    });
{% endfor %}
</script>
{{ end_require_js }}

 

hope this helps,

 

best,

Anton




check
Did my post help answer your query? Help the Community by marking it as a solution



Ntbrown
Contributor
@Anton wrote

@Ntbrown man - your comments are always so informational. They really help the communtiy and enlighten the world. 

 


I agree! Thanks for noticing 🙂

 

As the error message implies you can't call a module inside another module hence the "disabled in this context" message.

What is incorrect about this statement? Is your dissatisfaction that I didn't write multiple paragraphs explaining every minute detail? - which I might add I do regularly where warranted and on sufficient topics. It's what was wrong and didn't require an illuminating statement beyond a terse point. At which point if someone is correcting your mistakes - free of charge I might add - with little to no debugging on your end - clearly - I'd expect you to be able to work through your problems from there especially if labelling yourself a developer. People constantly spelling out your problems for you doesn't help you improve or learn hence why I tend to provide hints and let people do the rest. Not to mention there are many knowledge base articles and posts on this - which ironically rebukes your point about being "helpful to the community" as excessive bloat causing search redundancies with people looking for solutions certainly doesn't.

0 Upvotes