Site icon AppTractor

Все, что вы хотели знать о флагах в эмодзи (но не знали, что вам это нужно)

Допустим, вы создаете аналитическое приложение и вам нужен простой способ обозначить местоположение аналитического события. Если вы, как и я, подумали: «Да, флажки эмодзи — забавный способ показать страны», то наверняка сразу же начали создавать такой словарь

let emojiFlags = [
  "AU": "🇦🇺",
  "BE": "🇧🇪",
  "BR": "🇧🇷",
  "BS": "🇧🇸",
  ...
]

Но подождите. Оказывается, есть гораздо более простой способ сделать это (ну как «простой»… с программной точки зрения).

Флаги Юникода

Флаги эмодзи, как и все эмодзи, представлены с помощью именованных последовательностей символов Юникода. Удивительным может показаться тот факт, что юникодовое представление каждого флага напрямую соотносится с двухбуквенным кодом страны по стандарту ISO (он же ISO 3166-1 alpha-2).

Чтобы понять, как это работает, давайте рассмотрим набор флагов эмодзи, который мы определили ранее, но на этот раз добавим их представления в юникоде.

Код страны Эмодзи Unicode Страна
AU 🇦🇺 U+1F1E6 U+1F1FA Australia
BE 🇧🇪 U+1F1E7 U+1F1EA Belgium
BR 🇧🇷 U+1F1E7 U+1F1F7 Brazil
BS 🇧🇸 U+1F1E7 U+1F1F8 Bahamas

Во-первых, обратите внимание, что каждый флаг имеет два символа юникода.

Затем обратите внимание, что все страны с префиксом B имеют один и тот же первый символ юникода — U+1F1E7. Это не совпадение!

Символы региональных индикаторов

Из Википедии:

Cимволы региональных индикаторов — это набор из 26 буквенных символов Юникода (A-Z), предназначенных для использования при кодировании двухбуквенных кодов стран ISO 3166-1 alpha-2 таким образом, чтобы обеспечить необязательную специальную обработку.

Они кодируются в диапазоне от U+1F1E6 для 🇦 в символе регионального индикатора до U+1F1FF для 🇿.

Таким образом, если мы вычтем U+1F1E6 из каждой из наших последовательностей юникода, приведенных выше, мы получим следующие смещения:

Код страны Emoji Смещение Страна
AU 🇦🇺 0 20 Australia
BE 🇧🇪 1 4 Belgium
BR 🇧🇷 1 17 Brazil
BS 🇧🇸 1 18 Bahamas

Теперь можно просто перевести соответствующий код страны с помощью алфавита. Вот таблица на случай, если вы захотите попробовать сделать это вручную.

A B C D E F G H I J
0 1 2 3 4 5 6 7 8 9
K L M N O P Q R S
10 11 12 13 14 15 16 17 18
T U V W X Y Z
19 20 21 22 23 24 25

Функция получения эмодзи

Теперь, когда мы знаем, как создавать эмодзи из кодов стран, мы можем создать функцию, которая будет делать именно это.

func emojiFlag(countryCode: String) -> String? {
  guard countryCode.count == 2 else {
    return nil
  }
  // https://en.wikipedia.org/wiki/Regional_indicator_symbol
  let regionalIndicatorStartIndex: UInt32 = 0x1F1E6
  let alphabetOffset = UnicodeScalar(unicodeScalarLiteral: "A").value
  return String(countryCode
    .uppercased()
    .unicodeScalars
    .compactMap { UnicodeScalar(
      regionalIndicatorStartIndex + ($0.value - alphabetOffset)
    )}
    .map { Character($0) }
  )
}

emojiFlag(countryCode: "CA") // "🇨🇦"

Это гораздо лучше, чем вести словарь.

P.S. Политика Unicode в отношении флагов

Наверное, важно отметить, что консорциум Unicode пару лет назад сообщил, что больше не принимает предложения по созданию новых флагов.

Основываясь на вопросах и ответах, я не думаю, что это обязательно означает, что страны не получат эмодзи с флагами, это просто означает, что консорциум Unicode не рассматривает предложения по новым нестандартизированным флагам (то есть тем, у которых в Unicode нет кода региона).

Подождите, если страна получит независимость и будет признана ISO, значит ли это, что для нее не будет эмодзи флага?

Флаги для стран с кодами регионов в Unicode рекомендуются автоматически, без каких-либо предложений! Сначала их коды и переведенные названия добавляются в Common Locale Data Repository [CLDR] Юникода, а затем эмодзи становятся действительными в следующей версии Юникода. Эти эмодзи также автоматически рекомендуются для общего обмена и широкого применения.

P.P.S.: Регионы

Я как раз закончил писать эту статью, когда увидел сноску в спецификациях Unicode, касающуюся регионов стран, и не смог удержаться, чтобы не покопаться еще немного.

Оказывается, Emoji 5.0 «ввел поддержку флагов регионов и включил Англию, Шотландию и Уэльс в список рекомендованных для общего обмена (RGI)». Это означает, что большинство платформ должны поддерживать эти флаги, но они не реализованы с помощью тех же смещений кодов стран, которые мы использовали выше.

Вместо этого они используют забавную последовательность тегов emoji, которая начинается с черного флага (🏴 U+1F3F4) и заканчивается специальным кодом символа отмены тега (U+E007F).

Если мы прочитаем ISO 3166-2:GB и сопоставим его с опубликованным списком последовательностей эмодзи Unicode, то увидим, что следующие последовательности позволяют нам указывать следующие флаги:

Unicode Код Флаг Регион
U+1F3F4 U+E0067 U+E0062 U+E0065 U+E006E U+E0067 U+E007F 🏴GBENG✦ 🏴󠁧󠁢󠁥󠁮󠁧󠁿 England
U+1F3F4 U+E0067 U+E0062 U+E0073 U+E0063 U+E0074 U+E007F 🏴GBSCT✦ 🏴󠁧󠁢󠁳󠁣󠁴󠁿 Scotland
U+1F3F4 U+E0067 U+E0062 U+E0077 U+E006C U+E0073 U+E007F 🏴GBWLS✦ 🏴󠁧󠁢󠁷󠁬󠁳󠁿 Wales

Мы можем отталкиваться от предыдущей функции, чтобы добавить вариацию регионов:

func emojiFlag(subdivision: String) -> String? {
  guard let blackFlag = Unicode.Scalar(0x1F3F4),
        let cancelTag = Unicode.Scalar(0xE007F) else {
    return nil
  }
  // https://en.wikipedia.org/wiki/Tags_(Unicode_block)
  let tagLetterOffset: UInt32 = 0xE0061
  let alphabetOffset = UnicodeScalar(unicodeScalarLiteral: "a").value
  return String(Character(blackFlag)) + String(subdivision
    .lowercased()
    .unicodeScalars
    .compactMap { Unicode.Scalar(
      tagLetterOffset + ($0.value - alphabetOffset)
    )}
    .map { Character($0) }
  ) + String(Character(cancelTag))
}

emojiFlag(subdivision: "GBENG") // "🏴󠁧󠁢󠁥󠁮󠁧󠁿"
emojiFlag(subdivision: "GBSCT") // "🏴󠁧󠁢󠁳󠁣󠁴󠁿"
emojiFlag(subdivision: "GBWLS") // "🏴󠁧󠁢󠁷󠁬󠁳󠁿"

Очень аккуратно!

Exit mobile version