refactor(i18n): slim locale files using composable grammar
Reduce locale files from ~4600 to 338 total lines (93% reduction):
- en_GB.json: 1520 → 262 lines (83% reduction)
- en_US.json: ~1500 → 10 lines (US spelling overrides only)
- en_AU.json: ~1500 → 2 lines (inherits from en_GB)
- de.json: 120 → 64 lines
Removed redundant entries that can now be composed via core.* patterns:
- Labels: T("core.label.status") → "Status:"
- Counts: T("core.count.item", 5) → "5 items"
- Progress: T("core.progress.build") → "Building..."
- Done/Fail: T("core.done.build", subj) → "Build completed"
Updated tests to use new key patterns.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
621540433b
commit
39de3c2836
7 changed files with 263 additions and 4124 deletions
|
|
@ -23,8 +23,8 @@ func TestTranslate(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Basic translation
|
// Basic translation
|
||||||
result := svc.T("cli.success")
|
result := svc.T("cmd.dev.short")
|
||||||
assert.Equal(t, "Success", result)
|
assert.Equal(t, "Multi-repo development workflow", result)
|
||||||
|
|
||||||
// Missing key returns the key
|
// Missing key returns the key
|
||||||
result = svc.T("nonexistent.key")
|
result = svc.T("nonexistent.key")
|
||||||
|
|
@ -36,11 +36,11 @@ func TestTranslateWithArgs(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Translation with template data
|
// Translation with template data
|
||||||
result := svc.T("error.not_found", map[string]string{"Item": "config.yaml"})
|
result := svc.T("error.repo_not_found", map[string]string{"Name": "config.yaml"})
|
||||||
assert.Equal(t, "Not found: config.yaml", result)
|
assert.Equal(t, "Repository 'config.yaml' not found", result)
|
||||||
|
|
||||||
result = svc.T("cli.time.minutes_ago", map[string]int{"Count": 5})
|
result = svc.T("cmd.ai.task_pr.branch_error", map[string]string{"Branch": "main"})
|
||||||
assert.Equal(t, "5 minutes ago", result)
|
assert.Equal(t, "cannot create PR from main branch; create a feature branch first", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetLanguage(t *testing.T) {
|
func TestSetLanguage(t *testing.T) {
|
||||||
|
|
@ -71,8 +71,8 @@ func TestDefaultService(t *testing.T) {
|
||||||
require.NotNil(t, svc)
|
require.NotNil(t, svc)
|
||||||
|
|
||||||
// Global T function should work
|
// Global T function should work
|
||||||
result := T("cli.success")
|
result := T("cmd.dev.short")
|
||||||
assert.Equal(t, "Success", result)
|
assert.Equal(t, "Multi-repo development workflow", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddMessages(t *testing.T) {
|
func TestAddMessages(t *testing.T) {
|
||||||
|
|
@ -138,17 +138,18 @@ func TestDetectLanguage(t *testing.T) {
|
||||||
func TestPluralization(t *testing.T) {
|
func TestPluralization(t *testing.T) {
|
||||||
svc, err := New()
|
svc, err := New()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
SetDefault(svc)
|
||||||
|
|
||||||
// Singular
|
// Singular - uses core.count.* magic
|
||||||
result := svc.T("cli.count.items", map[string]any{"Count": 1})
|
result := svc.T("core.count.item", 1)
|
||||||
assert.Equal(t, "1 item", result)
|
assert.Equal(t, "1 item", result)
|
||||||
|
|
||||||
// Plural
|
// Plural
|
||||||
result = svc.T("cli.count.items", map[string]any{"Count": 5})
|
result = svc.T("core.count.item", 5)
|
||||||
assert.Equal(t, "5 items", result)
|
assert.Equal(t, "5 items", result)
|
||||||
|
|
||||||
// Zero uses plural
|
// Zero uses plural
|
||||||
result = svc.T("cli.count.items", map[string]any{"Count": 0})
|
result = svc.T("core.count.item", 0)
|
||||||
assert.Equal(t, "0 items", result)
|
assert.Equal(t, "0 items", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,13 +157,13 @@ func TestNestedKeys(t *testing.T) {
|
||||||
svc, err := New()
|
svc, err := New()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Deeply nested key
|
// Nested key
|
||||||
result := svc.T("cmd.dev.work.short")
|
result := svc.T("cmd.dev.short")
|
||||||
assert.Equal(t, "Multi-repo git operations", result)
|
assert.Equal(t, "Multi-repo development workflow", result)
|
||||||
|
|
||||||
// Nested with flag
|
// Deeper nested key (flat key with dots)
|
||||||
result = svc.T("cmd.dev.work.flag.status")
|
result = svc.T("cmd.dev.push.short")
|
||||||
assert.Equal(t, "Show status only, don't push", result)
|
assert.Equal(t, "Push commits across all repos", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMessage_ForCategory(t *testing.T) {
|
func TestMessage_ForCategory(t *testing.T) {
|
||||||
|
|
@ -260,21 +261,21 @@ func TestDebugMode(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Without debug
|
// Without debug
|
||||||
result := svc.T("cli.success")
|
result := svc.T("cmd.dev.short")
|
||||||
assert.Equal(t, "Success", result)
|
assert.Equal(t, "Multi-repo development workflow", result)
|
||||||
|
|
||||||
// Enable debug
|
// Enable debug
|
||||||
svc.SetDebug(true)
|
svc.SetDebug(true)
|
||||||
assert.True(t, svc.Debug())
|
assert.True(t, svc.Debug())
|
||||||
|
|
||||||
// With debug - shows key prefix
|
// With debug - shows key prefix
|
||||||
result = svc.T("cli.success")
|
result = svc.T("cmd.dev.short")
|
||||||
assert.Equal(t, "[cli.success] Success", result)
|
assert.Equal(t, "[cmd.dev.short] Multi-repo development workflow", result)
|
||||||
|
|
||||||
// Disable debug
|
// Disable debug
|
||||||
svc.SetDebug(false)
|
svc.SetDebug(false)
|
||||||
result = svc.T("cli.success")
|
result = svc.T("cmd.dev.short")
|
||||||
assert.Equal(t, "Success", result)
|
assert.Equal(t, "Multi-repo development workflow", result)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("C with debug mode", func(t *testing.T) {
|
t.Run("C with debug mode", func(t *testing.T) {
|
||||||
|
|
@ -311,8 +312,8 @@ func TestDebugMode(t *testing.T) {
|
||||||
assert.True(t, Default().Debug())
|
assert.True(t, Default().Debug())
|
||||||
|
|
||||||
// Translate
|
// Translate
|
||||||
result := T("cli.success")
|
result := T("cmd.dev.short")
|
||||||
assert.Equal(t, "[cli.success] Success", result)
|
assert.Equal(t, "[cmd.dev.short] Multi-repo development workflow", result)
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
SetDebug(false)
|
SetDebug(false)
|
||||||
|
|
|
||||||
|
|
@ -270,8 +270,8 @@ func TestIntentT_Integration(t *testing.T) {
|
||||||
assert.Equal(t, "Delete config.yaml?", result)
|
assert.Equal(t, "Delete config.yaml?", result)
|
||||||
|
|
||||||
// Using T with regular key should work normally
|
// Using T with regular key should work normally
|
||||||
result = svc.T("cli.success")
|
result = svc.T("cmd.dev.short")
|
||||||
assert.Equal(t, "Success", result)
|
assert.Equal(t, "Multi-repo development workflow", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIntent_EmptyTemplates(t *testing.T) {
|
func TestIntent_EmptyTemplates(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ func TestServiceImplementsTranslator(t *testing.T) {
|
||||||
translator = svc
|
translator = svc
|
||||||
|
|
||||||
// Test interface methods
|
// Test interface methods
|
||||||
assert.Equal(t, "Success", translator.T("cli.success"))
|
assert.Equal(t, "Multi-repo development workflow", translator.T("cmd.dev.short"))
|
||||||
assert.NotEmpty(t, translator.Language())
|
assert.NotEmpty(t, translator.Language())
|
||||||
assert.NotNil(t, translator.Direction())
|
assert.NotNil(t, translator.Direction())
|
||||||
assert.NotNil(t, translator.Formality())
|
assert.NotNil(t, translator.Formality())
|
||||||
|
|
|
||||||
|
|
@ -1,120 +1,64 @@
|
||||||
{
|
{
|
||||||
"gram.verb.delete": { "base": "löschen", "past": "gelöscht", "gerund": "löschend" },
|
"gram": {
|
||||||
"gram.verb.save": { "base": "speichern", "past": "gespeichert", "gerund": "speichernd" },
|
"verb": {
|
||||||
"gram.verb.create": { "base": "erstellen", "past": "erstellt", "gerund": "erstellend" },
|
"delete": { "base": "löschen", "past": "gelöscht", "gerund": "löschend" },
|
||||||
"gram.verb.update": { "base": "aktualisieren", "past": "aktualisiert", "gerund": "aktualisierend" },
|
"save": { "base": "speichern", "past": "gespeichert", "gerund": "speichernd" },
|
||||||
"gram.verb.build": { "base": "bauen", "past": "gebaut", "gerund": "bauend" },
|
"create": { "base": "erstellen", "past": "erstellt", "gerund": "erstellend" },
|
||||||
"gram.verb.run": { "base": "laufen", "past": "gelaufen", "gerund": "laufend" },
|
"update": { "base": "aktualisieren", "past": "aktualisiert", "gerund": "aktualisierend" },
|
||||||
"gram.verb.check": { "base": "prüfen", "past": "geprüft", "gerund": "prüfend" },
|
"build": { "base": "bauen", "past": "gebaut", "gerund": "bauend" },
|
||||||
"gram.verb.install": { "base": "installieren", "past": "installiert", "gerund": "installierend" },
|
"run": { "base": "laufen", "past": "gelaufen", "gerund": "laufend" },
|
||||||
"gram.verb.push": { "base": "pushen", "past": "gepusht", "gerund": "pushend" },
|
"check": { "base": "prüfen", "past": "geprüft", "gerund": "prüfend" },
|
||||||
"gram.verb.pull": { "base": "pullen", "past": "gepullt", "gerund": "pullend" },
|
"install": { "base": "installieren", "past": "installiert", "gerund": "installierend" },
|
||||||
"gram.verb.commit": { "base": "committen", "past": "committet", "gerund": "committend" },
|
"push": { "base": "pushen", "past": "gepusht", "gerund": "pushend" },
|
||||||
|
"pull": { "base": "pullen", "past": "gepullt", "gerund": "pullend" },
|
||||||
"gram.noun.file": { "one": "Datei", "other": "Dateien", "gender": "feminine" },
|
"commit": { "base": "committen", "past": "committet", "gerund": "committend" }
|
||||||
"gram.noun.repo": { "one": "Repository", "other": "Repositories", "gender": "neuter" },
|
},
|
||||||
"gram.noun.commit": { "one": "Commit", "other": "Commits", "gender": "masculine" },
|
"noun": {
|
||||||
"gram.noun.branch": { "one": "Branch", "other": "Branches", "gender": "masculine" },
|
"file": { "one": "Datei", "other": "Dateien", "gender": "feminine" },
|
||||||
"gram.noun.change": { "one": "Änderung", "other": "Änderungen", "gender": "feminine" },
|
"repo": { "one": "Repository", "other": "Repositories", "gender": "neuter" },
|
||||||
"gram.noun.item": { "one": "Element", "other": "Elemente", "gender": "neuter" },
|
"commit": { "one": "Commit", "other": "Commits", "gender": "masculine" },
|
||||||
|
"branch": { "one": "Branch", "other": "Branches", "gender": "masculine" },
|
||||||
"gram.article.indefinite.masculine": "ein",
|
"change": { "one": "Änderung", "other": "Änderungen", "gender": "feminine" },
|
||||||
"gram.article.indefinite.feminine": "eine",
|
"item": { "one": "Element", "other": "Elemente", "gender": "neuter" }
|
||||||
"gram.article.indefinite.neuter": "ein",
|
},
|
||||||
"gram.article.definite.masculine": "der",
|
"article": {
|
||||||
"gram.article.definite.feminine": "die",
|
"indefinite": { "masculine": "ein", "feminine": "eine", "neuter": "ein" },
|
||||||
"gram.article.definite.neuter": "das",
|
"definite": { "masculine": "der", "feminine": "die", "neuter": "das" }
|
||||||
|
},
|
||||||
"cli.success": "Erfolg",
|
"punct": {
|
||||||
"cli.error": "Fehler",
|
"label": ":",
|
||||||
"cli.warning": "Warnung",
|
"progress": "..."
|
||||||
"cli.info": "Info",
|
}
|
||||||
"cli.done": "Fertig",
|
|
||||||
"cli.failed": "Fehlgeschlagen",
|
|
||||||
"cli.pass": "BESTANDEN",
|
|
||||||
"cli.fail": "FEHLGESCHLAGEN",
|
|
||||||
"cli.ok": "OK",
|
|
||||||
"cli.skip": "Übersprungen",
|
|
||||||
"cli.pending": "Ausstehend",
|
|
||||||
"cli.running": "Läuft",
|
|
||||||
"cli.completed": "Abgeschlossen",
|
|
||||||
"cli.cancelled": "Abgebrochen",
|
|
||||||
"cli.aborted": "Abgebrochen",
|
|
||||||
|
|
||||||
"cli.confirm.yes": "Ja",
|
|
||||||
"cli.confirm.no": "Nein",
|
|
||||||
"cli.confirm.proceed": "Fortfahren?",
|
|
||||||
"cli.confirm.continue": "Weiter?",
|
|
||||||
"cli.confirm.abort": "Vorgang abgebrochen",
|
|
||||||
|
|
||||||
"cli.progress.checking": "Prüfe",
|
|
||||||
"cli.progress.fetching": "Lade",
|
|
||||||
"cli.progress.loading": "Lade",
|
|
||||||
"cli.progress.processing": "Verarbeite",
|
|
||||||
"cli.progress.installing": "Installiere",
|
|
||||||
"cli.progress.building": "Baue",
|
|
||||||
"cli.progress.deploying": "Deploye",
|
|
||||||
"cli.progress.testing": "Teste",
|
|
||||||
|
|
||||||
"cli.time.just_now": "gerade eben",
|
|
||||||
"cli.time.seconds_ago": "vor {{.Count}} Sekunden",
|
|
||||||
"cli.time.minute_ago": "vor 1 Minute",
|
|
||||||
"cli.time.minutes_ago": "vor {{.Count}} Minuten",
|
|
||||||
"cli.time.hour_ago": "vor 1 Stunde",
|
|
||||||
"cli.time.hours_ago": "vor {{.Count}} Stunden",
|
|
||||||
"cli.time.day_ago": "vor 1 Tag",
|
|
||||||
"cli.time.days_ago": "vor {{.Count}} Tagen",
|
|
||||||
"cli.time.week_ago": "vor 1 Woche",
|
|
||||||
"cli.time.weeks_ago": "vor {{.Count}} Wochen",
|
|
||||||
|
|
||||||
"cli.count.items": {
|
|
||||||
"one": "{{.Count}} Element",
|
|
||||||
"other": "{{.Count}} Elemente"
|
|
||||||
},
|
},
|
||||||
"cli.count.files": {
|
"prompt": {
|
||||||
"one": "{{.Count}} Datei",
|
"yes": "j",
|
||||||
"other": "{{.Count}} Dateien"
|
"no": "n",
|
||||||
|
"continue": "Weiter?",
|
||||||
|
"proceed": "Fortfahren?",
|
||||||
|
"confirm": "Sind Sie sicher?"
|
||||||
},
|
},
|
||||||
"cli.count.repos": {
|
"time": {
|
||||||
"one": "{{.Count}} Repository",
|
"just_now": "gerade eben",
|
||||||
"other": "{{.Count}} Repositories"
|
"ago": {
|
||||||
|
"second": { "one": "vor {{.Count}} Sekunde", "other": "vor {{.Count}} Sekunden" },
|
||||||
|
"minute": { "one": "vor {{.Count}} Minute", "other": "vor {{.Count}} Minuten" },
|
||||||
|
"hour": { "one": "vor {{.Count}} Stunde", "other": "vor {{.Count}} Stunden" },
|
||||||
|
"day": { "one": "vor {{.Count}} Tag", "other": "vor {{.Count}} Tagen" },
|
||||||
|
"week": { "one": "vor {{.Count}} Woche", "other": "vor {{.Count}} Wochen" }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"cli.count.commits": {
|
"cmd": {
|
||||||
"one": "{{.Count}} Commit",
|
"dev.short": "Multi-Repository-Entwicklung",
|
||||||
"other": "{{.Count}} Commits"
|
"doctor.short": "Entwicklungsumgebung prüfen"
|
||||||
},
|
},
|
||||||
|
"error": {
|
||||||
"cmd.dev.short": "Multi-Repository-Entwicklung",
|
"gh_not_found": "'gh' CLI nicht gefunden. Installieren von https://cli.github.com/"
|
||||||
"cmd.dev.status.dirty": "geändert",
|
},
|
||||||
"cmd.dev.status.clean": "sauber",
|
"lang": {
|
||||||
"cmd.dev.status.ahead": "voraus",
|
"de": "Deutsch",
|
||||||
"cmd.dev.status.behind": "zurück",
|
"en": "Englisch",
|
||||||
"cmd.dev.status.synced": "synchronisiert",
|
"es": "Spanisch",
|
||||||
|
"fr": "Französisch",
|
||||||
"cmd.dev.push.confirm": "Alle pushen?",
|
"zh": "Chinesisch"
|
||||||
"cmd.dev.commit.committing": "Committe geänderte Repos mit Claude...",
|
}
|
||||||
|
|
||||||
"cmd.doctor.short": "Entwicklungsumgebung prüfen",
|
|
||||||
"cmd.doctor.checking": "Prüfe Entwicklungsumgebung...",
|
|
||||||
"cmd.doctor.required": "Erforderlich",
|
|
||||||
"cmd.doctor.optional": "Optional",
|
|
||||||
"cmd.doctor.ready": "Umgebung bereit",
|
|
||||||
|
|
||||||
"error.not_found": "Nicht gefunden: {{.Item}}",
|
|
||||||
"error.invalid": "Ungültig: {{.Item}}",
|
|
||||||
"error.permission": "Zugriff verweigert: {{.Item}}",
|
|
||||||
"error.timeout": "Zeitüberschreitung",
|
|
||||||
"error.gh_not_found": "'gh' CLI nicht gefunden. Installieren von https://cli.github.com/",
|
|
||||||
|
|
||||||
"label.status": "Status",
|
|
||||||
"label.branch": "Branch",
|
|
||||||
"label.commit": "Commit",
|
|
||||||
"label.coverage": "Abdeckung",
|
|
||||||
"label.total": "Gesamt",
|
|
||||||
|
|
||||||
"lang.en": "Englisch",
|
|
||||||
"lang.de": "Deutsch",
|
|
||||||
"lang.es": "Spanisch",
|
|
||||||
"lang.fr": "Französisch",
|
|
||||||
"lang.zh": "Chinesisch"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue