BS Blog

Country Flag output for Drupal

Published Aug 06, 2025 |

Recently I needed to output flag icons for country selections on a Drupal site. There's a few modules out there, but all have a lot of dependencies and are using images when really this is supported out of the box by unicode.

So why not build one that allows unicode flags that can be used both in Twig and custom PHP. The full code is at the bottom of this article, but let's first see how to use it in different ways. By making it a twig extension, but also a public static function, I can use this EVERYWHERE!!!! For example:

Twig:

twig
<span class="address" translate="no"> {{ country.code|toUnicodeFlag }} </span>

To make this work, you can set a service tagged with twig.extension

yaml
my_module.twig.country_flags: class: Drupal\my_module\Twig\TwigFlagUnicode tags: - { name: twig.extension }

Inside Custom Renders:

Since the method is public static, you can call it in any render like so:

php
$build['member_title'] = [ '#type' => 'markup', '#markup' => Markup::create(TwigFlagUnicode::render($country) . ' ' . $member->label()), ];

Views or FieldFormatters:

Similarly, this allows for formatters like field formatters, or like in this example: Views

php
/** * Override of Country plugin to allow flags. */ class CountryFlag extends Country { /** * {@inheritdoc} */ protected function defineOptions() { $options = parent::defineOptions(); $options['display_flag'] = ['default' => FALSE]; return $options; } /** * {@inheritdoc} */ public function buildOptionsForm(&$form, FormStateInterface $form_state) { $form['display_flag'] = [ '#type' => 'checkbox', '#title' => $this->t('Display the country flag instead of the country code'), '#default_value' => !empty($this->options['display_flag']), ]; parent::buildOptionsForm($form, $form_state); } /** * {@inheritdoc} */ public function render(ResultRow $values) { $value = $this->getValue($values); if (!empty($this->options['display_flag']) && !empty($value)) { return TwigFlagUnicode::render($value); } return parent::render($values); } }

Pretty freaking fantastic huh? I should probably make this a simple contrib module or see if the address module want's an upgrade, but for now I'll just post the code here for you lovely people:

FULL SOURCE:

php
<?php namespace Drupal\my_module\Twig; use Drupal\Component\Render\MarkupInterface; use Drupal\Component\Utility\Html; use Drupal\Core\Render\Markup; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; /** * Twig function to convert country to flag icon. */ class TwigFlagUnicode extends AbstractExtension { /** * {@inheritDoc} */ public function getFilters(): array { return [ new TwigFilter('toUnicodeFlag', [$this, 'filter']), ]; } /** * Filter the country code to unicode flag. * * @param string $text * The country code. * * @return string * Returns the unicode flag. */ public function filter($text) { return static::render(trim(Html::escape($text))); } /** * Render the unicode flag. * * @param string $code * The country code. * * @return \Drupal\Component\Render\MarkupInterface * Returns markup. */ public static function render(string $code): MarkupInterface { /** @var \CommerceGuys\Addressing\Country\CountryRepositoryInterface $countryRepository */ $countryRepository = \Drupal::service('address.country_repository'); $unicode = mb_convert_encoding('&#' . (127397 + ord($code[0])) . '&#' . (127397 + ord($code[1])) . ';', 'UTF-8', 'HTML-ENTITIES'); try { $country = $countryRepository->get($code); } catch (\Exception $e) { return Markup::create($unicode); } return Markup::create('<span title="' . $country->getName() . '">' . $unicode . '</span>'); } }