The writing systems of letters and of a site's languages.
MishkaSlug.slugify/2 uses it for two rules:
- Allow-list. A site's languages (
locales: ["fa", "en"]) become the scripts a slug may use ([:arabic, :latin]). Latin is always allowed, because English words and brand names appear in every language. A letter of any other script is dropped. - One script per word. A word that changes script, like
pаypalwith a Cyrillicа, is cut at the change or refused. Japanese (Han with Hiragana and Katakana), Korean (Han with Hangul) and Chinese with Bopomofo are one family each, so they are never cut. All the scripts of a part must fit one family:夏の幻ㄒㄧㄤis cut before the Bopomofo, because Han carries Hiragana in from the start and no family holds Hiragana with Bopomofo.
A combining mark belongs to the letter before it and is dropped with it, so a dropped letter
never leaves its mark behind. A mark of one script never completes a letter of another: a
Khudawadi vowel sign after a Latin a is dropped, as latin: :ascii drops it too. Characters
that belong to no single script (ASCII digits, the Japanese long-vowel mark ー) are neutral:
they stay with the letters around them. A digit of a script (Persian ۱۴۰۳, Devanagari १२)
follows the allow-list like a letter, so locales: ["en"] drops it, but it never makes a word
mixed: iphone۱۵ stays one word. digits: :ascii writes it as 0-9 first, so it is kept. A letter of a script this module does not know
by name (for example Tibetan) counts as :other.
Summary
Types
A script a slug may use, as MishkaSlug.Scripts.for_locales/1 returns it.
Functions
Returns the scripts the languages of a site are written in, always with :latin.
Returns the scripts of an ISO 15924 script code, as CLDR and BCP 47 tags write them.
Returns the language part of a locale code, lowercased: "pt-BR" gives "pt".
Cuts one word into the parts a slug keeps.
Types
@type script() :: atom()
A script a slug may use, as MishkaSlug.Scripts.for_locales/1 returns it.
Functions
@spec for_locales([String.t()], {module(), atom(), list()} | nil) :: {:ok, :any | [script()]} | {:error, {:unknown_locale, String.t()}}
Returns the scripts the languages of a site are written in, always with :latin.
A locale is a language code as Mishka CMS stores it ("fa", "pt-br", "zh-hans"). A script
subtag picks the script ("sr-Latn" is Latin, "pa-Arab" Arabic); otherwise the language part
is looked up in a built-in table. An empty list means no restriction and returns :any.
resolver lets the host decide instead of the built-in table: a {module, function, args}
called as apply(module, function, [locale | args]) for each locale, returning
{:ok, [script()]} or :error. When it is nil, the :script_resolver of the :mishka_slug
application config is used, and without one the built-in table. :error gives
{:error, {:unknown_locale, locale}}.
iex> MishkaSlug.Scripts.for_locales(["fa", "en"])
{:ok, [:arabic, :latin]}
iex> MishkaSlug.Scripts.for_locales(["sr-Latn"])
{:ok, [:latin]}
iex> MishkaSlug.Scripts.for_locales(["xx"])
{:error, {:unknown_locale, "xx"}}
Returns the scripts of an ISO 15924 script code, as CLDR and BCP 47 tags write them.
The code is a string or an atom in any case ("Arab", :Cyrl). Compound codes give every
script of their family: "Jpan" is Han with both kana, "Kore" Hangul with Han. A script this
library does not know by name gives :error. A :script_resolver can return its result as is.
iex> MishkaSlug.Scripts.from_iso15924("Arab")
{:ok, [:arabic]}
iex> MishkaSlug.Scripts.from_iso15924(:Jpan)
{:ok, [:han, :hiragana, :katakana]}
iex> MishkaSlug.Scripts.from_iso15924("Tibt")
:error
Returns the language part of a locale code, lowercased: "pt-BR" gives "pt".
@spec split(String.t(), :any | [script()], :split | :reject) :: {:ok, [String.t()]} | {:error, :mixed_scripts}
Cuts one word into the parts a slug keeps.
allowed is :any or a list from for_locales/1. With :split the word is cut where its
script changes and the parts in a script that is not allowed are dropped; with :reject a word
that changes script returns {:error, :mixed_scripts}.
iex> MishkaSlug.Scripts.split("pаypal", :any, :split)
{:ok, ["p", "а", "ypal"]}
iex> MishkaSlug.Scripts.split("pаypal", [:latin], :split)
{:ok, ["p", "ypal"]}