WordPress の多言語化プラグイン Bogo の言語スイッチャーですが、言語コードのアルファベット順に表示されます。例えば、日本語、英語、中国語、ポルトガル語、タガログ語の5言語の場合、次のような順序になります。

  1. 英語(en_US)
  2. 日本語(ja)
  3. ポルトガル語(pt_BR)
  4. タガログ語(tl)
  5. 中国語(zh_CN)

これを任意の順序で表示させたい場合、よく紹介されているのは CSS の order プロパティで順序を指定する方法ですが、これでは HTML 上の順序は変わらないため、スクリーンリーダー等では HTML の順序のまま読み上げてしまい、視覚的な表示順序と一致しなくなります。

そこで、CSS を使用せず、PHP によって HTML 上の出力順序そのものを変更する方法です。よくある国旗を非表示にする方法と合わせて紹介します。

functions.php

// Bogoの言語スイッチャーの順序を変更する
add_filter( 'bogo_language_switcher_links', function ( $links ) {
	$order = array(
		'ja'    => 1,
		'en_US' => 2,
		'zh_CN' => 3,
		'pt_BR' => 4,
		'tl'    => 5,
	);

	usort( $links, function( $a, $b ) use ( $order ) {
		$oa = $order[ $a['locale'] ] ?? 999;
		$ob = $order[ $b['locale'] ] ?? 999;
		return $oa <=> $ob;
	});

	return $links;
});

// Bogoの言語スイッチャーの国旗を非表示にする
add_filter( 'bogo_use_flags', '__return_false' );

これで視覚的な順序と HTML の順序が一致します。

ついでに、Bogo の言語スイッチャーは順序なしリスト(ul > li)で出力されます。これを JavaScript と CSS でプルダウンリスト的な操作感にします。WAI-ARIA の aria-expandedaria-hidden も動的に書き換えています。

HTML

<div id="langSwitcher">
	<button type="button" aria-controls="languages" aria-expanded="false">languages</button>
	<div id="languages" aria-hidden="true">
		<?php echo do_shortcode('[bogo]'); ?>
	</div>
</div>

JavaScript (jQuery)

jQuery(function($){
	const $langSwitcher = $('#langSwitcher');
	const $switchBtn = $('#langSwitcher button');
	const $languages = $('#languages');

	function languageClose() {
		$langSwitcher.removeClass('langOpen');
		$switchBtn.attr('aria-expanded', 'false');
		$languages.attr('aria-hidden', 'true');
	}

	$languages.attr('aria-hidden', $langSwitcher.hasClass('langOpen') ? 'false' : 'true');

	$($langSwitcher).on('click', function () {
		const opened = $langSwitcher.toggleClass('langOpen').hasClass('langOpen');
		$switchBtn.attr('aria-expanded', opened ? 'true' : 'false');
		$languages.attr('aria-hidden', opened ? 'false' : 'true');
	});

	// 言語スイッチャー以外をクリックしたら閉じる
	$(document).on('click', function (e) {
		if ( $langSwitcher.hasClass('langOpen') && !$(e.target).closest('#langSwitcher').length ) {
			languageClose();
		}
	});

	// [esc]キーで閉じる
	$(window).on('keydown', function (e) {
		if (e.key !== 'Escape') {
			return;
		}
		if ($langSwitcher.hasClass('langOpen')) {
			languageClose();
			return;
		}
	});
});

CSS(ボーダーや背景等の見た目に関するプロパティは割愛します)

#langSwitcher {
	position: relative;
}

#languages {
	position: absolute;
	top: 100%;
	left: 0;
	width: 100%;
	visibility: hidden;
	transform: translateY(-10px);
	max-height: 0;
	overflow: hidden;
	transition:
		transform .3s ease,
		max-height .3s ease,
		visibility 0s linear .3s;
}

#langSwitcher.langOpen #languages {
	visibility: visible;
	transform: translateY(0);
	max-height: 1000px;
	transition:
		transform .3s ease,
		max-height .3s ease,
		visibility 0s;
}