15 changed files with 243 additions and 112 deletions
@ -1,14 +0,0 @@
@@ -1,14 +0,0 @@
|
||||
import { getTag } from "@sozialhelden/ietf-language-tags"; |
||||
|
||||
export function getLanguageFromIETF(ietf: string): string | null { |
||||
const tag = getTag(ietf, true); |
||||
|
||||
const lang = tag?.language?.Description?.[0] ?? null; |
||||
if (!lang) return null; |
||||
|
||||
const region = tag?.region?.Description?.[0] ?? null; |
||||
let regionText = ""; |
||||
if (region) regionText = ` (${region})`; |
||||
|
||||
return `${lang}${regionText}`; |
||||
} |
@ -0,0 +1,188 @@
@@ -0,0 +1,188 @@
|
||||
import countryLanguages from "@ladjs/country-language"; |
||||
import { getTag } from "@sozialhelden/ietf-language-tags"; |
||||
|
||||
const languageOrder = ["en", "hi", "fr", "de", "nl", "pt"]; |
||||
|
||||
// mapping of language code to country code.
|
||||
// multiple mappings can exist, since languages are spoken in multiple countries.
|
||||
// This mapping purely exists to prioritize a country over another in languages.
|
||||
// iso639_1 -> iso3166 Alpha-2
|
||||
const countryPriority: Record<string, string> = { |
||||
en: "gb", |
||||
nl: "nl", |
||||
fr: "fr", |
||||
de: "de", |
||||
pt: "pt", |
||||
ar: "sa", |
||||
es: "es", |
||||
zh: "cn", |
||||
}; |
||||
|
||||
// list of iso639_1 Alpha-2 codes used as default languages
|
||||
const defaultLanguageCodes: string[] = [ |
||||
"en-US", |
||||
"cs-CZ", |
||||
"de-DE", |
||||
"fr-FR", |
||||
"pt-BR", |
||||
"it-IT", |
||||
"nl-NL", |
||||
"pl-PL", |
||||
"tr-TR", |
||||
"vi-VN", |
||||
"zh-CN", |
||||
"he-IL", |
||||
"sv-SE", |
||||
"lv-LV", |
||||
"th-TH", |
||||
"ne-NP", |
||||
"ar-SA", |
||||
"es-ES", |
||||
"et-EE", |
||||
]; |
||||
|
||||
export interface LocaleInfo { |
||||
name: string; |
||||
nativeName?: string; |
||||
code: string; |
||||
isRtl?: boolean; |
||||
} |
||||
|
||||
interface LanguageObj { |
||||
countries: Array<{ |
||||
code_2: string; |
||||
code_3: string; |
||||
numCode: string; |
||||
}>; |
||||
direction: "RTL" | "LTR"; |
||||
name: string[]; |
||||
nativeName: string[]; |
||||
iso639_1: string; |
||||
} |
||||
|
||||
const extraLanguages: Record<string, LocaleInfo> = { |
||||
pirate: { |
||||
code: "pirate", |
||||
name: "Pirate", |
||||
nativeName: "Pirate Tongue", |
||||
}, |
||||
minion: { |
||||
code: "minion", |
||||
name: "Minion", |
||||
nativeName: "Minionese", |
||||
}, |
||||
tok: { |
||||
code: "tok", |
||||
name: "Toki pona", |
||||
nativeName: "Toki pona", |
||||
}, |
||||
}; |
||||
|
||||
function populateLanguageCode(language: string): string { |
||||
if (language.includes("-")) return language; |
||||
if (language.length !== 2) return language; |
||||
return ( |
||||
defaultLanguageCodes.find((v) => v.startsWith(`${language}-`)) ?? language |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @param locale idk what kinda code this takes, anytihhng in ietf format I guess |
||||
* @returns pretty format for language, null if it no info can be found for language |
||||
*/ |
||||
export function getPrettyLanguageNameFromLocale(locale: string): string | null { |
||||
const tag = getTag(populateLanguageCode(locale), true); |
||||
|
||||
const lang = tag?.language?.Description?.[0] ?? null; |
||||
if (!lang) return null; |
||||
|
||||
const region = tag?.region?.Description?.[0] ?? null; |
||||
let regionText = ""; |
||||
if (region) regionText = ` (${region})`; |
||||
|
||||
return `${lang}${regionText}`; |
||||
} |
||||
|
||||
/** |
||||
* Sort locale codes by occurance, rest on alphabetical order |
||||
* @param langCodes list language codes to sort |
||||
* @returns sorted version of inputted list |
||||
*/ |
||||
export function sortLangCodes(langCodes: string[]) { |
||||
const languagesOrder = [...languageOrder].reverse(); // Reverse is neccesary, not sure why
|
||||
|
||||
const results = langCodes.sort((a, b) => { |
||||
const langOrderA = languagesOrder.findIndex( |
||||
(v) => a.startsWith(`${v}-`) || a === v, |
||||
); |
||||
const langOrderB = languagesOrder.findIndex( |
||||
(v) => b.startsWith(`${v}-`) || b === v, |
||||
); |
||||
if (langOrderA !== -1 || langOrderB !== -1) return langOrderB - langOrderA; |
||||
|
||||
return a.localeCompare(b); |
||||
}); |
||||
|
||||
return results; |
||||
} |
||||
|
||||
/** |
||||
* Get country code for locale |
||||
* @param locale input locale |
||||
* @returns country code or null |
||||
*/ |
||||
export function getCountryCodeForLocale(locale: string): string | null { |
||||
let output: LanguageObj | null = null as any as LanguageObj; |
||||
const tag = getTag(locale, true); |
||||
if (!tag?.language?.Subtag) return null; |
||||
// this function isnt async, so its garuanteed to work like this
|
||||
countryLanguages.getLanguage( |
||||
tag.language.Subtag, |
||||
(_err: string, lang: LanguageObj) => { |
||||
if (lang) output = lang; |
||||
}, |
||||
); |
||||
if (!output) return null; |
||||
if (output.countries.length === 0) return null; |
||||
const priority = countryPriority[output.iso639_1.toLowerCase()]; |
||||
if (priority) { |
||||
const priotizedCountry = output.countries.find( |
||||
(v) => v.code_2.toLowerCase() === priority, |
||||
); |
||||
if (priotizedCountry) return priotizedCountry.code_2.toLowerCase(); |
||||
} |
||||
return output.countries[0].code_2.toLowerCase(); |
||||
} |
||||
|
||||
/** |
||||
* Get information for a specific local |
||||
* @param locale local code |
||||
* @returns locale object |
||||
*/ |
||||
export function getLocaleInfo(locale: string): LocaleInfo | null { |
||||
const realLocale = populateLanguageCode(locale); |
||||
const extraLang = extraLanguages[realLocale]; |
||||
if (extraLang) return extraLang; |
||||
|
||||
const tag = getTag(realLocale, true); |
||||
if (!tag?.language?.Subtag) return null; |
||||
|
||||
let output: LanguageObj | null = null as any as LanguageObj; |
||||
// this function isnt async, so its garuanteed to work like this
|
||||
countryLanguages.getLanguage( |
||||
tag.language.Subtag, |
||||
(_err: string, lang: LanguageObj) => { |
||||
if (lang) output = lang; |
||||
}, |
||||
); |
||||
if (!output) return null; |
||||
|
||||
return { |
||||
code: tag.parts.langtag ?? realLocale, |
||||
isRtl: output.direction === "RTL", |
||||
name: |
||||
output.name[0] + |
||||
(tag.region?.Description ? ` (${tag.region.Description[0]})` : ""), |
||||
nativeName: output.nativeName[0] ?? undefined, |
||||
}; |
||||
} |
@ -1,12 +0,0 @@
@@ -1,12 +0,0 @@
|
||||
export function sortLangCodes(langCodes: string[]) { |
||||
const languagesOrder = ["en", "hi", "fr", "de", "nl", "pt"].reverse(); // Reverse is neccesary, not sure why
|
||||
|
||||
const results = langCodes.sort((a, b) => { |
||||
if (languagesOrder.indexOf(b) !== -1 || languagesOrder.indexOf(a) !== -1) |
||||
return languagesOrder.indexOf(b) - languagesOrder.indexOf(a); |
||||
|
||||
return a.localeCompare(b); |
||||
}); |
||||
|
||||
return results; |
||||
} |
Loading…
Reference in new issue