feat: add language package with core types and helpers
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package language
|
||||
|
||||
// Language represents a supported transcription language
|
||||
type Language struct {
|
||||
Code string // ISO 639-1 code (e.g., "en", "es", "zh")
|
||||
Name string // English name (e.g., "English", "Spanish")
|
||||
NativeName string // Native name (e.g., "English", "Espanol", "中文")
|
||||
}
|
||||
|
||||
// Auto represents auto-detection - used when user doesn't specify a language
|
||||
var Auto = Language{Code: "", Name: "Auto-detect", NativeName: ""}
|
||||
|
||||
// languages is the master list of supported languages
|
||||
// derived from OpenAI Whisper's 57 supported languages
|
||||
var languages = []Language{
|
||||
{Code: "af", Name: "Afrikaans", NativeName: "Afrikaans"},
|
||||
{Code: "ar", Name: "Arabic", NativeName: "العربية"},
|
||||
{Code: "hy", Name: "Armenian", NativeName: "Հdelays"},
|
||||
{Code: "az", Name: "Azerbaijani", NativeName: "Azərbaycan"},
|
||||
{Code: "be", Name: "Belarusian", NativeName: "Беларуская"},
|
||||
{Code: "bs", Name: "Bosnian", NativeName: "Bosanski"},
|
||||
{Code: "bg", Name: "Bulgarian", NativeName: "Български"},
|
||||
{Code: "ca", Name: "Catalan", NativeName: "Català"},
|
||||
{Code: "zh", Name: "Chinese", NativeName: "中文"},
|
||||
{Code: "hr", Name: "Croatian", NativeName: "Hrvatski"},
|
||||
{Code: "cs", Name: "Czech", NativeName: "Čeština"},
|
||||
{Code: "da", Name: "Danish", NativeName: "Dansk"},
|
||||
{Code: "nl", Name: "Dutch", NativeName: "Nederlands"},
|
||||
{Code: "en", Name: "English", NativeName: "English"},
|
||||
{Code: "et", Name: "Estonian", NativeName: "Eesti"},
|
||||
{Code: "fi", Name: "Finnish", NativeName: "Suomi"},
|
||||
{Code: "fr", Name: "French", NativeName: "Français"},
|
||||
{Code: "gl", Name: "Galician", NativeName: "Galego"},
|
||||
{Code: "de", Name: "German", NativeName: "Deutsch"},
|
||||
{Code: "el", Name: "Greek", NativeName: "Ελληνικά"},
|
||||
{Code: "he", Name: "Hebrew", NativeName: "עברית"},
|
||||
{Code: "hi", Name: "Hindi", NativeName: "हिन्दी"},
|
||||
{Code: "hu", Name: "Hungarian", NativeName: "Magyar"},
|
||||
{Code: "is", Name: "Icelandic", NativeName: "Íslenska"},
|
||||
{Code: "id", Name: "Indonesian", NativeName: "Bahasa Indonesia"},
|
||||
{Code: "it", Name: "Italian", NativeName: "Italiano"},
|
||||
{Code: "ja", Name: "Japanese", NativeName: "日本語"},
|
||||
{Code: "kn", Name: "Kannada", NativeName: "ಕನ್ನಡ"},
|
||||
{Code: "kk", Name: "Kazakh", NativeName: "Қазақ"},
|
||||
{Code: "ko", Name: "Korean", NativeName: "한국어"},
|
||||
{Code: "lv", Name: "Latvian", NativeName: "Latviešu"},
|
||||
{Code: "lt", Name: "Lithuanian", NativeName: "Lietuvių"},
|
||||
{Code: "mk", Name: "Macedonian", NativeName: "Македонски"},
|
||||
{Code: "ms", Name: "Malay", NativeName: "Bahasa Melayu"},
|
||||
{Code: "mr", Name: "Marathi", NativeName: "मराठी"},
|
||||
{Code: "mi", Name: "Maori", NativeName: "Māori"},
|
||||
{Code: "ne", Name: "Nepali", NativeName: "नेपाली"},
|
||||
{Code: "no", Name: "Norwegian", NativeName: "Norsk"},
|
||||
{Code: "fa", Name: "Persian", NativeName: "فارسی"},
|
||||
{Code: "pl", Name: "Polish", NativeName: "Polski"},
|
||||
{Code: "pt", Name: "Portuguese", NativeName: "Português"},
|
||||
{Code: "ro", Name: "Romanian", NativeName: "Română"},
|
||||
{Code: "ru", Name: "Russian", NativeName: "Русский"},
|
||||
{Code: "sr", Name: "Serbian", NativeName: "Српски"},
|
||||
{Code: "sk", Name: "Slovak", NativeName: "Slovenčina"},
|
||||
{Code: "sl", Name: "Slovenian", NativeName: "Slovenščina"},
|
||||
{Code: "es", Name: "Spanish", NativeName: "Español"},
|
||||
{Code: "sw", Name: "Swahili", NativeName: "Kiswahili"},
|
||||
{Code: "sv", Name: "Swedish", NativeName: "Svenska"},
|
||||
{Code: "tl", Name: "Tagalog", NativeName: "Tagalog"},
|
||||
{Code: "ta", Name: "Tamil", NativeName: "தமிழ்"},
|
||||
{Code: "th", Name: "Thai", NativeName: "ไทย"},
|
||||
{Code: "tr", Name: "Turkish", NativeName: "Türkçe"},
|
||||
{Code: "uk", Name: "Ukrainian", NativeName: "Українська"},
|
||||
{Code: "ur", Name: "Urdu", NativeName: "اردو"},
|
||||
{Code: "vi", Name: "Vietnamese", NativeName: "Tiếng Việt"},
|
||||
{Code: "cy", Name: "Welsh", NativeName: "Cymraeg"},
|
||||
}
|
||||
|
||||
// codeIndex maps language codes to their Language structs for fast lookup
|
||||
var codeIndex map[string]Language
|
||||
|
||||
func init() {
|
||||
codeIndex = make(map[string]Language, len(languages)+1)
|
||||
codeIndex[""] = Auto // auto-detect is valid
|
||||
for _, lang := range languages {
|
||||
codeIndex[lang.Code] = lang
|
||||
}
|
||||
}
|
||||
|
||||
// FromCode returns the Language for the given code.
|
||||
// Returns Auto if code is not found.
|
||||
func FromCode(code string) Language {
|
||||
if lang, ok := codeIndex[code]; ok {
|
||||
return lang
|
||||
}
|
||||
return Auto
|
||||
}
|
||||
|
||||
// List returns all supported languages (excluding Auto)
|
||||
func List() []Language {
|
||||
result := make([]Language, len(languages))
|
||||
copy(result, languages)
|
||||
return result
|
||||
}
|
||||
|
||||
// Codes returns all language codes (excluding empty string for auto)
|
||||
func Codes() []string {
|
||||
codes := make([]string, len(languages))
|
||||
for i, lang := range languages {
|
||||
codes[i] = lang.Code
|
||||
}
|
||||
return codes
|
||||
}
|
||||
|
||||
// AllLanguageCodes is an alias for Codes - used by models that support all languages
|
||||
func AllLanguageCodes() []string {
|
||||
return Codes()
|
||||
}
|
||||
|
||||
// IsValidCode returns true if the code is recognized (including empty for auto)
|
||||
func IsValidCode(code string) bool {
|
||||
_, ok := codeIndex[code]
|
||||
return ok
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package language
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFromCode(t *testing.T) {
|
||||
tests := []struct {
|
||||
code string
|
||||
wantCode string
|
||||
wantName string
|
||||
}{
|
||||
{"en", "en", "English"},
|
||||
{"es", "es", "Spanish"},
|
||||
{"zh", "zh", "Chinese"},
|
||||
{"invalid", "", "Auto-detect"},
|
||||
{"", "", "Auto-detect"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.code, func(t *testing.T) {
|
||||
got := FromCode(tt.code)
|
||||
if got.Code != tt.wantCode {
|
||||
t.Errorf("FromCode(%q).Code = %q, want %q", tt.code, got.Code, tt.wantCode)
|
||||
}
|
||||
if got.Name != tt.wantName {
|
||||
t.Errorf("FromCode(%q).Name = %q, want %q", tt.code, got.Name, tt.wantName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromCodeEnglish(t *testing.T) {
|
||||
lang := FromCode("en")
|
||||
if lang.Code != "en" {
|
||||
t.Errorf("FromCode('en').Code = %q, want 'en'", lang.Code)
|
||||
}
|
||||
if lang.Name != "English" {
|
||||
t.Errorf("FromCode('en').Name = %q, want 'English'", lang.Name)
|
||||
}
|
||||
if lang.NativeName != "English" {
|
||||
t.Errorf("FromCode('en').NativeName = %q, want 'English'", lang.NativeName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidCode(t *testing.T) {
|
||||
tests := []struct {
|
||||
code string
|
||||
want bool
|
||||
}{
|
||||
{"en", true},
|
||||
{"es", true},
|
||||
{"zh", true},
|
||||
{"invalid", false},
|
||||
{"", true}, // auto is valid
|
||||
{"xyz", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.code, func(t *testing.T) {
|
||||
got := IsValidCode(tt.code)
|
||||
if got != tt.want {
|
||||
t.Errorf("IsValidCode(%q) = %v, want %v", tt.code, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
list := List()
|
||||
if len(list) != 57 {
|
||||
t.Errorf("List() returned %d languages, want 57", len(list))
|
||||
}
|
||||
|
||||
// verify English is in the list
|
||||
found := false
|
||||
for _, lang := range list {
|
||||
if lang.Code == "en" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("List() does not contain English")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodes(t *testing.T) {
|
||||
codes := Codes()
|
||||
if len(codes) != 57 {
|
||||
t.Errorf("Codes() returned %d codes, want 57", len(codes))
|
||||
}
|
||||
|
||||
// verify 'en' is in the codes
|
||||
found := false
|
||||
for _, code := range codes {
|
||||
if code == "en" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("Codes() does not contain 'en'")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllLanguageCodes(t *testing.T) {
|
||||
codes := AllLanguageCodes()
|
||||
if len(codes) != 57 {
|
||||
t.Errorf("AllLanguageCodes() returned %d codes, want 57", len(codes))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuto(t *testing.T) {
|
||||
if Auto.Code != "" {
|
||||
t.Errorf("Auto.Code = %q, want empty string", Auto.Code)
|
||||
}
|
||||
if Auto.Name != "Auto-detect" {
|
||||
t.Errorf("Auto.Name = %q, want 'Auto-detect'", Auto.Name)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user