Implement Stories feature behind flag.

Co-Authored-By: Greyson Parrelli <37311915+greyson-signal@users.noreply.github.com>
Co-Authored-By: Rashad Sookram <95182499+rashad-signal@users.noreply.github.com>
This commit is contained in:
Alex Hart
2022-02-24 13:40:28 -04:00
parent 765185952e
commit 174cd860a0
416 changed files with 19506 additions and 857 deletions

View File

@@ -0,0 +1,85 @@
package org.thoughtcrime.securesms.fonts
import android.content.Context
import androidx.annotation.WorkerThread
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.util.EncryptedStreamUtils
import java.io.File
/**
* FontFileMap links a network font name (e.g. Inter-Bold.ttf) to a UUID used as an on-disk filename.
* These mappings are encoded into JSON and stored on disk in a file called .map
*/
data class FontFileMap(val map: Map<String, String>) {
companion object {
private val TAG = Log.tag(FontFileMap::class.java)
private const val PATH = ".map"
private val objectMapper = ObjectMapper().registerKotlinModule()
/**
* Adds the given mapping to the .map file.
*
* @param context A context
* @param fontVersion The font version from which to get the parent directory
* @param nameOnDisk The name written to disk
* @param nameOnNetwork The network name from the manifest
*/
@WorkerThread
fun put(context: Context, fontVersion: FontVersion, nameOnDisk: String, nameOnNetwork: String) {
val fontFileMap = getMap(context, fontVersion)
@Suppress("IfThenToElvis")
val newMap = if (fontFileMap == null) {
Log.d(TAG, "Creating a new font file map.")
FontFileMap(mapOf(nameOnNetwork to nameOnDisk))
} else {
Log.d(TAG, "Modifying existing font file map.")
fontFileMap.copy(map = fontFileMap.map.plus(nameOnNetwork to nameOnDisk))
}
setMap(context, fontVersion, newMap)
}
/**
* Retrieves the on-disk name for a given network name
*
* @param context a Context
* @param fontVersion The version from which to get the parent directory
* @param nameOnNetwork The name of the font from the manifest
* @return The name of the file on disk, or null
*/
@WorkerThread
fun getNameOnDisk(context: Context, fontVersion: FontVersion, nameOnNetwork: String): String? {
val fontFileMap = getMap(context, fontVersion) ?: return null
return fontFileMap.map[nameOnNetwork]
}
@WorkerThread
private fun getMap(context: Context, fontVersion: FontVersion): FontFileMap? {
return try {
EncryptedStreamUtils.getInputStream(context, File(Fonts.getDirectory(context), "${fontVersion.path}/$PATH")).use {
objectMapper.readValue(it, FontFileMap::class.java)
}
} catch (e: Exception) {
Log.w(TAG, "Couldn't read names file.")
return null
}
}
@WorkerThread
private fun setMap(context: Context, fontVersion: FontVersion, fontFileMap: FontFileMap) {
try {
EncryptedStreamUtils.getOutputStream(context, File(Fonts.getDirectory(context), "${fontVersion.path}/$PATH")).use {
objectMapper.writeValue(it, fontFileMap)
}
} catch (e: Exception) {
Log.w(TAG, "Couldn't write names file.")
}
}
}
}

View File

@@ -0,0 +1,97 @@
package org.thoughtcrime.securesms.fonts
import android.content.Context
import androidx.annotation.WorkerThread
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.util.EncryptedStreamUtils
import java.io.File
/**
* Description of available scripts and fonts for different locales.
*
* @param scripts A collection of supported scripts
*/
data class FontManifest(
val scripts: FontScripts
) {
/**
* A collection of supported scripts
*
* @param latinExtended LATN Script fonts
* @param cyrillicExtended CYRL Script fonts
* @param devanagari DEVA Script fonts
* @param chineseTraditionalHk Hans / HK Script Fonts
* @param chineseTraditional Hant Script Fonts
* @param chineseSimplified Hans Script Fonts
*/
data class FontScripts(
@JsonProperty("latin-extended") val latinExtended: FontScript,
@JsonProperty("cyrillic-extended") val cyrillicExtended: FontScript,
val devanagari: FontScript,
@JsonProperty("chinese-traditional-hk") val chineseTraditionalHk: FontScript,
@JsonProperty("chinese-traditional") val chineseTraditional: FontScript,
@JsonProperty("chinese-simplified") val chineseSimplified: FontScript,
val arabic: FontScript,
val japanese: FontScript,
)
/**
* A collection of fonts for a specific script
*/
data class FontScript(
val regular: String,
val bold: String,
val serif: String,
val script: String,
val condensed: String
)
companion object {
private val TAG = Log.tag(FontManifest::class.java)
private const val PATH = ".manifest"
private val objectMapper = ObjectMapper().registerKotlinModule()
/**
* Gets the latest manifest object for the given version. This may hit the network, disk, or both, depending on whether we have
* a cached manifest available for the given version.
*/
@WorkerThread
fun get(context: Context, fontVersion: FontVersion): FontManifest? {
return fromDisk(context, fontVersion) ?: fromNetwork(context, fontVersion)
}
@WorkerThread
private fun fromDisk(context: Context, fontVersion: FontVersion): FontManifest? {
if (fontVersion.path.isEmpty()) {
throw AssertionError()
}
return try {
EncryptedStreamUtils.getInputStream(context, File(Fonts.getDirectory(context), fontVersion.manifestPath())).use {
objectMapper.readValue(it, FontManifest::class.java)
}
} catch (e: Exception) {
Log.w(TAG, "Failed to load manifest from disk")
return null
}
}
@WorkerThread
private fun fromNetwork(context: Context, fontVersion: FontVersion): FontManifest? {
return if (Fonts.downloadAndVerifyLatestManifest(context, fontVersion, fontVersion.manifestPath())) {
fromDisk(context, fontVersion)
} else {
null
}
}
private fun FontVersion.manifestPath(): String {
return "$path/$PATH"
}
}
}

View File

@@ -0,0 +1,129 @@
package org.thoughtcrime.securesms.fonts
import android.content.Context
import androidx.annotation.WorkerThread
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.EncryptedStreamUtils
import java.io.File
import java.util.UUID
import java.util.concurrent.TimeUnit
/**
* Represents a single version of fonts.
*
* @param id The numeric ID of this version, retrieved from the server
* @param path The UUID path of this version on disk, where supporting files will be stored.
*/
data class FontVersion(val id: Long, val path: String) {
companion object {
val NONE = FontVersion(-1, "")
private val TAG = Log.tag(FontVersion::class.java)
private val VERSION_CHECK_INTERVAL = TimeUnit.DAYS.toMillis(7)
private const val PATH = ".version"
private val objectMapper = ObjectMapper().registerKotlinModule()
/**
* Retrieves the latest font version. This may hit the disk, network, or both, depending on when we last checked for a font version.
*/
@WorkerThread
fun get(context: Context): FontVersion {
val fromDisk = fromDisk(context)
val version: FontVersion = if (System.currentTimeMillis() - SignalStore.storyValues().lastFontVersionCheck > VERSION_CHECK_INTERVAL) {
Log.i(TAG, "Timeout interval exceeded, checking network for new font version.")
val fromNetwork = fromNetwork()
if (fromDisk == null && fromNetwork == null) {
Log.i(TAG, "Couldn't download font version and none present on disk.")
return NONE
} else if (fromDisk == null && fromNetwork != null) {
Log.i(TAG, "Found initial font version.")
return writeVersionToDisk(context, fromNetwork) ?: NONE
} else if (fromDisk != null && fromNetwork != null) {
if (fromDisk.id < fromNetwork.id) {
Log.i(TAG, "Found a new font version. Replacing old version")
writeVersionToDisk(context, fromNetwork) ?: NONE
} else {
Log.i(TAG, "Network version is the same as our local version.")
fromDisk
}
} else {
Log.i(TAG, "Couldn't download font version, using what we have.")
fromDisk ?: NONE
}
} else {
Log.i(TAG, "Timeout interval not exceeded, using what we have.")
fromDisk ?: NONE
}
cleanOldVersions(context, version.path)
return version
}
@WorkerThread
private fun writeVersionToDisk(context: Context, fontVersion: FontVersion): FontVersion? {
return try {
val versionPath = File(Fonts.getDirectory(context), PATH)
if (versionPath.exists()) {
versionPath.delete()
}
EncryptedStreamUtils.getOutputStream(context, versionPath).use {
objectMapper.writeValue(it, fontVersion)
}
File(Fonts.getDirectory(context), fontVersion.path).mkdir()
Log.i(TAG, "Wrote version ${fontVersion.id} to disk.")
SignalStore.storyValues().lastFontVersionCheck = System.currentTimeMillis()
fontVersion
} catch (e: Exception) {
Log.e(TAG, "Failed to write new font version to disk", e)
null
}
}
@WorkerThread
private fun fromDisk(context: Context): FontVersion? {
return try {
EncryptedStreamUtils.getInputStream(context, File(Fonts.getDirectory(context), PATH)).use {
objectMapper.readValue(it, FontVersion::class.java)
}
} catch (e: Exception) {
Log.w(TAG, "Could not read font version from disk.")
null
}
}
@WorkerThread
private fun fromNetwork(): FontVersion? {
return try {
FontVersion(Fonts.downloadLatestVersionLong(), UUID.randomUUID().toString()).apply {
Log.i(TAG, "Downloaded version $id")
}
} catch (e: Exception) {
Log.w(TAG, "Could not read font version from network.", e)
null
}
}
@WorkerThread
private fun cleanOldVersions(context: Context, path: String) {
if (path.isEmpty()) {
Log.i(TAG, "No versions downloaded. Skipping cleanup.")
return
}
Fonts.getDirectory(context)
.listFiles { _, name -> name != path && name != PATH }
?.apply { Log.i(TAG, "Deleting $size files") }
?.forEach { it.delete() }
}
}
}

View File

@@ -0,0 +1,207 @@
package org.thoughtcrime.securesms.fonts
import android.content.Context
import android.graphics.Typeface
import androidx.annotation.WorkerThread
import org.signal.core.util.concurrent.SignalExecutors
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.s3.S3
import org.thoughtcrime.securesms.util.ListenableFutureTask
import java.io.File
import java.util.Collections
import java.util.Locale
import java.util.UUID
/**
* Text Story Fonts management
*
* Fonts are stored on S3 in a bucket called story-fonts, and are backed by a version number.
* At that version, there is a manifest.json that contains information about which fonts are available for which script
*
* This utilizes a file structure like so:
*
* .version ( long -> UUID )
* uuid/
* .manifest (manifest JSON)
* .map ( object name -> UUID )
* uuid1
* uuid2
* ...
*/
object Fonts {
private val TAG = Log.tag(Fonts::class.java)
private const val VERSION_URL = "https://updates.signal.org/dynamic/story-fonts/version.txt"
private const val BASE_STATIC_BUCKET_URL = "https://updates.signal.org/static/story-fonts"
private const val MANIFEST = "manifest.json"
private val taskCache = Collections.synchronizedMap(mutableMapOf<FontDownloadKey, ListenableFutureTask<Typeface>>())
/**
* Returns a File which font data should be written to.
*/
fun getDirectory(context: Context): File {
return context.getDir("story-fonts", Context.MODE_PRIVATE)
}
/**
* Attempts to retrieve a Typeface for the given font / locale combination
*
* @param context An application context
* @param locale The locale the content will be displayed in
* @param font The desired font
* @return a FontResult that represents either a Typeface or a task retrieving a Typeface.
*/
@WorkerThread
fun resolveFont(context: Context, locale: Locale, font: TextFont): FontResult {
synchronized(this) {
val errorFallback = FontResult.Immediate(Typeface.create(font.fallbackFamily, font.fallbackStyle))
val version = FontVersion.get(context)
if (version == FontVersion.NONE) {
return errorFallback
}
val manifest = FontManifest.get(context, version) ?: return errorFallback
Log.d(TAG, "Loaded manifest.")
val fontScript = resolveScriptNameFromLocale(locale, manifest) ?: return errorFallback
Log.d(TAG, "Loaded script for locale.")
val fontNetworkPath = getScriptPath(font, fontScript)
val fontLocalPath = FontFileMap.getNameOnDisk(context, version, fontNetworkPath)
if (fontLocalPath != null) {
Log.d(TAG, "Local font version found, returning immediate.")
return FontResult.Immediate(loadFontIntoTypeface(context, version, fontLocalPath) ?: errorFallback.typeface)
}
val fontDownloadKey = FontDownloadKey(
version, locale, font
)
val taskInProgress = taskCache[fontDownloadKey]
return if (taskInProgress != null) {
Log.d(TAG, "Found a task in progress. Returning in-progress async.")
FontResult.Async(
future = taskInProgress,
placeholder = errorFallback.typeface
)
} else {
Log.d(TAG, "Could not find a task in progress. Returning new async.")
val newTask = ListenableFutureTask {
val newLocalPath = downloadFont(context, locale, font, version, manifest)
Log.d(TAG, "Finished download, $newLocalPath")
val typeface = newLocalPath?.let { loadFontIntoTypeface(context, version, it) } ?: errorFallback.typeface
taskCache.remove(fontDownloadKey)
typeface
}
taskCache[fontDownloadKey] = newTask
SignalExecutors.BOUNDED.execute(newTask::run)
FontResult.Async(
future = newTask,
placeholder = errorFallback.typeface
)
}
}
}
@WorkerThread
private fun loadFontIntoTypeface(context: Context, fontVersion: FontVersion, fontLocalPath: String): Typeface? {
return try {
Typeface.createFromFile(File(getDirectory(context), "${fontVersion.path}/$fontLocalPath"))
} catch (e: Exception) {
Log.w(TAG, "Could not load typeface from disk.")
null
}
}
/**
* Downloads the latest version code.
*/
@WorkerThread
fun downloadLatestVersionLong(): Long {
return S3.getLong(VERSION_URL)
}
/**
* Downloads and verifies the latest manifest.
*/
@WorkerThread
fun downloadAndVerifyLatestManifest(context: Context, version: FontVersion, manifestPath: String): Boolean {
return S3.verifyAndWriteToDisk(
context,
"$BASE_STATIC_BUCKET_URL/${version.id}/$MANIFEST",
File(getDirectory(context), manifestPath)
)
}
/**
* Downloads the given font file from S3
*/
@WorkerThread
private fun downloadFont(context: Context, locale: Locale, font: TextFont, fontVersion: FontVersion, fontManifest: FontManifest): String? {
val script: FontManifest.FontScript = resolveScriptNameFromLocale(locale, fontManifest) ?: return null
val path = getScriptPath(font, script)
val networkPath = "$BASE_STATIC_BUCKET_URL/${fontVersion.id}/$path"
val localUUID = UUID.randomUUID().toString()
val localPath = "${fontVersion.path}/" + localUUID
return if (S3.verifyAndWriteToDisk(context, networkPath, File(getDirectory(context), localPath), doNotEncrypt = true)) {
FontFileMap.put(context, fontVersion, localUUID, path)
localUUID
} else {
Log.w(TAG, "Failed to download and verify font.")
null
}
}
private fun getScriptPath(font: TextFont, script: FontManifest.FontScript): String {
return when (font) {
TextFont.REGULAR -> script.regular
TextFont.BOLD -> script.bold
TextFont.SERIF -> script.serif
TextFont.SCRIPT -> script.script
TextFont.CONDENSED -> script.condensed
}
}
private fun resolveScriptNameFromLocale(locale: Locale, fontManifest: FontManifest): FontManifest.FontScript? {
val fontScript: FontManifest.FontScript = when (ScriptUtil.getScript(locale).apply { Log.d(TAG, "Getting Script for $this") }) {
ScriptUtil.LATIN -> fontManifest.scripts.latinExtended
ScriptUtil.ARABIC -> fontManifest.scripts.arabic
ScriptUtil.CHINESE_SIMPLIFIED -> fontManifest.scripts.chineseSimplified
ScriptUtil.CHINESE_TRADITIONAL -> fontManifest.scripts.chineseTraditional
ScriptUtil.CYRILLIC -> fontManifest.scripts.cyrillicExtended
ScriptUtil.DEVANAGARI -> fontManifest.scripts.devanagari
ScriptUtil.JAPANESE -> fontManifest.scripts.japanese
else -> return null
}
return if (fontScript == fontManifest.scripts.chineseSimplified && locale.isO3Country == "HKG") {
fontManifest.scripts.chineseTraditionalHk
} else {
fontScript
}
}
/**
* A Typeface or an Async future retrieving a typeface with a placeholder.
*/
sealed class FontResult {
data class Immediate(val typeface: Typeface) : FontResult()
data class Async(val future: ListenableFutureTask<Typeface>, val placeholder: Typeface) : FontResult()
}
private data class FontDownloadKey(
val version: FontVersion,
val locale: Locale,
val font: TextFont
)
}

View File

@@ -0,0 +1,755 @@
package org.thoughtcrime.securesms.fonts;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/*
* Copyright 2013 Phil Brown
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* Get Script name by Locale
* <br>
* @author Phil Brown
* @since 9:47:09 AM Dec 20, 2013
*
*/
class ScriptUtil {
static final String LATIN = "Latn";
static final String CYRILLIC = "Cyrl";
static final String DEVANAGARI = "Deva";
static final String CHINESE_TRADITIONAL = "Hant";
static final String CHINESE_SIMPLIFIED = "Hans";
static final String ARABIC = "Arab";
static final String JAPANESE = "Jpan";
public static Map<String, Map<String, String>> SCRIPTS_BY_LOCALE = new HashMap<>();
public static Map<String, String> getScriptsMap(String... keyValuePairs)
{
Map<String, String> languages = new HashMap<String, String>();
for (int i = 0; i < keyValuePairs.length; i += 2) {
languages.put(keyValuePairs[i], keyValuePairs[i + 1]);
}
return languages;
}
static {
SCRIPTS_BY_LOCALE.put("aa", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ab", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("abq", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("abr", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ace", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ach", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ada", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ady", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("ae", getScriptsMap("", "Avst"));
SCRIPTS_BY_LOCALE.put("af", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("agq", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("aii", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("ain", getScriptsMap("", "Kana"));
SCRIPTS_BY_LOCALE.put("ak", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("akk", getScriptsMap("", "Xsux"));
SCRIPTS_BY_LOCALE.put("ale", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("alt", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("am", getScriptsMap("", "Ethi"));
SCRIPTS_BY_LOCALE.put("amo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("an", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("anp", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("aoz", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ar", getScriptsMap("", "Arab", "IR", "Syrc"));
SCRIPTS_BY_LOCALE.put("arc", getScriptsMap("", "Armi"));
SCRIPTS_BY_LOCALE.put("arn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("arp", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("arw", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("as", getScriptsMap("", "Beng"));
SCRIPTS_BY_LOCALE.put("asa", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ast", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("atj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("av", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("awa", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("ay", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("az", getScriptsMap("", "Latn", "AZ", "Cyrl", "IR", "Arab"));
SCRIPTS_BY_LOCALE.put("ba", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("bal", getScriptsMap("", "Arab", "IR", "Latn", "PK", "Latn"));
SCRIPTS_BY_LOCALE.put("ban", getScriptsMap("", "Latn", "ID", "Bali"));
SCRIPTS_BY_LOCALE.put("bap", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bas", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bax", getScriptsMap("", "Bamu"));
SCRIPTS_BY_LOCALE.put("bbc", getScriptsMap("", "Latn", "ID", "Batk"));
SCRIPTS_BY_LOCALE.put("bbj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bci", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("be", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("bej", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("bem", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bew", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bez", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bfd", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bfq", getScriptsMap("", "Taml"));
SCRIPTS_BY_LOCALE.put("bft", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("bfy", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("bg", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("bgc", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bgx", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bh", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("bhb", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("bhi", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bhk", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bho", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("bi", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bik", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bin", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bjj", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("bjn", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bkm", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bku", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bla", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("blt", getScriptsMap("", "Tavt"));
SCRIPTS_BY_LOCALE.put("bm", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bmq", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bn", getScriptsMap("", "Beng"));
SCRIPTS_BY_LOCALE.put("bo", getScriptsMap("", "Tibt"));
SCRIPTS_BY_LOCALE.put("bqi", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bqv", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("br", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bra", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("brh", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("brx", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("bs", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bss", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bto", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("btv", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("bua", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("buc", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("bug", getScriptsMap("", "Latn", "ID", "Bugi"));
SCRIPTS_BY_LOCALE.put("bum", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bvb", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bya", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("byn", getScriptsMap("", "Ethi"));
SCRIPTS_BY_LOCALE.put("byv", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bze", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("bzx", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ca", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("cad", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("car", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("cay", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("cch", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ccp", getScriptsMap("", "Beng"));
SCRIPTS_BY_LOCALE.put("ce", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("ceb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("cgg", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ch", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("chk", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("chm", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("chn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("cho", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("chp", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("chr", getScriptsMap("", "Cher"));
SCRIPTS_BY_LOCALE.put("chy", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("cja", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("cjm", getScriptsMap("", "Cham"));
SCRIPTS_BY_LOCALE.put("cjs", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("ckb", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("ckt", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("co", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("cop", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("cpe", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("cr", getScriptsMap("", "Cans"));
SCRIPTS_BY_LOCALE.put("crh", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("crj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("crk", getScriptsMap("", "Cans"));
SCRIPTS_BY_LOCALE.put("crl", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("crm", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("crs", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("cs", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("csb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("csw", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("cu", getScriptsMap("", "Glag"));
SCRIPTS_BY_LOCALE.put("cv", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("cy", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("da", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("daf", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("dak", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("dar", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("dav", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("dcc", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("de", getScriptsMap("", "Latn", "BR", "Runr", "KZ", "Runr", "US", "Runr"));
SCRIPTS_BY_LOCALE.put("del", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("den", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("dgr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("din", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("dje", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("dng", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("doi", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("dsb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("dtm", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("dua", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("dv", getScriptsMap("", "Thaa"));
SCRIPTS_BY_LOCALE.put("dyo", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("dyu", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("dz", getScriptsMap("", "Tibt"));
SCRIPTS_BY_LOCALE.put("ebu", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ee", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("efi", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("egy", getScriptsMap("", "Egyp"));
SCRIPTS_BY_LOCALE.put("eka", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("eky", getScriptsMap("", "Kali"));
SCRIPTS_BY_LOCALE.put("el", getScriptsMap("", "Grek"));
SCRIPTS_BY_LOCALE.put("en", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("eo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("es", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("et", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ett", getScriptsMap("", "Ital"));
SCRIPTS_BY_LOCALE.put("eu", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("evn", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("ewo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("fa", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("fan", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ff", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ffm", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("fi", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("fil", getScriptsMap("", "Latn", "US", "Tglg"));
SCRIPTS_BY_LOCALE.put("fiu", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("fj", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("fo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("fon", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("fr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("frr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("frs", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("fud", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("fuq", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("fur", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("fuv", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("fy", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ga", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gaa", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gag", getScriptsMap("", "Latn", "MD", "Cyrl"));
SCRIPTS_BY_LOCALE.put("gay", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gba", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("gbm", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("gcr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gd", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gez", getScriptsMap("", "Ethi"));
SCRIPTS_BY_LOCALE.put("ggn", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("gil", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gjk", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("gju", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("gl", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gld", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("glk", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("gn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gon", getScriptsMap("", "Telu"));
SCRIPTS_BY_LOCALE.put("gor", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gos", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("got", getScriptsMap("", "Goth"));
SCRIPTS_BY_LOCALE.put("grb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("grc", getScriptsMap("", "Cprt"));
SCRIPTS_BY_LOCALE.put("grt", getScriptsMap("", "Beng"));
SCRIPTS_BY_LOCALE.put("gsw", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gu", getScriptsMap("", "Gujr"));
SCRIPTS_BY_LOCALE.put("gub", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("guz", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gv", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("gvr", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("gwi", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ha", getScriptsMap("", "Arab", "NE", "Latn", "GH", "Latn"));
SCRIPTS_BY_LOCALE.put("hai", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("haw", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("haz", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("he", getScriptsMap("", "Hebr"));
SCRIPTS_BY_LOCALE.put("hi", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("hil", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("hit", getScriptsMap("", "Xsux"));
SCRIPTS_BY_LOCALE.put("hmn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("hnd", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("hne", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("hnn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("hno", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ho", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("hoc", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("hoj", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("hop", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("hr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("hsb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ht", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("hu", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("hup", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("hy", getScriptsMap("", "Armn"));
SCRIPTS_BY_LOCALE.put("hz", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ia", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("iba", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ibb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("id", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ig", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ii", getScriptsMap("", "Yiii", "CN", "Latn"));
SCRIPTS_BY_LOCALE.put("ik", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ikt", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ilo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("inh", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("is", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("it", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("iu", getScriptsMap("", "Cans", "CA", "Latn"));
SCRIPTS_BY_LOCALE.put("ja", getScriptsMap("", "Jpan"));
SCRIPTS_BY_LOCALE.put("jmc", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("jml", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("jpr", getScriptsMap("", "Hebr"));
SCRIPTS_BY_LOCALE.put("jrb", getScriptsMap("", "Hebr"));
SCRIPTS_BY_LOCALE.put("jv", getScriptsMap("", "Latn", "ID", "Java"));
SCRIPTS_BY_LOCALE.put("ka", getScriptsMap("", "Geor"));
SCRIPTS_BY_LOCALE.put("kaa", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kab", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kac", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kaj", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kam", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kao", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kbd", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kca", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kcg", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kck", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kde", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kdt", getScriptsMap("", "Thai"));
SCRIPTS_BY_LOCALE.put("kea", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kfo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kfr", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("kfy", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kg", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kge", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kgp", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kha", getScriptsMap("", "Latn", "IN", "Beng"));
SCRIPTS_BY_LOCALE.put("khb", getScriptsMap("", "Talu"));
SCRIPTS_BY_LOCALE.put("khn", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("khq", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kht", getScriptsMap("", "Mymr"));
SCRIPTS_BY_LOCALE.put("khw", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ki", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kj", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kjg", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kjh", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kk", getScriptsMap("", "Arab", "KZ", "Cyrl", "TR", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kkj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kl", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kln", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("km", getScriptsMap("", "Khmr"));
SCRIPTS_BY_LOCALE.put("kmb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kn", getScriptsMap("", "Knda"));
SCRIPTS_BY_LOCALE.put("ko", getScriptsMap("", "Kore"));
SCRIPTS_BY_LOCALE.put("koi", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kok", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("kos", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kpe", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kpy", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("krc", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kri", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("krl", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kru", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("ks", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("ksb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ksf", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ksh", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ku", getScriptsMap("", "Latn", "LB", "Arab"));
SCRIPTS_BY_LOCALE.put("kum", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kut", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kv", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("kvr", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kvx", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kw", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("kxm", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("kxp", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ky", getScriptsMap("", "Cyrl", "CN", "Arab", "TR", "Latn"));
SCRIPTS_BY_LOCALE.put("kyu", getScriptsMap("", "Kali"));
SCRIPTS_BY_LOCALE.put("la", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lad", getScriptsMap("", "Hebr"));
SCRIPTS_BY_LOCALE.put("lag", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lah", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("laj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("lam", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lbe", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("lbw", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("lcp", getScriptsMap("", "Thai"));
SCRIPTS_BY_LOCALE.put("lep", getScriptsMap("", "Lepc"));
SCRIPTS_BY_LOCALE.put("lez", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("lg", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("li", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lif", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("lis", getScriptsMap("", "Lisu"));
SCRIPTS_BY_LOCALE.put("ljp", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("lki", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("lkt", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("lmn", getScriptsMap("", "Telu"));
SCRIPTS_BY_LOCALE.put("lmo", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ln", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lo", getScriptsMap("", "Laoo"));
SCRIPTS_BY_LOCALE.put("lol", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("loz", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lrc", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("lt", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lu", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lua", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lui", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lun", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("luo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lus", getScriptsMap("", "Beng"));
SCRIPTS_BY_LOCALE.put("lut", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("luy", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("luz", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("lv", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("lwl", getScriptsMap("", "Thai"));
SCRIPTS_BY_LOCALE.put("mad", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("maf", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("mag", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("mai", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("mak", getScriptsMap("", "Latn", "ID", "Bugi"));
SCRIPTS_BY_LOCALE.put("man", getScriptsMap("", "Latn", "GN", "Nkoo"));
SCRIPTS_BY_LOCALE.put("mas", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("maz", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("mdf", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("mdh", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mdr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mdt", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("men", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mer", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mfa", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("mfe", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mg", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mgh", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mgp", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("mgy", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("mh", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mi", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mic", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("min", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mk", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("ml", getScriptsMap("", "Mlym"));
SCRIPTS_BY_LOCALE.put("mn", getScriptsMap("", "Cyrl", "CN", "Mong"));
SCRIPTS_BY_LOCALE.put("mnc", getScriptsMap("", "Mong"));
SCRIPTS_BY_LOCALE.put("mni", getScriptsMap("", "Beng", "IN", "Mtei"));
SCRIPTS_BY_LOCALE.put("mns", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("mnw", getScriptsMap("", "Mymr"));
SCRIPTS_BY_LOCALE.put("moe", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("moh", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mos", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mr", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("mrd", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("mrj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ms", getScriptsMap("", "Arab", "MY", "Latn", "SG", "Latn"));
SCRIPTS_BY_LOCALE.put("mt", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mtr", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("mua", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mus", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mvy", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("mwk", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("mwl", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("mwr", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("mxc", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("my", getScriptsMap("", "Mymr"));
SCRIPTS_BY_LOCALE.put("myv", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("myx", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("myz", getScriptsMap("", "Mand"));
SCRIPTS_BY_LOCALE.put("na", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nap", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("naq", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nbf", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("nch", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("nd", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ndc", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("nds", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ne", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("new", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("ng", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ngl", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("nhe", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("nhw", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("nia", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nij", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("niu", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nl", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nmg", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nnh", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("nod", getScriptsMap("", "Lana"));
SCRIPTS_BY_LOCALE.put("noe", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("nog", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("nqo", getScriptsMap("", "Nkoo"));
SCRIPTS_BY_LOCALE.put("nr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nsk", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("nso", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nus", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nv", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ny", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nym", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nyn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nyo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("nzi", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("oc", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("oj", getScriptsMap("", "Cans"));
SCRIPTS_BY_LOCALE.put("om", getScriptsMap("", "Latn", "ET", "Ethi"));
SCRIPTS_BY_LOCALE.put("or", getScriptsMap("", "Orya"));
SCRIPTS_BY_LOCALE.put("os", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("osa", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("osc", getScriptsMap("", "Ital"));
SCRIPTS_BY_LOCALE.put("otk", getScriptsMap("", "Orkh"));
SCRIPTS_BY_LOCALE.put("pa", getScriptsMap("", "Guru", "PK", "Arab"));
SCRIPTS_BY_LOCALE.put("pag", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("pal", getScriptsMap("", "Phli"));
SCRIPTS_BY_LOCALE.put("pam", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("pap", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("pau", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("peo", getScriptsMap("", "Xpeo"));
SCRIPTS_BY_LOCALE.put("phn", getScriptsMap("", "Phnx"));
SCRIPTS_BY_LOCALE.put("pi", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("pko", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("pl", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("pon", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("pra", getScriptsMap("", "Brah"));
SCRIPTS_BY_LOCALE.put("prd", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("prg", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("prs", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("ps", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("pt", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("puu", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("qu", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("raj", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("rap", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("rar", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("rcf", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("rej", getScriptsMap("", "Latn", "ID", "Rjng"));
SCRIPTS_BY_LOCALE.put("ria", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("rif", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("rjs", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("rkt", getScriptsMap("", "Beng"));
SCRIPTS_BY_LOCALE.put("rm", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("rmf", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("rmo", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("rmt", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("rn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("rng", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ro", getScriptsMap("", "Latn", "RS", "Cyrl"));
SCRIPTS_BY_LOCALE.put("rob", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("rof", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("rom", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("ru", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("rue", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("rup", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("rw", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("rwk", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ryu", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("sa", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("sad", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("saf", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sah", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("sam", getScriptsMap("", "Hebr"));
SCRIPTS_BY_LOCALE.put("saq", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sas", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sat", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("saz", getScriptsMap("", "Saur"));
SCRIPTS_BY_LOCALE.put("sbp", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sc", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sck", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("scn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sco", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("scs", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("sd", getScriptsMap("", "Arab", "IN", "Deva"));
SCRIPTS_BY_LOCALE.put("sdh", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("se", getScriptsMap("", "Latn", "NO", "Cyrl"));
SCRIPTS_BY_LOCALE.put("see", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sef", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("seh", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sel", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("ses", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sg", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sga", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("shi", getScriptsMap("", "Tfng"));
SCRIPTS_BY_LOCALE.put("shn", getScriptsMap("", "Mymr"));
SCRIPTS_BY_LOCALE.put("si", getScriptsMap("", "Sinh"));
SCRIPTS_BY_LOCALE.put("sid", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sk", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("skr", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("sl", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sm", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sma", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("smi", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("smj", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("smn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sms", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("snk", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("so", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("son", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sou", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("sq", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("srn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("srr", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("srx", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ss", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ssy", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("st", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("su", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("suk", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sus", getScriptsMap("", "Latn", "GN", "Arab"));
SCRIPTS_BY_LOCALE.put("sv", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("sw", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("swb", getScriptsMap("", "Arab", "YT", "Latn"));
SCRIPTS_BY_LOCALE.put("swc", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("swv", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("sxn", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("syi", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("syl", getScriptsMap("", "Beng", "BD", "Sylo"));
SCRIPTS_BY_LOCALE.put("syr", getScriptsMap("", "Syrc"));
SCRIPTS_BY_LOCALE.put("ta", getScriptsMap("", "Taml"));
SCRIPTS_BY_LOCALE.put("tab", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("taj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("tbw", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tcy", getScriptsMap("", "Knda"));
SCRIPTS_BY_LOCALE.put("tdd", getScriptsMap("", "Tale"));
SCRIPTS_BY_LOCALE.put("tdg", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("tdh", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("te", getScriptsMap("", "Telu"));
SCRIPTS_BY_LOCALE.put("tem", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("teo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ter", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tet", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tg", getScriptsMap("", "Cyrl", "PK", "Arab"));
SCRIPTS_BY_LOCALE.put("th", getScriptsMap("", "Thai"));
SCRIPTS_BY_LOCALE.put("thl", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("thq", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("thr", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("ti", getScriptsMap("", "Ethi"));
SCRIPTS_BY_LOCALE.put("tig", getScriptsMap("", "Ethi"));
SCRIPTS_BY_LOCALE.put("tiv", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tk", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tkl", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tkt", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("tli", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tmh", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tn", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("to", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tog", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tpi", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tr", getScriptsMap("", "Latn", "DE", "Arab", "MK", "Arab"));
SCRIPTS_BY_LOCALE.put("tru", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("trv", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ts", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tsf", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("tsg", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tsi", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tsj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("tt", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("ttj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("tts", getScriptsMap("", "Thai"));
SCRIPTS_BY_LOCALE.put("tum", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tut", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("tvl", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("twq", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ty", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("tyv", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("tzm", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ude", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("udm", getScriptsMap("", "Cyrl", "RU", "Latn"));
SCRIPTS_BY_LOCALE.put("ug", getScriptsMap("", "Arab", "KZ", "Cyrl", "MN", "Cyrl"));
SCRIPTS_BY_LOCALE.put("uga", getScriptsMap("", "Ugar"));
SCRIPTS_BY_LOCALE.put("uk", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("uli", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("umb", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("und", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("unr", getScriptsMap("", "Beng", "NP", "Deva"));
SCRIPTS_BY_LOCALE.put("unx", getScriptsMap("", "Beng"));
SCRIPTS_BY_LOCALE.put("ur", getScriptsMap("", "Arab"));
SCRIPTS_BY_LOCALE.put("uz", getScriptsMap("", "Latn", "AF", "Arab", "CN", "Cyrl"));
SCRIPTS_BY_LOCALE.put("vai", getScriptsMap("", "Vaii"));
SCRIPTS_BY_LOCALE.put("ve", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("vi", getScriptsMap("", "Latn", "US", "Hani"));
SCRIPTS_BY_LOCALE.put("vic", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("vmw", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("vo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("vot", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("vun", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("wa", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("wae", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("wak", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("wal", getScriptsMap("", "Ethi"));
SCRIPTS_BY_LOCALE.put("war", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("was", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("wbq", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("wbr", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("wls", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("wo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("wtm", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("xal", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("xav", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("xcr", getScriptsMap("", "Cari"));
SCRIPTS_BY_LOCALE.put("xh", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("xnr", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("xog", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("xpr", getScriptsMap("", "Prti"));
SCRIPTS_BY_LOCALE.put("xsa", getScriptsMap("", "Sarb"));
SCRIPTS_BY_LOCALE.put("xsr", getScriptsMap("", "Deva"));
SCRIPTS_BY_LOCALE.put("xum", getScriptsMap("", "Ital"));
SCRIPTS_BY_LOCALE.put("yao", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("yap", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("yav", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("ybb", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("yi", getScriptsMap("", "Hebr"));
SCRIPTS_BY_LOCALE.put("yo", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("yrk", getScriptsMap("", "Cyrl"));
SCRIPTS_BY_LOCALE.put("yua", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("yue", getScriptsMap("", "Hans"));
SCRIPTS_BY_LOCALE.put("za", getScriptsMap("", "Latn", "CN", "Hans"));
SCRIPTS_BY_LOCALE.put("zap", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("zdj", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("zea", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("zen", getScriptsMap("", "Tfng"));
SCRIPTS_BY_LOCALE.put("zh", getScriptsMap("", "Hant", "CN", "Hans", "HK", "Hans", "MO", "Hans", "SG", "Hans", "MN", "Hans"));
SCRIPTS_BY_LOCALE.put("zmi", getScriptsMap("", ""));
SCRIPTS_BY_LOCALE.put("zu", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("zun", getScriptsMap("", "Latn"));
SCRIPTS_BY_LOCALE.put("zza", getScriptsMap("", "Arab"));
}
/**
* Gets the script for the given locale. For example, if a US citizen uses German Locale,
* and calls this method with Locale.getDefault(), the result would be "Runr"
*
* @param locale
* @return
*/
public static String getScript(Locale locale)
{
String localeString = locale.toString();
String language = "";
String country = "";
if (localeString.contains("_")) {
String[] split = localeString.split("_");
language = split[0];
country = split[1];
} else
language = localeString;
Map<String, String> scripts = SCRIPTS_BY_LOCALE.get(language);
if (scripts == null) {
return null;
} else {
if (scripts.containsKey(country)) {
return scripts.get(country);
} else {
return scripts.get("");
}
}
}
}

View File

@@ -0,0 +1,16 @@
package org.thoughtcrime.securesms.fonts
import android.graphics.Typeface
import androidx.annotation.DrawableRes
import org.thoughtcrime.securesms.R
/**
* Describes which font the user wishes to render content in.
*/
enum class TextFont(@DrawableRes val icon: Int, val fallbackFamily: String, val fallbackStyle: Int, val isAllCaps: Boolean) {
REGULAR(R.drawable.ic_font_regular, "sans-serif", Typeface.NORMAL, false),
BOLD(R.drawable.ic_font_bold, "sans-serif", Typeface.BOLD, false),
SERIF(R.drawable.ic_font_serif, "serif", Typeface.NORMAL, false),
SCRIPT(R.drawable.ic_font_script, "serif", Typeface.BOLD, false),
CONDENSED(R.drawable.ic_font_condensed, "sans-serif", Typeface.BOLD, true);
}