@audiofool/moondog 0.1.0 → 0.2.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +153 -68
  3. package/contracts/v1/definitions.schema.json +1 -169
  4. package/package.json +7 -23
  5. package/scripts/contract-lib.mjs +0 -7
  6. package/scripts/contract-semantics.mjs +0 -1904
  7. package/scripts/moondog.mjs +285 -74
  8. package/src/core/apple-library-source-status.mjs +0 -16
  9. package/src/core/apple-projection-domain-services.mjs +168 -36
  10. package/src/core/application-update.mjs +320 -0
  11. package/src/core/capability-catalog.mjs +278 -55
  12. package/src/core/listening-now.mjs +108 -0
  13. package/src/core/listening-profile-domain-services.mjs +153 -41
  14. package/src/core/listening-upkeep.mjs +31 -0
  15. package/src/core/moondog-application.mjs +1908 -104
  16. package/src/core/playback-preferences.mjs +97 -0
  17. package/src/core/spotify-listening-intent.mjs +121 -0
  18. package/src/core/synthetic-domain-services.mjs +24 -3
  19. package/src/demo/moondog-demo.mjs +4 -22
  20. package/src/i18n/index.mjs +285 -0
  21. package/src/i18n/messages/de.mjs +128 -0
  22. package/src/i18n/messages/en.mjs +129 -0
  23. package/src/i18n/messages/es.mjs +128 -0
  24. package/src/i18n/messages/fr.mjs +128 -0
  25. package/src/i18n/messages/it.mjs +128 -0
  26. package/src/i18n/messages/ja.mjs +128 -0
  27. package/src/i18n/messages/ko.mjs +128 -0
  28. package/src/i18n/messages/pt.mjs +128 -0
  29. package/src/i18n/messages/zh-Hant.mjs +128 -0
  30. package/src/i18n/messages/zh.mjs +128 -0
  31. package/src/i18n/preferences.mjs +34 -0
  32. package/src/i18n/screen/es.mjs +966 -0
  33. package/src/i18n/screen/ja.mjs +966 -0
  34. package/src/i18n/screen/pt.mjs +966 -0
  35. package/src/i18n/screen/zh.mjs +966 -0
  36. package/src/importers/apple-music-library/index.mjs +0 -16
  37. package/src/importers/apple-music-library/sqlite-projection.mjs +51 -9
  38. package/src/integrations/apple-music/catalog.mjs +123 -14
  39. package/src/integrations/apple-music/music-app.mjs +232 -0
  40. package/src/integrations/apple-music/storefront.mjs +28 -0
  41. package/src/integrations/apple-music/terminal-preview-player.mjs +221 -0
  42. package/src/integrations/lastfm/history.mjs +221 -0
  43. package/src/integrations/lastfm/settings.mjs +75 -0
  44. package/src/integrations/netease/ncm-cli-player.mjs +321 -0
  45. package/src/integrations/open-music-similarity/artist-radio.mjs +26 -18
  46. package/src/integrations/public-playlists.mjs +2 -1
  47. package/src/integrations/spotify/account-data-profile.mjs +14 -6
  48. package/src/integrations/spotify/authentication.mjs +149 -15
  49. package/src/integrations/spotify/catalog-resolver.mjs +8 -4
  50. package/src/integrations/spotify/connection.mjs +2 -1
  51. package/src/integrations/spotify/credential-store.mjs +4 -0
  52. package/src/integrations/spotify/extended-streaming-history.mjs +11 -11
  53. package/src/integrations/spotify/history-archive.mjs +1 -1
  54. package/src/integrations/spotify/library-snapshot.mjs +227 -0
  55. package/src/integrations/spotify/recent-activity.mjs +2 -0
  56. package/src/integrations/spotify/service.mjs +873 -69
  57. package/src/integrations/spotify/web-api-client.mjs +407 -127
  58. package/src/integrations/web/brave-web.mjs +45 -0
  59. package/src/integrations/web/codex-web.mjs +15 -82
  60. package/src/integrations/web/deepseek-web.mjs +81 -0
  61. package/src/integrations/web/web-research.mjs +119 -0
  62. package/src/integrations/web/web-result.mjs +215 -0
  63. package/src/integrations/youtube-music/takeout.mjs +3 -1
  64. package/src/memory/local-memory-store.mjs +469 -41
  65. package/src/profile/history-import.mjs +42 -6
  66. package/src/profile/listener-profile-build.mjs +378 -0
  67. package/src/profile/listener-profile-store.mjs +130 -0
  68. package/src/profile/listener-profile-verification.mjs +68 -0
  69. package/src/profile/listener-profile-worker.mjs +172 -0
  70. package/src/profile/listening-history-store.mjs +424 -8
  71. package/src/profile/listening-profile-projection.mjs +242 -76
  72. package/src/profile/local-music-data.mjs +1 -1
  73. package/src/profile/music-providers.mjs +11 -7
  74. package/src/profile/profile-exploration.mjs +84 -0
  75. package/src/profile/recorded-measures.mjs +24 -0
  76. package/src/profile/spotify-archive-taste.mjs +0 -4
  77. package/src/runtime/pi/agent-runtime.mjs +2204 -605
  78. package/src/runtime/pi/authentication.mjs +34 -1
  79. package/src/runtime/pi/configured-runtime.mjs +22 -6
  80. package/src/runtime/pi/conversation-name.mjs +53 -0
  81. package/src/runtime/pi/memory-agent-runtime.mjs +2 -2
  82. package/src/runtime/pi/model-catalog.mjs +28 -5
  83. package/src/runtime/pi/model-settings.mjs +202 -0
  84. package/src/runtime/pi/model-transport.mjs +10 -0
  85. package/src/runtime/pi/persistent-credential-store.mjs +67 -31
  86. package/src/runtime/pi/profile-build-runtime.mjs +160 -0
  87. package/src/runtime/pi/public-model-catalog.mjs +288 -0
  88. package/src/runtime/pi/runtime-settings.mjs +10 -2
  89. package/src/runtime/pi/skills/listener-profile/SKILL.md +46 -0
  90. package/src/runtime/pi/tool-trace.mjs +37 -0
  91. package/src/surfaces/cli/auth-command.mjs +12 -4
  92. package/src/surfaces/cli/brand-components.mjs +310 -132
  93. package/src/surfaces/cli/brand-theme.mjs +51 -28
  94. package/src/surfaces/cli/command-completions.mjs +58 -42
  95. package/src/surfaces/cli/format-output.mjs +199 -388
  96. package/src/surfaces/cli/history-import-view.mjs +292 -139
  97. package/src/surfaces/cli/import-guides.mjs +2 -0
  98. package/src/surfaces/cli/listener-profile-command.mjs +92 -0
  99. package/src/surfaces/cli/listening-menu.mjs +7 -4
  100. package/src/surfaces/cli/profile-command.mjs +30 -21
  101. package/src/surfaces/cli/seasons-chart.mjs +87 -0
  102. package/src/surfaces/cli/spotify-command.mjs +54 -6
  103. package/src/surfaces/cli/taste-profile-model.mjs +78 -79
  104. package/src/surfaces/cli/taste-profile-view.mjs +33 -26
  105. package/src/surfaces/cli/terminal-art.mjs +140 -35
  106. package/src/surfaces/cli/tui.mjs +1066 -357
  107. package/src/surfaces/cli/update-command.mjs +32 -0
  108. package/src/surfaces/cli/web-command.mjs +53 -14
  109. package/src/surfaces/html/tasteprint.mjs +43 -70
  110. package/contracts/v1/capability-policy.schema.json +0 -572
  111. package/contracts/v1/profile-evidence.schema.json +0 -237
  112. package/contracts/v1/tool-invocation.schema.json +0 -318
  113. package/contracts/v1/tool-receipt.schema.json +0 -1066
  114. package/scripts/demo-studio.mjs +0 -58
  115. package/src/core/apple-data-migration.mjs +0 -50
  116. package/src/demo/moondog-interactive-tasteprint-demo.mjs +0 -293
  117. package/src/demo/moondog-tasteprint-demo.mjs +0 -631
  118. package/src/integrations/spotify/index.mjs +0 -80
  119. package/src/surfaces/web/showcase.mjs +0 -272
  120. package/src/surfaces/web/studio-page.mjs +0 -4148
  121. package/src/surfaces/web/studio.mjs +0 -2288
@@ -0,0 +1,128 @@
1
+ // Deutsch. Keys match messages/en.mjs; a missing key falls back to English.
2
+ export default {
3
+ "discovery.reason.listenbrainz": ({ adjacent, seed, release }) => `Hördaten von ListenBrainz verbinden ${adjacent} mit ${seed}; aus „${release}“.`,
4
+ "discovery.reason.appleMusic": ({ queries, release, genre }) => `Apple-Music-Katalog: ${queries ? `Treffer für ${queries}` : "Kandidat"}; aus „${release}“${genre ? `, Katalog-Genre: ${genre}` : ""}.`,
5
+ "discovery.reason.external": ({ release }) => `Aus dem abgerufenen externen Katalog, auf „${release}“.`,
6
+ "discovery.order": ({ path }) => `In dieser Reihenfolge anhören: ${path}.`,
7
+ "discovery.unavailable": "Ich konnte die Dienste zur Musikentdeckung nicht erreichen, daher gibt es diesmal keine Empfehlungen. Sende die Anfrage noch einmal, um es erneut zu versuchen.",
8
+
9
+ "citations.heading": "Öffentliche Musikquellen (getrennt von deinen persönlichen Hördaten):",
10
+ "citations.item": ({ label, url, storefront, date }) => `- [${label}](<${url}>) - ${storefront}-Store, abgerufen am ${date}.`,
11
+ "citations.scope": "Geltungsbereich: Diese öffentlichen Katalogseiten belegen weder persönliche Vorlieben noch einen vollständigen Hörverlauf noch die Verfügbarkeit auf anderen Plattformen.",
12
+ "web.sources.heading": ({ providers }) => `Öffentliche Webquellen (${providers})`,
13
+ "web.sources.headingUnnamed": "Öffentliche Webquellen",
14
+ "web.sources.published": ({ date }) => `Datum der Quelle: ${date}`,
15
+ "web.sources.retrieved": ({ date }) => `abgerufen: ${date}`,
16
+
17
+ "external.neighbors": ({ seed }) => seed ? `ListenBrainz-Hörer-Nachbarn von ${seed}` : "ListenBrainz-Hörer-Nachbarn",
18
+ "external.listenbrainz": ({ neighbor, date }) => `${neighbor} (Wikidata-Identität, CC0, abgerufen am ${date}; das ist keine klangliche Ähnlichkeit)`,
19
+ "external.appleMusic": ({ date }) => `Titeltreffer bei Apple Music US (abgerufen am ${date})`,
20
+ "external.limit": "Ausgeschlossen ist nur, was in deiner importierten Bibliothek mit gleichem Titel und Künstler vorkommt; du hast diese Songs also nicht unbedingt noch nie gehört. Es sind Vorschläge zum Reinhören.",
21
+ "external.from": ({ parts, limit }) => `Aus ${parts.join(" und ")}. ${limit}`,
22
+
23
+ "failure.deviceList": ({ noMatch, names, code }) => `Der Gerätename ${noMatch ? "passte zu keinem Gerät" : "passte zu mehreren Geräten"}. Spotify zeigt gerade: ${names}. Sag „spiel es auf“ und dann den angezeigten Gerätenamen; der ausgewählte Song bleibt erhalten. Es wurde keine Wiedergabe-Anfrage gesendet. (${code})`,
24
+ "failure.networkRead": ({ notSent, code }) => `Ein Netzwerkfehler hat das Lesen des Geräte- und Wiedergabestatus von Spotify verhindert; die Vorbereitung wurde abgebrochen.${notSent ? " Die Wiedergabe-Anfrage wurde nicht gesendet." : ""} Prüfe die Verbindung zu Spotify und versuche es erneut. (${code})`,
25
+ "failure.readiness.spotify_device_selection_required": "Der ausgewählte Song bleibt erhalten, aber das Zielgerät steht noch nicht fest. Nenne das gewünschte Gerät; das vorherige Gerät wird nicht stattdessen verwendet.",
26
+ "failure.readiness.spotify_device_not_found": "Spotify sieht das Zielgerät nicht. Öffne Spotify darauf und bitte dann erneut um die Wiedergabe; es wurde kein anderes Gerät stattdessen verwendet.",
27
+ "failure.readiness.spotify_device_ambiguous": "Möglicherweise sind mehrere Spotify-Geräte verfügbar. Nenne den gewünschten Gerätenamen.",
28
+ "failure.readiness.spotify_device_restricted": "Das Zielgerät bestätigt keine Steuerung über die Spotify Web API. Wähle ein anderes verfügbares Gerät.",
29
+ "failure.readiness.spotify_device_changed": "Ein anderes Spotify-Gerät wurde aktiv. Wähle das gewünschte Gerät ausdrücklich; die Wiedergabe wurde nicht automatisch verschoben.",
30
+ "failure.readiness.spotify_device_state_unconfirmed": "Der Geräte- und Wiedergabestatus von Spotify ist unvollständig oder widersprüchlich. Prüfe das aktuelle Gerät in Spotify, bevor du es erneut versuchst.",
31
+ "failure.readiness.spotify_device_not_ready": "Das Zielgerät war innerhalb der Wartezeit nicht bereit. Öffne Spotify darauf und bitte dann erneut um denselben Song.",
32
+ "failure.readiness.spotify_playback_restricted": "Spotify erlaubt gerade nicht, die Wiedergabe fortzusetzen. Prüfe das Zielgerät.",
33
+ "failure.songNotSent": " Die Wiedergabe-Anfrage wurde nicht gesendet.",
34
+ "failure.preparationStopped": ({ evidence }) => `Die Prüfung des Wiedergabegeräts wurde nicht abgeschlossen (${evidence}); es wurde keine Wiedergabe-Anfrage gesendet. Prüfe den Gerätestatus in Spotify, bevor du es erneut versuchst.`,
35
+ "failure.queueRequestRequired": "Es wurden keine Songs zur Warteschlange hinzugefügt: Ich konnte diese Antwort keiner Anfrage für die Warteschlange zuordnen. Um fortzufahren, braucht es eine ausdrückliche Bitte, Musik zur Warteschlange hinzuzufügen.",
36
+ "failure.notSent": ({ code }) => `An Spotify wurde nichts gesendet: Ich konnte das keinem der gefundenen Songs zuordnen (${code}). Frag noch einmal mit Songtitel oder Künstler, dann suche ich erneut.`,
37
+ "failure.outcome.lookup": "Die Spotify-Suche wurde nicht abgeschlossen",
38
+ "failure.outcome.unknown": "Das Ergebnis der Spotify-Aktion ist unklar",
39
+ "failure.outcome.unconfirmed": "Die Spotify-Wiedergabeaktion wurde nicht bestätigt",
40
+ "failure.reason": ({ reason }) => `Spotify meldet: ${reason}.`,
41
+ "failure.reasonUnknown": "Die genaue Ursache ist nicht bestätigt.",
42
+ "failure.lookup": ({ outcome, evidence, reason }) => `${outcome} (${evidence}). ${reason} Es wurde keine Wiedergabe und nichts für die Warteschlange gesendet.`,
43
+ "failure.general": ({ targets, outcome, evidence, reason, playbackNotSent, recovered }) =>
44
+ `${targets ? `Ziel: ${targets}.\n` : ""}${outcome} (${evidence}). ${reason}${playbackNotSent ? " Die Wiedergabe-Anfrage wurde nicht gesendet." : ""} ${recovered ? "Eine begrenzte Gerätewiederherstellung ist beendet; es gab keinen weiteren Versuch." : "Die Aktion wurde nicht wiederholt."} Lies den Wiedergabestatus und die verfügbaren Geräte, um weiter einzugrenzen.`,
45
+
46
+ "queue.unverified": ({ one }) => `Spotify hat ${one ? "ihn" : "sie"} angenommen, aber ich konnte deine Warteschlange nicht lesen, um das zu prüfen.`,
47
+ "queue.confirmedPlaying": ({ one }) => one ? "Wiedergabe auf Spotify bestätigt." : "Auf Spotify bestätigt: Der erste läuft, die übrigen stehen in deiner Warteschlange.",
48
+ "queue.confirmed": ({ count }) => count === 1 ? "In deiner Spotify-Warteschlange bestätigt." : `In deiner Spotify-Warteschlange bestätigt: alle ${count}.`,
49
+ "queue.partlyVisible": ({ accepted, confirmed, truncated }) => `Spotify hat ${accepted} angenommen; ${confirmed} sind gerade in deiner Warteschlange zu sehen${truncated ? " (Spotify zeigt bei langen Warteschlangen nur einen Teil)" : ""}.`,
50
+ "queue.noPlayback": "Auf Spotify läuft gerade kein Song, der als Ausgangspunkt dienen kann; es wurde nichts hinzugefügt.",
51
+ "queue.noCandidates": "Nach dem Filtern blieben keine passenden Kandidaten übrig; es wurde nichts hinzugefügt.",
52
+ "queue.requested": ({ requested, accepted, shortfall }) => `${requested} angefragt; Spotify hat ${accepted} angenommen${shortfall ? `, ${shortfall} fehlen` : ""}.`,
53
+ "queue.similarSeed": ({ artist }) => `Künstler, die laut Hörerdaten ${artist} ähneln:`,
54
+ "queue.cancelled": "Nach dem Abbruch werden keine weiteren Songs hinzugefügt; angenommene Titel bleiben in der Warteschlange.",
55
+ "queue.stopped": ({ count, title, artist }) => `${count} zur Spotify-Warteschlange hinzugefügt, dann bei „${title}“ - ${artist} angehalten.`,
56
+ "queue.queued": ({ count }) => `${count} zur Spotify-Warteschlange hinzugefügt:`,
57
+ "queue.queuedSome": ({ count, unmatched }) => `${count} von ${count + unmatched} zur Spotify-Warteschlange hinzugefügt:`,
58
+ "queue.outcomeUnknown": "Spotify hat nicht bestätigt, ob der angehaltene Titel hinzugefügt wurde. Prüfe die Warteschlange, bevor du es erneut versuchst; dieser Schreibvorgang wurde nicht wiederholt.",
59
+ "queue.started": ({ title, device, more }) => `Es lief nichts, also habe ich „${title}“${device ? ` auf ${device}` : ""} gestartet${more ? " und den Rest dahinter eingereiht" : ""}.`,
60
+ "queue.unmatchedHeading": "Auf Spotify nicht gefunden:",
61
+ "queue.skippedDuplicates": ({ count }) => `${count} doppelte Titel übersprungen.`,
62
+ "queue.skippedUncertain": ({ count }) => `${count} Titel mit unklarem früherem Ergebnis übersprungen, um Doppelungen zu vermeiden; prüfe die Spotify-Warteschlange.`,
63
+ "queue.skippedAvoided": ({ count }) => `${count} Titel übersprungen, die du vermeiden wolltest.`,
64
+ "queue.skippedKnown": ({ count }) => `${count} Titel aus deinem Hörverlauf übersprungen, wie gewünscht.`,
65
+ "queue.observationTruncated": "Doppelungen wurden nur im begrenzten Ausschnitt der Warteschlange geprüft, den Spotify liefert; nicht sichtbare Einträge können noch doppelt sein.",
66
+ "queue.notAddedHeading": "Nicht hinzugefügt:",
67
+ "queue.earlierReceipt": "Das ist das Ergebnis der früheren Warteschlangen-Aktion; diese Klarstellung hat die Songs nicht erneut hinzugefügt.",
68
+
69
+ "playlist.partialUnknown": ({ name }) => `Spotify hat die private Playlist „${name}“ erstellt, aber das Hinzufügen der Titel wurde nicht bestätigt. Prüfe sie, bevor du entscheidest, ob du es erneut versuchst.`,
70
+ "playlist.partialEmpty": ({ name }) => `Spotify hat die private Playlist „${name}“ erstellt, aber keine Titel hinzugefügt. Die leere Playlist existiert jetzt; prüfe sie, bevor du entscheidest, ob du es erneut versuchst.`,
71
+ "playlist.saved": ({ name, count }) => `Als private Spotify-Playlist „${name}“ gespeichert (${count} Titel).`,
72
+
73
+ "action.playback.resume": "Wiedergabe-Anfrage",
74
+ "action.playback.pause": "Pause-Anfrage",
75
+ "action.playback.next": "Anfrage für den nächsten Titel",
76
+ "action.playback.previous": "Anfrage für den vorherigen Titel",
77
+ "action.playback.volume.set": "Lautstärkeänderung",
78
+ "action.playback.seek": "Änderung der Wiedergabeposition",
79
+ "action.playback.shuffle.set": "Zufallswiedergabe-Einstellung",
80
+ "action.playback.repeat.set": "Wiederholungs-Einstellung",
81
+ "action.playback.transfer": "Gerätewechsel",
82
+ "action.playback.queue.add": "Hinzufügen zur Warteschlange",
83
+ "action.library.remove": "Entfernen aus der Bibliothek",
84
+ "action.library.save": "Speichern in der Bibliothek",
85
+ "action.playlist.write": "Erstellen der Playlist",
86
+ "action.playlist.edit": "Änderung der Playlist",
87
+ "action.playlist.rename": "Umbenennen der Playlist",
88
+ "action.playlist.unfollow": "Entfernen der Playlist aus der Bibliothek",
89
+ "action.playlist.remove_track": "Entfernen eines Titels aus der Playlist",
90
+ "action.other": "gewünschte Änderung",
91
+ "write.unknown": ({ action, target }) => `Spotify hat die Aktion „${action}“${target ? ` für „${target}“` : ""} nicht bestätigt. Prüfe den Status, bevor du entscheidest, ob du es erneut versuchst.`,
92
+ "write.unfollowed": ({ name }) => `„${name}“ wurde aus deiner Spotify-Bibliothek entfernt (nicht mehr gefolgt). Für andere Hörer kann die Playlist weiter bestehen; sie wurde nicht für alle gelöscht.`,
93
+ "write.renamed": ({ name }) => `Spotify hat das Umbenennen der Playlist in „${name}“ angenommen.`,
94
+ "write.trackRemoved": ({ name }) => `Spotify hat das Entfernen eines Titels aus „${name}“ angenommen.`,
95
+ "write.accepted": ({ action, count, targets }) => `Spotify hat die Aktion „${action}“ angenommen${count === null ? "" : ` (${count} Titel)`}${targets ? `: ${targets}` : ""}.`,
96
+
97
+ "plan.heading.private_library": ({ count }) => `Auswahl mit ${count} Titeln aus deiner Bibliothek:`,
98
+ "plan.heading.private_history": ({ count }) => `${count} Titel zum Wiederhören aus deinem Verlauf:`,
99
+ "plan.heading.external_catalog": ({ count }) => `${count} Songs:`,
100
+ "plan.heading.mixed": ({ count }) => `${count} Songs:`,
101
+ "plan.heading.timeCapsule": ({ count }) => `Zeitreise in ${count} Stationen durch deine Hörjahre:`,
102
+ "plan.heading.historicalReturn": ({ count }) => `${count} Titel, die nach langer Pause zurückkamen:`,
103
+ "plan.heading.backToBack": ({ count }) => `${count} Titel, die du direkt hintereinander gehört hast:`,
104
+ "plan.externalTrack": ({ line, release }) => `${line}, aus „${release}“`,
105
+ "plan.rationale": ({ text }) => ` Begründung der Auswahl: ${text}`,
106
+ "plan.ordering": ({ text }) => `Begründung der Reihenfolge: ${text}`,
107
+ "plan.notSaved": "Noch ist nichts gespeichert oder zur Warteschlange hinzugefügt.",
108
+ "plan.previewing": "Die 30-Sekunden-Vorschauen laufen in deinem Player; die vollständigen Songs gibt es bei Apple Music.",
109
+ "plan.pending": "Grenze: Diese Auswahl wartet nur in der laufenden Sitzung auf Bestätigung, wurde nicht in Spotify geschrieben und hat keine Auswirkungen nach außen.",
110
+ "plan.backToBackBoundary": "Grenze der Deutung: Direkt aufeinanderfolgende Wiedergaben belegen weder den Wiederholungsmodus noch bewusstes Wiederhören noch Gefallen.",
111
+ "plan.validationScope": "Umfang der Prüfung: Der lokale Planer prüft Titelidentität, Zugehörigkeit zu den Kandidaten, Anzahl und Reihenfolge; Begründungen und Passung zur Situation bleiben Einschätzungen des Modells auf Grundlage der verfügbaren Metadaten.",
112
+ "plan.unvalidated": "Ich konnte diese Auswahl nicht prüfen, deshalb gebe ich keine Titelliste aus, die nicht mit vertrauenswürdigen Kandidaten abgeglichen wurde. Grenze die Suche ein oder erweitere sie und versuche es erneut.",
113
+
114
+ "edit.previewHeading": ({ name, before, after }) => `Vorschau der Änderung an der privaten Spotify-Playlist „${name}“ (${before} -> ${after} Titel):`,
115
+ "edit.status.added": "neu",
116
+ "edit.status.moved": "verschoben",
117
+ "edit.status.retained": "bleibt",
118
+ "edit.removeHeading": "Entfernen:",
119
+ "edit.summary": ({ added, removed, moved, retained }) => `Zusammenfassung: ${added} hinzugefügt, ${removed} entfernt, ${moved} verschoben, ${retained} unverändert an ihrem Platz.`,
120
+ "edit.boundary": "Grenze: Spotify wurde noch nicht geändert. Bestätige in deiner nächsten Nachricht ausdrücklich, damit Moondog genau diese gespeicherte Version schreibt.",
121
+ "edit.updated": ({ name, before, after }) => `Die private Spotify-Playlist „${name}“ wurde genau nach der bestätigten Vorschau aktualisiert${before === null ? ` (jetzt ${after} Titel)` : ` (${before} -> ${after} Titel)`}.`,
122
+
123
+ "selection.note": ({ playlist, track }) => `\n\nFür weitere Änderungen ausgewählt: Playlist ${playlist}; Song ${track}.`,
124
+ "memory.spotifyExcluded": "\n\nSpotify-Ergebnisse kommen nicht ins allgemeine Gedächtnis; nur deine eigenen, überprüften Worte konnten gespeichert werden, alles andere wurde nicht gespeichert.",
125
+ "memory.notSaved": "\n\nDie Antwort ist fertig, aber die Erinnerung wurde nicht gespeichert.",
126
+ "turn.cancelled": "Weitere Schritte abgebrochen.",
127
+ "turn.interrupted": "Die Antwort wurde unterbrochen. Die erfassten Spotify-Ergebnisse stehen unten; keine Aktion wurde wiederholt.",
128
+ };
@@ -0,0 +1,129 @@
1
+ // Host-written conversation text. English is the source; keys are stable IDs.
2
+ // Every value is a string or a function of named parameters.
3
+ export default {
4
+ "discovery.reason.listenbrainz": ({ adjacent, seed, release }) => `ListenBrainz listening data connects ${adjacent} with ${seed}; from "${release}".`,
5
+ "discovery.reason.appleMusic": ({ queries, release, genre }) => `Apple Music catalog ${queries ? `match for ${queries}` : "candidate"}; from "${release}"${genre ? `, catalog genre: ${genre}` : ""}.`,
6
+ "discovery.reason.external": ({ release }) => `From the retrieved external catalog, on "${release}".`,
7
+ "discovery.order": ({ path }) => `Explore in this order: ${path}.`,
8
+ "discovery.unavailable": "I couldn't reach the music discovery services, so this request produced no recommendations. Please resend this request to try again.",
9
+
10
+ "citations.heading": "Public music sources (separate from personal listening evidence):",
11
+ "citations.item": ({ label, url, storefront, date }) => `- [${label}](<${url}>) - ${storefront} storefront, retrieved ${date}.`,
12
+ "citations.scope": "Scope: these public catalog pages do not establish personal preference, complete listening history, or cross-platform availability.",
13
+ "web.sources.heading": ({ providers }) => `Public web sources (${providers})`,
14
+ "web.sources.headingUnnamed": "Public web sources",
15
+ "web.sources.published": ({ date }) => `source date: ${date}`,
16
+ "web.sources.retrieved": ({ date }) => `retrieved: ${date}`,
17
+
18
+ "external.neighbors": ({ seed }) => seed ? `ListenBrainz listener-neighbors of ${seed}` : "ListenBrainz listener-neighbors",
19
+ "external.listenbrainz": ({ neighbor, date }) => `${neighbor} (Wikidata identity, CC0, retrieved ${date}; this is not audio similarity)`,
20
+ "external.appleMusic": ({ date }) => `Apple Music US title matches (retrieved ${date})`,
21
+ "external.limit": "A title-and-artist match in your imported library is the only thing excluded, so this does not mean you have never heard them. These are suggestions for listening.",
22
+ "external.from": ({ parts, limit }) => `From ${parts.join(", and ")}. ${limit}`,
23
+
24
+ "failure.deviceList": ({ noMatch, names, code }) => `The device name ${noMatch ? "did not match" : "matched several devices"}. Spotify currently lists: ${names}. Say “play it on” followed by the displayed device name; the selected song is retained. No song play request was sent. (${code})`,
25
+ "failure.networkRead": ({ notSent, code }) => `A network error prevented reading Spotify device/playback state; preparation stopped.${notSent ? " The song play request was not sent." : ""} Check the connection to Spotify and retry. (${code})`,
26
+ "failure.readiness.spotify_device_selection_required": "The selected song is retained, but the target device is not established. Name the intended device; the previous device will not be substituted.",
27
+ "failure.readiness.spotify_device_not_found": "Spotify cannot see the target device. Open Spotify on it, then request playback again; no other device was substituted.",
28
+ "failure.readiness.spotify_device_ambiguous": "Several Spotify devices may be available. Specify the intended device name.",
29
+ "failure.readiness.spotify_device_restricted": "The target device does not confirm Spotify Web API control. Choose another available device.",
30
+ "failure.readiness.spotify_device_changed": "Another Spotify device became active. Select the intended device explicitly; playback was not moved automatically.",
31
+ "failure.readiness.spotify_device_state_unconfirmed": "Spotify device/playback state is incomplete or inconsistent. Check the current device in Spotify before trying again.",
32
+ "failure.readiness.spotify_device_not_ready": "The target device did not become ready within the bounded wait. Open Spotify on it, then request the same song again.",
33
+ "failure.readiness.spotify_playback_restricted": "Spotify currently disallows resuming playback. Check the target device.",
34
+ "failure.songNotSent": " The song play request was not sent.",
35
+ "failure.preparationStopped": ({ evidence }) => `Playback device checks did not complete (${evidence}); no song play request was sent. Check Spotify device state before trying again.`,
36
+ "failure.queueRequestRequired": "No songs were queued: I could not connect this reply to a queue request. An explicit request to add music to the playback queue is needed to continue.",
37
+ "failure.notSent": ({ code }) => `Nothing was sent to Spotify: I couldn't match that to the songs I found (${code}). Ask again with the song or artist name, and I'll search again.`,
38
+ "failure.outcome.lookup": "The Spotify lookup did not complete",
39
+ "failure.outcome.unknown": "The Spotify action outcome is uncertain",
40
+ "failure.outcome.unconfirmed": "The Spotify playback action was not confirmed",
41
+ "failure.reason": ({ reason }) => `Spotify reported: ${reason}.`,
42
+ "failure.reasonUnknown": "The specific cause is unconfirmed.",
43
+ "failure.lookup": ({ outcome, evidence, reason }) => `${outcome} (${evidence}). ${reason} No play or queue action was sent.`,
44
+ "failure.general": ({ targets, outcome, evidence, reason, playbackNotSent, recovered }) =>
45
+ `${targets ? `Target: ${targets}.\n` : ""}${outcome} (${evidence}). ${reason}${playbackNotSent ? " The song play request was not sent." : ""} ${recovered ? "One bounded device recovery ended; no further retry was made." : "The action was not retried."} Read playback state and available devices to diagnose further.`,
46
+
47
+ "queue.unverified": ({ one }) => `Spotify accepted ${one ? "it" : "them"}, but I couldn't read your queue to double-check.`,
48
+ "queue.confirmedPlaying": ({ one }) => one ? "Confirmed playing on Spotify." : "Confirmed on Spotify: the first is playing and the rest are in your queue.",
49
+ "queue.confirmed": ({ count }) => count === 1 ? "Confirmed in your Spotify queue." : `Confirmed in your Spotify queue: all ${count}.`,
50
+ "queue.partlyVisible": ({ accepted, confirmed, truncated }) => `Spotify accepted ${accepted}; ${confirmed} show in your queue right now${truncated ? " (Spotify only shows part of a long queue)" : ""}.`,
51
+ "queue.noPlayback": "Spotify has no current track to use as a seed; nothing was queued.",
52
+ "queue.noCandidates": "No eligible candidates remained after filtering; nothing was queued.",
53
+ "queue.requested": ({ requested, accepted, shortfall }) => `Requested ${requested}; Spotify accepted ${accepted}${shortfall ? `, ${shortfall} short` : ""}.`,
54
+ "queue.similarSeed": ({ artist }) => `Listener-derived artist similarity from ${artist}:`,
55
+ "queue.cancelled": "Stopped queueing after cancellation; accepted tracks remain in the queue.",
56
+ "queue.stopped": ({ count, title, artist }) => `Queued ${count} on Spotify, then stopped at "${title}" - ${artist}.`,
57
+ "queue.queued": ({ count }) => `Queued ${count} on Spotify:`,
58
+ "queue.queuedSome": ({ count, unmatched }) => `Queued ${count} of ${count + unmatched} on Spotify:`,
59
+ "queue.outcomeUnknown": "Spotify did not confirm whether the stopped track was added. Check the queue before trying again; this write was not replayed.",
60
+ "queue.started": ({ title, device, more }) => `Nothing was playing, so I started "${title}"${device ? ` on ${device}` : ""}${more ? " and lined up the rest" : ""}.`,
61
+ "queue.unmatchedHeading": "Could not match on Spotify:",
62
+ "queue.skippedDuplicates": ({ count }) => `Skipped ${count} duplicate tracks.`,
63
+ "queue.skippedUncertain": ({ count }) => `Skipped ${count} tracks with earlier uncertain queue outcomes to avoid replay; check the Spotify queue.`,
64
+ "queue.skippedAvoided": ({ count }) => `Skipped ${count} avoided tracks.`,
65
+ "queue.skippedKnown": ({ count }) => `Skipped ${count} tracks matched in retained history, as requested.`,
66
+ "queue.observationTruncated": "Deduplication covers Spotify's bounded queue snapshot; unseen entries may still duplicate a selection.",
67
+ "queue.notAddedHeading": "Not added:",
68
+ "queue.earlierReceipt": "This is the earlier queue receipt; this clarification did not add the songs again.",
69
+
70
+ "playlist.partialUnknown": ({ name }) => `Spotify created the private playlist "${name}", but adding its tracks was not confirmed. Inspect it before deciding whether to retry.`,
71
+ "playlist.partialEmpty": ({ name }) => `Spotify created the private playlist "${name}", but did not add its tracks. The empty playlist now exists; inspect it before deciding whether to retry.`,
72
+ "playlist.saved": ({ name, count }) => `Saved as the private Spotify playlist "${name}" (${count} tracks).`,
73
+
74
+ "action.playback.resume": "playback request",
75
+ "action.playback.pause": "pause request",
76
+ "action.playback.next": "next-track request",
77
+ "action.playback.previous": "previous-track request",
78
+ "action.playback.volume.set": "volume change",
79
+ "action.playback.seek": "playback position change",
80
+ "action.playback.shuffle.set": "shuffle setting",
81
+ "action.playback.repeat.set": "repeat setting",
82
+ "action.playback.transfer": "device transfer",
83
+ "action.playback.queue.add": "queue addition",
84
+ "action.library.remove": "library removal",
85
+ "action.library.save": "library save",
86
+ "action.playlist.write": "playlist creation",
87
+ "action.playlist.edit": "playlist change",
88
+ "action.playlist.rename": "playlist rename",
89
+ "action.playlist.unfollow": "playlist removal from library",
90
+ "action.playlist.remove_track": "playlist track removal",
91
+ "action.other": "requested change",
92
+ "write.unknown": ({ action, target }) => `Spotify's ${action}${target ? ` for "${target}"` : ""} was not confirmed. Check its state before deciding whether to retry.`,
93
+ "write.unfollowed": ({ name }) => `Removed "${name}" from your Spotify library (unfollowed). The playlist may still exist for other listeners; it was not globally deleted.`,
94
+ "write.renamed": ({ name }) => `Spotify accepted the playlist rename to "${name}".`,
95
+ "write.trackRemoved": ({ name }) => `Spotify accepted removal of one track from "${name}".`,
96
+ "write.accepted": ({ action, count, targets }) => `Spotify accepted the ${action}${count === null ? "" : ` (${count} tracks)`}${targets ? `: ${targets}` : ""}.`,
97
+
98
+ "plan.heading.private_library": ({ count }) => `${count}-track plan from your library:`,
99
+ "plan.heading.private_history": ({ count }) => `${count}-track listen-again plan from your private history:`,
100
+ "plan.heading.external_catalog": ({ count }) => `${count} songs:`,
101
+ "plan.heading.mixed": ({ count }) => `${count} songs:`,
102
+ "plan.heading.timeCapsule": ({ count }) => `${count}-stop Listening Time Machine across your years:`,
103
+ "plan.heading.historicalReturn": ({ count }) => `${count}-track return path from your private history:`,
104
+ "plan.heading.backToBack": ({ count }) => `${count}-track played-back-to-back path from your private history:`,
105
+ "plan.externalTrack": ({ line, release }) => `${line}, from "${release}"`,
106
+ "plan.rationale": ({ text }) => ` Curatorial rationale: ${text}`,
107
+ "plan.ordering": ({ text }) => `Ordering rationale: ${text}`,
108
+ "plan.notSaved": "Nothing has been saved or queued yet.",
109
+ "plan.previewing": "The 30-second previews are playing in your player; the full songs are on Apple Music.",
110
+ "plan.pending": "Boundary: this plan is pending only in the current process, has not been written to Spotify, and has no external effects.",
111
+ "plan.backToBackBoundary": "Interpretation boundary: adjacent retained playback events do not prove repeat mode, intentional replay, or liking.",
112
+ "plan.validationScope": "Validation scope: the local planner validates track identity, candidate-set membership, count, and output order; curatorial reasons and situational fit remain model judgments based on available metadata.",
113
+ "plan.unvalidated": "I could not validate this playlist plan, so I will not return a track list that was not checked against trusted candidates. Please refine or broaden the search and try again.",
114
+
115
+ "edit.previewHeading": ({ name, before, after }) => `Spotify private playlist "${name}" edit preview (${before} -> ${after} tracks):`,
116
+ "edit.status.added": "add",
117
+ "edit.status.moved": "move",
118
+ "edit.status.retained": "keep",
119
+ "edit.removeHeading": "Remove:",
120
+ "edit.summary": ({ added, removed, moved, retained }) => `Change summary: ${added} added, ${removed} removed, ${moved} moved, ${retained} retained in place.`,
121
+ "edit.boundary": "Boundary: Spotify has not changed. Explicitly confirm in your next message before Moondog writes this exact host-retained version.",
122
+ "edit.updated": ({ name, before, after }) => `Updated the private Spotify playlist "${name}" from the exact confirmed preview${before === null ? ` (${after} tracks now)` : ` (${before} -> ${after} tracks)`}.`,
123
+
124
+ "selection.note": ({ playlist, track }) => `\n\nSelected for follow-up edits: playlist ${playlist}; song ${track}.`,
125
+ "memory.spotifyExcluded": "\n\nSpotify results are excluded from generic memory; only verified user quotes were eligible, and other claims were not saved.",
126
+ "memory.notSaved": "\n\nThe response completed, but the memory was not saved.",
127
+ "turn.cancelled": "Cancelled further work.",
128
+ "turn.interrupted": "The response was interrupted. The recorded Spotify outcomes are below; no action was replayed.",
129
+ };
@@ -0,0 +1,128 @@
1
+ // Español neutro, tuteo. Keys match messages/en.mjs; a missing key falls back to English.
2
+ export default {
3
+ "discovery.reason.listenbrainz": ({ adjacent, seed, release }) => `Los datos de escucha de ListenBrainz relacionan a ${adjacent} con ${seed}; del álbum "${release}".`,
4
+ "discovery.reason.appleMusic": ({ queries, release, genre }) => `${queries ? `Coincidencia del catálogo de Apple Music para ${queries}` : "Candidato del catálogo de Apple Music"}; del álbum "${release}"${genre ? `, género en el catálogo: ${genre}` : ""}.`,
5
+ "discovery.reason.external": ({ release }) => `Del catálogo externo consultado, en "${release}".`,
6
+ "discovery.order": ({ path }) => `Escúchalas en este orden: ${path}.`,
7
+ "discovery.unavailable": "No pude conectar con los servicios de descubrimiento musical, así que esta petición no dio recomendaciones. Vuelve a enviarla para intentarlo de nuevo.",
8
+
9
+ "citations.heading": "Fuentes musicales públicas (aparte de tu historial de escucha personal):",
10
+ "citations.item": ({ label, url, storefront, date }) => `- [${label}](<${url}>) - tienda ${storefront}, consultada el ${date}.`,
11
+ "citations.scope": "Alcance: estas páginas públicas del catálogo no demuestran tus gustos personales, tu historial completo ni la disponibilidad en otras plataformas.",
12
+ "web.sources.heading": ({ providers }) => `Fuentes web públicas (${providers})`,
13
+ "web.sources.headingUnnamed": "Fuentes web públicas",
14
+ "web.sources.published": ({ date }) => `fecha de la fuente: ${date}`,
15
+ "web.sources.retrieved": ({ date }) => `consultado: ${date}`,
16
+
17
+ "external.neighbors": ({ seed }) => seed ? `oyentes afines a ${seed} en ListenBrainz` : "oyentes afines en ListenBrainz",
18
+ "external.listenbrainz": ({ neighbor, date }) => `${neighbor} (identidad de Wikidata, CC0, consultado el ${date}; esto no es similitud de sonido)`,
19
+ "external.appleMusic": ({ date }) => `coincidencias de título en Apple Music US (consultado el ${date})`,
20
+ "external.limit": "Solo se excluye lo que coincide en título y artista con tu biblioteca importada, así que no significa que nunca las hayas escuchado. Son sugerencias para escuchar.",
21
+ "external.from": ({ parts, limit }) => `A partir de ${parts.join(" y ")}. ${limit}`,
22
+
23
+ "failure.deviceList": ({ noMatch, names, code }) => `El nombre del dispositivo ${noMatch ? "no coincide con ninguno" : "coincide con varios dispositivos"}. Spotify muestra ahora: ${names}. Di "ponla en" seguido del nombre que aparece; la canción elegida se conserva. No se envió ninguna petición de reproducción. (${code})`,
24
+ "failure.networkRead": ({ notSent, code }) => `Un error de red impidió leer el estado de los dispositivos o de la reproducción de Spotify; la preparación se detuvo.${notSent ? " No se envió la petición de reproducción." : ""} Revisa la conexión con Spotify y vuelve a intentarlo. (${code})`,
25
+ "failure.readiness.spotify_device_selection_required": "La canción elegida se conserva, pero falta decidir el dispositivo. Di cuál quieres usar; no se usará el anterior en su lugar.",
26
+ "failure.readiness.spotify_device_not_found": "Spotify no ve el dispositivo elegido. Abre Spotify en él y vuelve a pedir la canción; no se usó otro dispositivo en su lugar.",
27
+ "failure.readiness.spotify_device_ambiguous": "Puede haber varios dispositivos de Spotify disponibles. Di el nombre del que quieres usar.",
28
+ "failure.readiness.spotify_device_restricted": "El dispositivo elegido no confirma que se pueda controlar desde la API web de Spotify. Elige otro dispositivo disponible.",
29
+ "failure.readiness.spotify_device_changed": "Se activó otro dispositivo de Spotify. Elige el dispositivo que quieres; la reproducción no se movió automáticamente.",
30
+ "failure.readiness.spotify_device_state_unconfirmed": "El estado de los dispositivos o de la reproducción de Spotify está incompleto o no cuadra. Revisa el dispositivo actual en Spotify antes de volver a intentarlo.",
31
+ "failure.readiness.spotify_device_not_ready": "El dispositivo no estuvo listo dentro del tiempo de espera. Abre Spotify en él y vuelve a pedir la misma canción.",
32
+ "failure.readiness.spotify_playback_restricted": "Ahora mismo Spotify no permite reanudar la reproducción. Revisa el dispositivo.",
33
+ "failure.songNotSent": " No se envió la petición de reproducción.",
34
+ "failure.preparationStopped": ({ evidence }) => `Las comprobaciones del dispositivo no terminaron (${evidence}); no se envió ninguna petición de reproducción. Revisa el estado del dispositivo en Spotify antes de volver a intentarlo.`,
35
+ "failure.queueRequestRequired": "No se añadió ninguna canción: no pude relacionar esta respuesta con una petición para la cola. Para seguir, pide de forma explícita añadir música a la cola.",
36
+ "failure.notSent": ({ code }) => `No envié nada a Spotify: no pude relacionarlo con las canciones que encontré (${code}). Vuelve a pedirlo con el nombre de la canción o del artista y la busco de nuevo.`,
37
+ "failure.outcome.lookup": "La búsqueda en Spotify no terminó",
38
+ "failure.outcome.unknown": "No se sabe con certeza el resultado de la acción en Spotify",
39
+ "failure.outcome.unconfirmed": "Spotify no confirmó la acción de reproducción",
40
+ "failure.reason": ({ reason }) => `Spotify indicó: ${reason}.`,
41
+ "failure.reasonUnknown": "La causa concreta no está confirmada.",
42
+ "failure.lookup": ({ outcome, evidence, reason }) => `${outcome} (${evidence}). ${reason} No se envió ninguna acción de reproducción ni de cola.`,
43
+ "failure.general": ({ targets, outcome, evidence, reason, playbackNotSent, recovered }) =>
44
+ `${targets ? `Objetivo: ${targets}.\n` : ""}${outcome} (${evidence}). ${reason}${playbackNotSent ? " No se envió la petición de reproducción." : ""} ${recovered ? "Terminó un intento acotado de recuperar el dispositivo; no se volvió a intentar." : "La acción no se volvió a intentar."} Consulta el estado de la reproducción y los dispositivos disponibles para saber más.`,
45
+
46
+ "queue.unverified": ({ one }) => `Spotify ${one ? "la aceptó" : "las aceptó"}, pero no pude leer tu cola para comprobarlo.`,
47
+ "queue.confirmedPlaying": ({ one }) => one ? "Confirmado: está sonando en Spotify." : "Confirmado en Spotify: la primera está sonando y el resto están en tu cola.",
48
+ "queue.confirmed": ({ count }) => count === 1 ? "Confirmado en tu cola de Spotify." : `Confirmadas en tu cola de Spotify: las ${count}.`,
49
+ "queue.partlyVisible": ({ accepted, confirmed, truncated }) => `Spotify aceptó ${accepted}; ahora mismo se ven ${confirmed} en tu cola${truncated ? " (Spotify solo muestra parte de una cola larga)" : ""}.`,
50
+ "queue.noPlayback": "Spotify no tiene ninguna canción sonando que sirva de punto de partida; no se añadió nada.",
51
+ "queue.noCandidates": "Después de filtrar no quedó ninguna candidata; no se añadió nada.",
52
+ "queue.requested": ({ requested, accepted, shortfall }) => `Pediste ${requested}; Spotify aceptó ${accepted}${shortfall ? `, faltan ${shortfall}` : ""}.`,
53
+ "queue.similarSeed": ({ artist }) => `Artistas afines a ${artist} según otros oyentes:`,
54
+ "queue.cancelled": "Dejé de añadir canciones tras la cancelación; las que ya se aceptaron siguen en la cola.",
55
+ "queue.stopped": ({ count, title, artist }) => `Añadí ${count} a la cola de Spotify y me detuve en "${title}" - ${artist}.`,
56
+ "queue.queued": ({ count }) => `Añadí ${count} a la cola de Spotify:`,
57
+ "queue.queuedSome": ({ count, unmatched }) => `Añadí ${count} de ${count + unmatched} a la cola de Spotify:`,
58
+ "queue.outcomeUnknown": "Spotify no confirmó si se añadió la canción en la que me detuve. Revisa la cola antes de volver a intentarlo; no la reenvié.",
59
+ "queue.started": ({ title, device, more }) => `No sonaba nada, así que puse "${title}"${device ? ` en ${device}` : ""}${more ? " y dejé el resto en la cola" : ""}.`,
60
+ "queue.unmatchedHeading": "No encontradas en Spotify:",
61
+ "queue.skippedDuplicates": ({ count }) => `Omití ${count} canciones repetidas.`,
62
+ "queue.skippedUncertain": ({ count }) => `Omití ${count} canciones cuyo resultado anterior en la cola no estaba claro, para no repetirlas; revisa la cola de Spotify.`,
63
+ "queue.skippedAvoided": ({ count }) => `Omití ${count} canciones que marcaste para evitar.`,
64
+ "queue.skippedKnown": ({ count }) => `Omití ${count} canciones que ya aparecen en tu historial, como pediste.`,
65
+ "queue.observationTruncated": "La comprobación de repetidas solo cubre la parte de la cola que muestra Spotify; lo que no se ve podría repetir alguna.",
66
+ "queue.notAddedHeading": "Sin añadir:",
67
+ "queue.earlierReceipt": "Este es el resultado de la cola anterior; esta aclaración no volvió a añadir las canciones.",
68
+
69
+ "playlist.partialUnknown": ({ name }) => `Spotify creó la playlist privada "${name}", pero no confirmó que se añadieran las canciones. Revísala antes de decidir si lo intentas de nuevo.`,
70
+ "playlist.partialEmpty": ({ name }) => `Spotify creó la playlist privada "${name}", pero no añadió las canciones. La playlist vacía ya existe; revísala antes de decidir si lo intentas de nuevo.`,
71
+ "playlist.saved": ({ name, count }) => `Guardada como la playlist privada de Spotify "${name}" (${count} canciones).`,
72
+
73
+ "action.playback.resume": "la petición de reproducción",
74
+ "action.playback.pause": "la pausa",
75
+ "action.playback.next": "el salto a la siguiente",
76
+ "action.playback.previous": "la vuelta a la anterior",
77
+ "action.playback.volume.set": "el cambio de volumen",
78
+ "action.playback.seek": "el cambio de posición",
79
+ "action.playback.shuffle.set": "el ajuste de aleatorio",
80
+ "action.playback.repeat.set": "el ajuste de repetición",
81
+ "action.playback.transfer": "el cambio de dispositivo",
82
+ "action.playback.queue.add": "la adición a la cola",
83
+ "action.library.remove": "la eliminación de tu biblioteca",
84
+ "action.library.save": "el guardado en tu biblioteca",
85
+ "action.playlist.write": "la creación de la playlist",
86
+ "action.playlist.edit": "el cambio en la playlist",
87
+ "action.playlist.rename": "el cambio de nombre de la playlist",
88
+ "action.playlist.unfollow": "la eliminación de la playlist de tu biblioteca",
89
+ "action.playlist.remove_track": "la eliminación de una canción de la playlist",
90
+ "action.other": "el cambio pedido",
91
+ "write.unknown": ({ action, target }) => `Spotify no confirmó ${action}${target ? ` para "${target}"` : ""}. Revisa su estado antes de decidir si lo intentas de nuevo.`,
92
+ "write.unfollowed": ({ name }) => `Quité "${name}" de tu biblioteca de Spotify (dejaste de seguirla). La playlist puede seguir existiendo para otros oyentes; no se borró.`,
93
+ "write.renamed": ({ name }) => `Spotify aceptó el nuevo nombre de la playlist: "${name}".`,
94
+ "write.trackRemoved": ({ name }) => `Spotify aceptó quitar una canción de "${name}".`,
95
+ "write.accepted": ({ action, count, targets }) => `Spotify aceptó ${action}${count === null ? "" : ` (${count} canciones)`}${targets ? `: ${targets}` : ""}.`,
96
+
97
+ "plan.heading.private_library": ({ count }) => `Plan de ${count} canciones de tu biblioteca:`,
98
+ "plan.heading.private_history": ({ count }) => `Plan de ${count} canciones de tu historial para volver a escuchar:`,
99
+ "plan.heading.external_catalog": ({ count }) => `${count} canciones:`,
100
+ "plan.heading.mixed": ({ count }) => `${count} canciones:`,
101
+ "plan.heading.timeCapsule": ({ count }) => `Máquina del tiempo de ${count} paradas por tus años de escucha:`,
102
+ "plan.heading.historicalReturn": ({ count }) => `${count} canciones de tu historial que volvieron tras una larga pausa:`,
103
+ "plan.heading.backToBack": ({ count }) => `${count} canciones de tu historial que escuchaste seguidas:`,
104
+ "plan.externalTrack": ({ line, release }) => `${line}, del álbum "${release}"`,
105
+ "plan.rationale": ({ text }) => ` Por qué está: ${text}`,
106
+ "plan.ordering": ({ text }) => `Por qué este orden: ${text}`,
107
+ "plan.notSaved": "Todavía no se ha guardado ni añadido nada a la cola.",
108
+ "plan.previewing": "Los avances de 30 segundos suenan en tu reproductor; las canciones completas están en Apple Music.",
109
+ "plan.pending": "Límite: este plan solo está pendiente en esta sesión, no se ha escrito en Spotify y no tiene efectos externos.",
110
+ "plan.backToBackBoundary": "Cómo leerlo: que dos reproducciones guardadas vayan seguidas no demuestra que usaras la repetición, que la volvieras a poner a propósito ni que te guste.",
111
+ "plan.validationScope": "Qué se comprobó: el planificador local verifica la identidad de cada canción, que pertenezca a las candidatas, la cantidad y el orden; los motivos y el encaje con el momento siguen siendo juicios del modelo a partir de los metadatos disponibles.",
112
+ "plan.unvalidated": "No pude validar este plan, así que no te daré una lista que no se haya comprobado contra candidatas fiables. Afina o amplía la búsqueda y vuelve a intentarlo.",
113
+
114
+ "edit.previewHeading": ({ name, before, after }) => `Vista previa de cambios en la playlist privada de Spotify "${name}" (${before} -> ${after} canciones):`,
115
+ "edit.status.added": "añadir",
116
+ "edit.status.moved": "mover",
117
+ "edit.status.retained": "mantener",
118
+ "edit.removeHeading": "Quitar:",
119
+ "edit.summary": ({ added, removed, moved, retained }) => `Resumen: ${added} añadidas, ${removed} quitadas, ${moved} movidas y ${retained} sin cambios.`,
120
+ "edit.boundary": "Límite: Spotify no ha cambiado. Confírmalo de forma explícita en tu próximo mensaje para que Moondog escriba exactamente esta versión.",
121
+ "edit.updated": ({ name, before, after }) => `Actualicé la playlist privada de Spotify "${name}" con la vista previa que confirmaste${before === null ? ` (ahora tiene ${after} canciones)` : ` (${before} -> ${after} canciones)`}.`,
122
+
123
+ "selection.note": ({ playlist, track }) => `\n\nSeleccionado para próximos cambios: playlist ${playlist}; canción ${track}.`,
124
+ "memory.spotifyExcluded": "\n\nLos resultados de Spotify no se guardan en la memoria general; solo se podían guardar citas tuyas verificables, y lo demás no se guardó.",
125
+ "memory.notSaved": "\n\nLa respuesta terminó, pero no se pudo guardar la memoria.",
126
+ "turn.cancelled": "Cancelé el resto.",
127
+ "turn.interrupted": "La respuesta se interrumpió. Abajo están los resultados registrados en Spotify; no se repitió ninguna acción.",
128
+ };
@@ -0,0 +1,128 @@
1
+ // Français. Keys match messages/en.mjs; a missing key falls back to English.
2
+ export default {
3
+ "discovery.reason.listenbrainz": ({ adjacent, seed, release }) => `Les données d'écoute de ListenBrainz relient ${adjacent} à ${seed} ; extrait de « ${release} ».`,
4
+ "discovery.reason.appleMusic": ({ queries, release, genre }) => `Catalogue Apple Music : ${queries ? `correspond à ${queries}` : "candidat"} ; extrait de « ${release} »${genre ? `, genre du catalogue : ${genre}` : ""}.`,
5
+ "discovery.reason.external": ({ release }) => `Tiré du catalogue externe consulté, sur « ${release} ».`,
6
+ "discovery.order": ({ path }) => `À écouter dans cet ordre : ${path}.`,
7
+ "discovery.unavailable": "Je n'ai pas pu joindre les services de découverte musicale, donc cette demande n'a produit aucune recommandation. Renvoyez la demande pour réessayer.",
8
+
9
+ "citations.heading": "Sources musicales publiques (distinctes de vos données d'écoute personnelles) :",
10
+ "citations.item": ({ label, url, storefront, date }) => `- [${label}](<${url}>) - boutique ${storefront}, consultée le ${date}.`,
11
+ "citations.scope": "Portée : ces pages de catalogue publiques ne prouvent ni une préférence personnelle, ni un historique d'écoute complet, ni la disponibilité sur d'autres plateformes.",
12
+ "web.sources.heading": ({ providers }) => `Sources web publiques (${providers})`,
13
+ "web.sources.headingUnnamed": "Sources web publiques",
14
+ "web.sources.published": ({ date }) => `date de la source : ${date}`,
15
+ "web.sources.retrieved": ({ date }) => `consulté le ${date}`,
16
+
17
+ "external.neighbors": ({ seed }) => seed ? `voisins d'écoute ListenBrainz de ${seed}` : "voisins d'écoute ListenBrainz",
18
+ "external.listenbrainz": ({ neighbor, date }) => `${neighbor} (identité Wikidata, CC0, consultée le ${date} ; ce n'est pas une similarité audio)`,
19
+ "external.appleMusic": ({ date }) => `correspondances de titres Apple Music US (consultées le ${date})`,
20
+ "external.limit": "Seuls les titres déjà présents dans votre bibliothèque importée, avec le même artiste, sont exclus : cela ne veut pas dire que vous ne les avez jamais entendus. Ce sont des suggestions d'écoute.",
21
+ "external.from": ({ parts, limit }) => `D'après ${parts.join(", et ")}. ${limit}`,
22
+
23
+ "failure.deviceList": ({ noMatch, names, code }) => `Le nom d'appareil ${noMatch ? "ne correspond à aucun appareil" : "correspond à plusieurs appareils"}. Spotify affiche actuellement : ${names}. Dites « joue-le sur » suivi du nom affiché ; le morceau choisi est conservé. Aucune demande de lecture n'a été envoyée. (${code})`,
24
+ "failure.networkRead": ({ notSent, code }) => `Une erreur réseau a empêché de lire l'état des appareils et de la lecture Spotify ; la préparation s'est arrêtée.${notSent ? " La demande de lecture n'a pas été envoyée." : ""} Vérifiez la connexion à Spotify et réessayez. (${code})`,
25
+ "failure.readiness.spotify_device_selection_required": "Le morceau choisi est conservé, mais l'appareil cible n'est pas déterminé. Indiquez l'appareil voulu ; l'appareil précédent ne sera pas utilisé à sa place.",
26
+ "failure.readiness.spotify_device_not_found": "Spotify ne voit pas l'appareil cible. Ouvrez Spotify dessus, puis redemandez la lecture ; aucun autre appareil n'a été utilisé à sa place.",
27
+ "failure.readiness.spotify_device_ambiguous": "Plusieurs appareils Spotify sont peut-être disponibles. Précisez le nom de l'appareil voulu.",
28
+ "failure.readiness.spotify_device_restricted": "L'appareil cible ne confirme pas le contrôle par l'API Web de Spotify. Choisissez un autre appareil disponible.",
29
+ "failure.readiness.spotify_device_changed": "Un autre appareil Spotify est devenu actif. Choisissez explicitement l'appareil voulu ; la lecture n'a pas été déplacée automatiquement.",
30
+ "failure.readiness.spotify_device_state_unconfirmed": "L'état des appareils et de la lecture Spotify est incomplet ou incohérent. Vérifiez l'appareil actuel dans Spotify avant de réessayer.",
31
+ "failure.readiness.spotify_device_not_ready": "L'appareil cible n'a pas été prêt dans le délai prévu. Ouvrez Spotify dessus, puis redemandez le même morceau.",
32
+ "failure.readiness.spotify_playback_restricted": "Spotify n'autorise pas la reprise de la lecture pour le moment. Vérifiez l'appareil cible.",
33
+ "failure.songNotSent": " La demande de lecture n'a pas été envoyée.",
34
+ "failure.preparationStopped": ({ evidence }) => `Les vérifications de l'appareil de lecture n'ont pas abouti (${evidence}) ; aucune demande de lecture n'a été envoyée. Vérifiez l'état de l'appareil Spotify avant de réessayer.`,
35
+ "failure.queueRequestRequired": "Aucun morceau n'a été ajouté à la file d'attente : je n'ai pas pu relier cette réponse à une demande d'ajout. Il faut une demande explicite d'ajouter de la musique à la file d'attente pour continuer.",
36
+ "failure.notSent": ({ code }) => `Rien n'a été envoyé à Spotify : je n'ai pas pu relier cela aux morceaux trouvés (${code}). Redemandez avec le titre ou l'artiste, et je chercherai à nouveau.`,
37
+ "failure.outcome.lookup": "La recherche Spotify n'a pas abouti",
38
+ "failure.outcome.unknown": "Le résultat de l'action Spotify est incertain",
39
+ "failure.outcome.unconfirmed": "L'action de lecture Spotify n'a pas été confirmée",
40
+ "failure.reason": ({ reason }) => `Motif indiqué par Spotify : ${reason}.`,
41
+ "failure.reasonUnknown": "La cause précise n'est pas confirmée.",
42
+ "failure.lookup": ({ outcome, evidence, reason }) => `${outcome} (${evidence}). ${reason} Aucune lecture ni aucun ajout à la file d'attente n'a été envoyé.`,
43
+ "failure.general": ({ targets, outcome, evidence, reason, playbackNotSent, recovered }) =>
44
+ `${targets ? `Cible : ${targets}.\n` : ""}${outcome} (${evidence}). ${reason}${playbackNotSent ? " La demande de lecture n'a pas été envoyée." : ""} ${recovered ? "Une récupération limitée de l'appareil s'est terminée ; aucune autre tentative n'a été faite." : "L'action n'a pas été retentée."} Lisez l'état de la lecture et les appareils disponibles pour aller plus loin.`,
45
+
46
+ "queue.unverified": ({ one }) => `Spotify ${one ? "l'a accepté" : "les a acceptés"}, mais je n'ai pas pu lire votre file d'attente pour vérifier.`,
47
+ "queue.confirmedPlaying": ({ one }) => one ? "Lecture confirmée sur Spotify." : "Confirmé sur Spotify : le premier est en lecture et les autres sont dans votre file d'attente.",
48
+ "queue.confirmed": ({ count }) => count === 1 ? "Confirmé dans votre file d'attente Spotify." : `Confirmé dans votre file d'attente Spotify : les ${count}.`,
49
+ "queue.partlyVisible": ({ accepted, confirmed, truncated }) => `Spotify en a accepté ${accepted} ; ${confirmed} apparaissent dans votre file d'attente pour l'instant${truncated ? " (Spotify n'affiche qu'une partie d'une longue file)" : ""}.`,
50
+ "queue.noPlayback": "Spotify n'a aucun morceau en cours à prendre comme point de départ ; rien n'a été ajouté.",
51
+ "queue.noCandidates": "Aucun candidat ne restait après le filtrage ; rien n'a été ajouté.",
52
+ "queue.requested": ({ requested, accepted, shortfall }) => `${requested} demandés ; Spotify en a accepté ${accepted}${shortfall ? `, il en manque ${shortfall}` : ""}.`,
53
+ "queue.similarSeed": ({ artist }) => `Artistes proches de ${artist}, d'après les auditeurs :`,
54
+ "queue.cancelled": "Ajout arrêté après l'annulation ; les morceaux acceptés restent dans la file d'attente.",
55
+ "queue.stopped": ({ count, title, artist }) => `${count} ajoutés à la file d'attente Spotify, puis arrêt à « ${title} » - ${artist}.`,
56
+ "queue.queued": ({ count }) => `${count} ajoutés à la file d'attente Spotify :`,
57
+ "queue.queuedSome": ({ count, unmatched }) => `${count} sur ${count + unmatched} ajoutés à la file d'attente Spotify :`,
58
+ "queue.outcomeUnknown": "Spotify n'a pas confirmé si le morceau interrompu a été ajouté. Vérifiez la file d'attente avant de réessayer ; cette écriture n'a pas été renvoyée.",
59
+ "queue.started": ({ title, device, more }) => `Rien ne jouait, alors j'ai lancé « ${title} »${device ? ` sur ${device}` : ""}${more ? " et mis la suite dans la file" : ""}.`,
60
+ "queue.unmatchedHeading": "Introuvables sur Spotify :",
61
+ "queue.skippedDuplicates": ({ count }) => `${count} doublons ignorés.`,
62
+ "queue.skippedUncertain": ({ count }) => `${count} morceaux ignorés pour éviter un doublon, car leur ajout précédent est incertain ; vérifiez la file d'attente Spotify.`,
63
+ "queue.skippedAvoided": ({ count }) => `${count} morceaux à éviter ignorés.`,
64
+ "queue.skippedKnown": ({ count }) => `${count} morceaux déjà présents dans votre historique ignorés, comme demandé.`,
65
+ "queue.observationTruncated": "Le dédoublonnage ne couvre que l'extrait limité de la file que Spotify renvoie ; des entrées non visibles peuvent encore faire doublon.",
66
+ "queue.notAddedHeading": "Non ajoutés :",
67
+ "queue.earlierReceipt": "Voici le reçu de l'ajout précédent ; cette précision n'a pas ajouté les morceaux une seconde fois.",
68
+
69
+ "playlist.partialUnknown": ({ name }) => `Spotify a créé la playlist privée « ${name} », mais l'ajout de ses morceaux n'a pas été confirmé. Vérifiez-la avant de décider de réessayer.`,
70
+ "playlist.partialEmpty": ({ name }) => `Spotify a créé la playlist privée « ${name} », mais n'a pas ajouté ses morceaux. La playlist vide existe désormais ; vérifiez-la avant de décider de réessayer.`,
71
+ "playlist.saved": ({ name, count }) => `Enregistré comme playlist Spotify privée « ${name} » (${count} morceaux).`,
72
+
73
+ "action.playback.resume": "demande de lecture",
74
+ "action.playback.pause": "demande de pause",
75
+ "action.playback.next": "demande de morceau suivant",
76
+ "action.playback.previous": "demande de morceau précédent",
77
+ "action.playback.volume.set": "changement de volume",
78
+ "action.playback.seek": "changement de position de lecture",
79
+ "action.playback.shuffle.set": "réglage de la lecture aléatoire",
80
+ "action.playback.repeat.set": "réglage de la répétition",
81
+ "action.playback.transfer": "changement d'appareil",
82
+ "action.playback.queue.add": "ajout à la file d'attente",
83
+ "action.library.remove": "retrait de la bibliothèque",
84
+ "action.library.save": "ajout à la bibliothèque",
85
+ "action.playlist.write": "création de playlist",
86
+ "action.playlist.edit": "modification de playlist",
87
+ "action.playlist.rename": "renommage de playlist",
88
+ "action.playlist.unfollow": "retrait de la playlist de la bibliothèque",
89
+ "action.playlist.remove_track": "retrait d'un morceau de la playlist",
90
+ "action.other": "modification demandée",
91
+ "write.unknown": ({ action, target }) => `Spotify n'a pas confirmé l'opération « ${action} »${target ? ` pour « ${target} »` : ""}. Vérifiez son état avant de décider de réessayer.`,
92
+ "write.unfollowed": ({ name }) => `« ${name} » a été retirée de votre bibliothèque Spotify (vous ne la suivez plus). La playlist peut encore exister pour d'autres auditeurs ; elle n'a pas été supprimée pour tous.`,
93
+ "write.renamed": ({ name }) => `Spotify a accepté de renommer la playlist en « ${name} ».`,
94
+ "write.trackRemoved": ({ name }) => `Spotify a accepté le retrait d'un morceau de « ${name} ».`,
95
+ "write.accepted": ({ action, count, targets }) => `Spotify a accepté l'opération « ${action} »${count === null ? "" : ` (${count} morceaux)`}${targets ? ` : ${targets}` : ""}.`,
96
+
97
+ "plan.heading.private_library": ({ count }) => `Sélection de ${count} morceaux tirée de votre bibliothèque :`,
98
+ "plan.heading.private_history": ({ count }) => `${count} morceaux à réécouter, tirés de votre historique :`,
99
+ "plan.heading.external_catalog": ({ count }) => `${count} morceaux :`,
100
+ "plan.heading.mixed": ({ count }) => `${count} morceaux :`,
101
+ "plan.heading.timeCapsule": ({ count }) => `Machine à remonter le temps en ${count} étapes à travers vos années d'écoute :`,
102
+ "plan.heading.historicalReturn": ({ count }) => `${count} morceaux revenus après une longue absence :`,
103
+ "plan.heading.backToBack": ({ count }) => `${count} morceaux que vous avez écoutés à la suite :`,
104
+ "plan.externalTrack": ({ line, release }) => `${line}, extrait de « ${release} »`,
105
+ "plan.rationale": ({ text }) => ` Raison du choix : ${text}`,
106
+ "plan.ordering": ({ text }) => `Raison de l'ordre : ${text}`,
107
+ "plan.notSaved": "Rien n'a encore été enregistré ni ajouté à la file d'attente.",
108
+ "plan.previewing": "Les extraits de 30 secondes jouent dans votre lecteur ; les morceaux complets sont sur Apple Music.",
109
+ "plan.pending": "Limite : cette sélection n'attend une confirmation que dans la session en cours ; elle n'a pas été écrite sur Spotify et n'a aucun effet extérieur.",
110
+ "plan.backToBackBoundary": "Limite d'interprétation : des écoutes enregistrées à la suite ne prouvent ni le mode répétition, ni une réécoute voulue, ni un goût pour le morceau.",
111
+ "plan.validationScope": "Portée de la vérification : le planificateur local vérifie l'identité des morceaux, leur appartenance aux candidats, leur nombre et leur ordre ; les raisons du choix et l'adéquation à la situation restent des jugements du modèle, fondés sur les métadonnées disponibles.",
112
+ "plan.unvalidated": "Je n'ai pas pu vérifier cette sélection, donc je ne renverrai pas une liste de morceaux non comparée à des candidats fiables. Précisez ou élargissez la recherche, puis réessayez.",
113
+
114
+ "edit.previewHeading": ({ name, before, after }) => `Aperçu de la modification de la playlist Spotify privée « ${name} » (${before} -> ${after} morceaux) :`,
115
+ "edit.status.added": "ajout",
116
+ "edit.status.moved": "déplacé",
117
+ "edit.status.retained": "conservé",
118
+ "edit.removeHeading": "À retirer :",
119
+ "edit.summary": ({ added, removed, moved, retained }) => `Résumé des changements : ${added} ajoutés, ${removed} retirés, ${moved} déplacés, ${retained} conservés à leur place.`,
120
+ "edit.boundary": "Limite : Spotify n'a pas changé. Confirmez explicitement dans votre prochain message pour que Moondog écrive exactement cette version conservée.",
121
+ "edit.updated": ({ name, before, after }) => `Playlist Spotify privée « ${name} » mise à jour d'après l'aperçu confirmé${before === null ? ` (${after} morceaux maintenant)` : ` (${before} -> ${after} morceaux)`}.`,
122
+
123
+ "selection.note": ({ playlist, track }) => `\n\nSélection pour les prochaines modifications : playlist ${playlist} ; morceau ${track}.`,
124
+ "memory.spotifyExcluded": "\n\nLes résultats Spotify sont exclus de la mémoire générale ; seules vos propres paroles vérifiées pouvaient être retenues, et le reste n'a pas été enregistré.",
125
+ "memory.notSaved": "\n\nLa réponse est terminée, mais la mémoire n'a pas été enregistrée.",
126
+ "turn.cancelled": "Suite annulée.",
127
+ "turn.interrupted": "La réponse a été interrompue. Les résultats Spotify enregistrés figurent ci-dessous ; aucune action n'a été relancée.",
128
+ };