Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

RubyでISO国名コード2文字を絵文字の国旗に変換する(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

regional indicator symbolやregional indicator characterは、仮訳の「地域指示記号」で統一しました。
また、一部のサンプルコードについては見やすさのためGistを使っています。

RubyでISO国名コード2文字を絵文字の国旗に変換する(翻訳)

アプリケーションで、国名の参照をISO 3166-1 alpha-2標準の2文字のコードとしてインラインで保存することがよくあります。たとえば「GB」は英国、「US」は米国を表すという具合です。

しかし絵文字でやりたい人たちがいるならば受けて立ちましょう。

def emoji_flag(country_code)
  cc = country_code.to_s.upcase
  return unless cc =~ /\A[A-Z]{2}\z/

  cc.codepoints.map { |c| (c + 127397).chr(Encoding::UTF_8) }.join
end

しくみ

最初の2行は「防御的コード」と呼ばれるもので、無関係な文字が入力されてもメソッドがおかしくならないようにします。

cc = country_code.to_s.upcase

上のコードは、入力文字を英大文字に揃えます。

return unless cc =~ /\A[A-Z]{2}\z/

上のコードは、英大文字2文字でない文字列が渡された場合にnilを返します(早期脱出)。

「メソッドが常に文字列を返すよう、nilではなく""を返すべき」という意見もおありかと思いますが、面倒でもnilを返す方がRubyらしいと言えます。

少々うまくやれた点を解説

すべての国にはアルファベット2文字の一意のコードが割り当てられています(一部の国には以前から割り当てられていますが)。

UNICODEには英大文字に対応する(Enclosed Alphanumeric Supplement)と呼ばれるブロックがあり、その中にRegional indicator symbol(地域指示記号)と呼ばれる特殊な大文字が26個連続で存在します。たとえば大文字のAには🇦という地域指示記号が対応しています。

UNICODEにあるこの地域指示記号を2つ並べると、OS上で国旗の絵文字がレンダリングされます。たとえば、画面に表示される🇪🇺という絵文字は、🇪🇺が連続したUTF-8文字列で構成されています。

Unicodeの文字テーブルには、通常の英大文字の他にこうした地域指示記号も含まれています。

UNICODE英大文字Aのコードポイントは65、それに対応する🇦のコードポイントは127,462です。そして、大文字(AZ)と、それに対応する地域指示記号(🇦🇿)のコードポイントの差は、どの大文字でも常に127,397です。この「マジックナンバー」こそが変換方法の鍵となります。

冒頭のコードの主な機能は、2文字の文字列を分割し、それぞれの文字をRubyの#codepointsメソッドでASCII(UTF-8)文字テーブルの数値表現に変換することです。そして各コードポイントに127,397を足し、この新しい参照をUTF-8エンコード文字に戻します。最後に2つの地域指示記号を#joinしてStringに戻します。

フランスを例に、これまでの手順をおさらいすると以下のようになるでしょう。

このコードは、RubyのUTF-8テキストレンダリングとモダンなOSを組み合わせると「2個の地域指示記号」ではなく国旗が表示されるという動作に依存しています。

UNICODE文字テーブルの構造がうまく考えられているおかげで、このメソッドはある程度寛容に動作します。以下のように未割り当ての国コードを渡すと、2つの地域指示記号をフォールバック表示します。

関連記事

漢字のようで漢字でないUnicodeの「康熙部首」と「CJK部首補助」


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。