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)
|
||||
|
||||
// Basic translation
|
||||
result := svc.T("cli.success")
|
||||
assert.Equal(t, "Success", result)
|
||||
result := svc.T("cmd.dev.short")
|
||||
assert.Equal(t, "Multi-repo development workflow", result)
|
||||
|
||||
// Missing key returns the key
|
||||
result = svc.T("nonexistent.key")
|
||||
|
|
@ -36,11 +36,11 @@ func TestTranslateWithArgs(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// Translation with template data
|
||||
result := svc.T("error.not_found", map[string]string{"Item": "config.yaml"})
|
||||
assert.Equal(t, "Not found: config.yaml", result)
|
||||
result := svc.T("error.repo_not_found", map[string]string{"Name": "config.yaml"})
|
||||
assert.Equal(t, "Repository 'config.yaml' not found", result)
|
||||
|
||||
result = svc.T("cli.time.minutes_ago", map[string]int{"Count": 5})
|
||||
assert.Equal(t, "5 minutes ago", result)
|
||||
result = svc.T("cmd.ai.task_pr.branch_error", map[string]string{"Branch": "main"})
|
||||
assert.Equal(t, "cannot create PR from main branch; create a feature branch first", result)
|
||||
}
|
||||
|
||||
func TestSetLanguage(t *testing.T) {
|
||||
|
|
@ -71,8 +71,8 @@ func TestDefaultService(t *testing.T) {
|
|||
require.NotNil(t, svc)
|
||||
|
||||
// Global T function should work
|
||||
result := T("cli.success")
|
||||
assert.Equal(t, "Success", result)
|
||||
result := T("cmd.dev.short")
|
||||
assert.Equal(t, "Multi-repo development workflow", result)
|
||||
}
|
||||
|
||||
func TestAddMessages(t *testing.T) {
|
||||
|
|
@ -138,17 +138,18 @@ func TestDetectLanguage(t *testing.T) {
|
|||
func TestPluralization(t *testing.T) {
|
||||
svc, err := New()
|
||||
require.NoError(t, err)
|
||||
SetDefault(svc)
|
||||
|
||||
// Singular
|
||||
result := svc.T("cli.count.items", map[string]any{"Count": 1})
|
||||
// Singular - uses core.count.* magic
|
||||
result := svc.T("core.count.item", 1)
|
||||
assert.Equal(t, "1 item", result)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
|
@ -156,13 +157,13 @@ func TestNestedKeys(t *testing.T) {
|
|||
svc, err := New()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Deeply nested key
|
||||
result := svc.T("cmd.dev.work.short")
|
||||
assert.Equal(t, "Multi-repo git operations", result)
|
||||
// Nested key
|
||||
result := svc.T("cmd.dev.short")
|
||||
assert.Equal(t, "Multi-repo development workflow", result)
|
||||
|
||||
// Nested with flag
|
||||
result = svc.T("cmd.dev.work.flag.status")
|
||||
assert.Equal(t, "Show status only, don't push", result)
|
||||
// Deeper nested key (flat key with dots)
|
||||
result = svc.T("cmd.dev.push.short")
|
||||
assert.Equal(t, "Push commits across all repos", result)
|
||||
}
|
||||
|
||||
func TestMessage_ForCategory(t *testing.T) {
|
||||
|
|
@ -260,21 +261,21 @@ func TestDebugMode(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// Without debug
|
||||
result := svc.T("cli.success")
|
||||
assert.Equal(t, "Success", result)
|
||||
result := svc.T("cmd.dev.short")
|
||||
assert.Equal(t, "Multi-repo development workflow", result)
|
||||
|
||||
// Enable debug
|
||||
svc.SetDebug(true)
|
||||
assert.True(t, svc.Debug())
|
||||
|
||||
// With debug - shows key prefix
|
||||
result = svc.T("cli.success")
|
||||
assert.Equal(t, "[cli.success] Success", result)
|
||||
result = svc.T("cmd.dev.short")
|
||||
assert.Equal(t, "[cmd.dev.short] Multi-repo development workflow", result)
|
||||
|
||||
// Disable debug
|
||||
svc.SetDebug(false)
|
||||
result = svc.T("cli.success")
|
||||
assert.Equal(t, "Success", result)
|
||||
result = svc.T("cmd.dev.short")
|
||||
assert.Equal(t, "Multi-repo development workflow", result)
|
||||
})
|
||||
|
||||
t.Run("C with debug mode", func(t *testing.T) {
|
||||
|
|
@ -311,8 +312,8 @@ func TestDebugMode(t *testing.T) {
|
|||
assert.True(t, Default().Debug())
|
||||
|
||||
// Translate
|
||||
result := T("cli.success")
|
||||
assert.Equal(t, "[cli.success] Success", result)
|
||||
result := T("cmd.dev.short")
|
||||
assert.Equal(t, "[cmd.dev.short] Multi-repo development workflow", result)
|
||||
|
||||
// Cleanup
|
||||
SetDebug(false)
|
||||
|
|
|
|||
|
|
@ -270,8 +270,8 @@ func TestIntentT_Integration(t *testing.T) {
|
|||
assert.Equal(t, "Delete config.yaml?", result)
|
||||
|
||||
// Using T with regular key should work normally
|
||||
result = svc.T("cli.success")
|
||||
assert.Equal(t, "Success", result)
|
||||
result = svc.T("cmd.dev.short")
|
||||
assert.Equal(t, "Multi-repo development workflow", result)
|
||||
}
|
||||
|
||||
func TestIntent_EmptyTemplates(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ func TestServiceImplementsTranslator(t *testing.T) {
|
|||
translator = svc
|
||||
|
||||
// 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.NotNil(t, translator.Direction())
|
||||
assert.NotNil(t, translator.Formality())
|
||||
|
|
|
|||
|
|
@ -1,120 +1,64 @@
|
|||
{
|
||||
"gram.verb.delete": { "base": "löschen", "past": "gelöscht", "gerund": "löschend" },
|
||||
"gram.verb.save": { "base": "speichern", "past": "gespeichert", "gerund": "speichernd" },
|
||||
"gram.verb.create": { "base": "erstellen", "past": "erstellt", "gerund": "erstellend" },
|
||||
"gram.verb.update": { "base": "aktualisieren", "past": "aktualisiert", "gerund": "aktualisierend" },
|
||||
"gram.verb.build": { "base": "bauen", "past": "gebaut", "gerund": "bauend" },
|
||||
"gram.verb.run": { "base": "laufen", "past": "gelaufen", "gerund": "laufend" },
|
||||
"gram.verb.check": { "base": "prüfen", "past": "geprüft", "gerund": "prüfend" },
|
||||
"gram.verb.install": { "base": "installieren", "past": "installiert", "gerund": "installierend" },
|
||||
"gram.verb.push": { "base": "pushen", "past": "gepusht", "gerund": "pushend" },
|
||||
"gram.verb.pull": { "base": "pullen", "past": "gepullt", "gerund": "pullend" },
|
||||
"gram.verb.commit": { "base": "committen", "past": "committet", "gerund": "committend" },
|
||||
|
||||
"gram.noun.file": { "one": "Datei", "other": "Dateien", "gender": "feminine" },
|
||||
"gram.noun.repo": { "one": "Repository", "other": "Repositories", "gender": "neuter" },
|
||||
"gram.noun.commit": { "one": "Commit", "other": "Commits", "gender": "masculine" },
|
||||
"gram.noun.branch": { "one": "Branch", "other": "Branches", "gender": "masculine" },
|
||||
"gram.noun.change": { "one": "Änderung", "other": "Änderungen", "gender": "feminine" },
|
||||
"gram.noun.item": { "one": "Element", "other": "Elemente", "gender": "neuter" },
|
||||
|
||||
"gram.article.indefinite.masculine": "ein",
|
||||
"gram.article.indefinite.feminine": "eine",
|
||||
"gram.article.indefinite.neuter": "ein",
|
||||
"gram.article.definite.masculine": "der",
|
||||
"gram.article.definite.feminine": "die",
|
||||
"gram.article.definite.neuter": "das",
|
||||
|
||||
"cli.success": "Erfolg",
|
||||
"cli.error": "Fehler",
|
||||
"cli.warning": "Warnung",
|
||||
"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"
|
||||
"gram": {
|
||||
"verb": {
|
||||
"delete": { "base": "löschen", "past": "gelöscht", "gerund": "löschend" },
|
||||
"save": { "base": "speichern", "past": "gespeichert", "gerund": "speichernd" },
|
||||
"create": { "base": "erstellen", "past": "erstellt", "gerund": "erstellend" },
|
||||
"update": { "base": "aktualisieren", "past": "aktualisiert", "gerund": "aktualisierend" },
|
||||
"build": { "base": "bauen", "past": "gebaut", "gerund": "bauend" },
|
||||
"run": { "base": "laufen", "past": "gelaufen", "gerund": "laufend" },
|
||||
"check": { "base": "prüfen", "past": "geprüft", "gerund": "prüfend" },
|
||||
"install": { "base": "installieren", "past": "installiert", "gerund": "installierend" },
|
||||
"push": { "base": "pushen", "past": "gepusht", "gerund": "pushend" },
|
||||
"pull": { "base": "pullen", "past": "gepullt", "gerund": "pullend" },
|
||||
"commit": { "base": "committen", "past": "committet", "gerund": "committend" }
|
||||
},
|
||||
"noun": {
|
||||
"file": { "one": "Datei", "other": "Dateien", "gender": "feminine" },
|
||||
"repo": { "one": "Repository", "other": "Repositories", "gender": "neuter" },
|
||||
"commit": { "one": "Commit", "other": "Commits", "gender": "masculine" },
|
||||
"branch": { "one": "Branch", "other": "Branches", "gender": "masculine" },
|
||||
"change": { "one": "Änderung", "other": "Änderungen", "gender": "feminine" },
|
||||
"item": { "one": "Element", "other": "Elemente", "gender": "neuter" }
|
||||
},
|
||||
"article": {
|
||||
"indefinite": { "masculine": "ein", "feminine": "eine", "neuter": "ein" },
|
||||
"definite": { "masculine": "der", "feminine": "die", "neuter": "das" }
|
||||
},
|
||||
"punct": {
|
||||
"label": ":",
|
||||
"progress": "..."
|
||||
}
|
||||
},
|
||||
"cli.count.files": {
|
||||
"one": "{{.Count}} Datei",
|
||||
"other": "{{.Count}} Dateien"
|
||||
"prompt": {
|
||||
"yes": "j",
|
||||
"no": "n",
|
||||
"continue": "Weiter?",
|
||||
"proceed": "Fortfahren?",
|
||||
"confirm": "Sind Sie sicher?"
|
||||
},
|
||||
"cli.count.repos": {
|
||||
"one": "{{.Count}} Repository",
|
||||
"other": "{{.Count}} Repositories"
|
||||
"time": {
|
||||
"just_now": "gerade eben",
|
||||
"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": {
|
||||
"one": "{{.Count}} Commit",
|
||||
"other": "{{.Count}} Commits"
|
||||
"cmd": {
|
||||
"dev.short": "Multi-Repository-Entwicklung",
|
||||
"doctor.short": "Entwicklungsumgebung prüfen"
|
||||
},
|
||||
|
||||
"cmd.dev.short": "Multi-Repository-Entwicklung",
|
||||
"cmd.dev.status.dirty": "geändert",
|
||||
"cmd.dev.status.clean": "sauber",
|
||||
"cmd.dev.status.ahead": "voraus",
|
||||
"cmd.dev.status.behind": "zurück",
|
||||
"cmd.dev.status.synced": "synchronisiert",
|
||||
|
||||
"cmd.dev.push.confirm": "Alle pushen?",
|
||||
"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"
|
||||
"error": {
|
||||
"gh_not_found": "'gh' CLI nicht gefunden. Installieren von https://cli.github.com/"
|
||||
},
|
||||
"lang": {
|
||||
"de": "Deutsch",
|
||||
"en": "Englisch",
|
||||
"es": "Spanisch",
|
||||
"fr": "Französisch",
|
||||
"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