feat: add language package with core types and helpers

This commit is contained in:
leonardotrapani
2026-02-01 00:24:13 +01:00
parent 38168fdcae
commit 3a1b75f58d
4 changed files with 251 additions and 1 deletions
+120
View File
@@ -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
}
+119
View File
@@ -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)
}
}
+11
View File
@@ -0,0 +1,11 @@
# Ralph Progress Log
Started: Sun Feb 1 12:22:47 AM CET 2026
---
## Completed
### Task 1: Create language package with core types and helpers
- Created `internal/language/language.go` with Language struct, Auto constant
- Implemented FromCode, List, Codes, AllLanguageCodes, IsValidCode
- Full 57 language list from OpenAI Whisper
- All tests passing, typecheck passes
+1 -1
View File
@@ -26,7 +26,7 @@
"IsValidCode('') returns true (auto is valid)", "IsValidCode('') returns true (auto is valid)",
"Typecheck passes" "Typecheck passes"
], ],
"passes": false "passes": true
}, },
{ {
"title": "Add language list and provider-specific mappings", "title": "Add language list and provider-specific mappings",