@audiofool/moondog 0.1.0 → 0.2.0-beta.3

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 (125) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +156 -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 +253 -53
  10. package/src/core/application-update.mjs +320 -0
  11. package/src/core/capability-catalog.mjs +290 -55
  12. package/src/core/listener-avoids.mjs +101 -0
  13. package/src/core/listening-now.mjs +108 -0
  14. package/src/core/listening-profile-domain-services.mjs +189 -41
  15. package/src/core/listening-upkeep.mjs +31 -0
  16. package/src/core/moondog-application.mjs +2115 -107
  17. package/src/core/playback-preferences.mjs +97 -0
  18. package/src/core/spotify-listening-intent.mjs +124 -0
  19. package/src/core/synthetic-domain-services.mjs +24 -3
  20. package/src/core/track-title.mjs +107 -0
  21. package/src/demo/moondog-demo.mjs +4 -22
  22. package/src/i18n/index.mjs +285 -0
  23. package/src/i18n/messages/de.mjs +135 -0
  24. package/src/i18n/messages/en.mjs +136 -0
  25. package/src/i18n/messages/es.mjs +135 -0
  26. package/src/i18n/messages/fr.mjs +135 -0
  27. package/src/i18n/messages/it.mjs +135 -0
  28. package/src/i18n/messages/ja.mjs +135 -0
  29. package/src/i18n/messages/ko.mjs +135 -0
  30. package/src/i18n/messages/pt.mjs +135 -0
  31. package/src/i18n/messages/zh-Hant.mjs +135 -0
  32. package/src/i18n/messages/zh.mjs +135 -0
  33. package/src/i18n/preferences.mjs +34 -0
  34. package/src/i18n/screen/es.mjs +967 -0
  35. package/src/i18n/screen/ja.mjs +967 -0
  36. package/src/i18n/screen/pt.mjs +967 -0
  37. package/src/i18n/screen/zh.mjs +967 -0
  38. package/src/importers/apple-music-library/index.mjs +0 -16
  39. package/src/importers/apple-music-library/sqlite-projection.mjs +51 -9
  40. package/src/integrations/apple-music/catalog.mjs +166 -14
  41. package/src/integrations/apple-music/music-app.mjs +232 -0
  42. package/src/integrations/apple-music/storefront.mjs +28 -0
  43. package/src/integrations/apple-music/terminal-preview-player.mjs +221 -0
  44. package/src/integrations/lastfm/history.mjs +221 -0
  45. package/src/integrations/lastfm/settings.mjs +75 -0
  46. package/src/integrations/netease/ncm-cli-player.mjs +328 -0
  47. package/src/integrations/open-music-similarity/artist-radio.mjs +26 -18
  48. package/src/integrations/public-playlists.mjs +2 -1
  49. package/src/integrations/spotify/account-data-profile.mjs +14 -6
  50. package/src/integrations/spotify/authentication.mjs +149 -15
  51. package/src/integrations/spotify/catalog-resolver.mjs +9 -6
  52. package/src/integrations/spotify/connection.mjs +2 -1
  53. package/src/integrations/spotify/credential-store.mjs +4 -0
  54. package/src/integrations/spotify/extended-streaming-history.mjs +11 -11
  55. package/src/integrations/spotify/history-archive.mjs +1 -1
  56. package/src/integrations/spotify/library-snapshot.mjs +227 -0
  57. package/src/integrations/spotify/recent-activity.mjs +2 -0
  58. package/src/integrations/spotify/service.mjs +873 -69
  59. package/src/integrations/spotify/web-api-client.mjs +410 -127
  60. package/src/integrations/web/brave-web.mjs +45 -0
  61. package/src/integrations/web/codex-web.mjs +15 -82
  62. package/src/integrations/web/deepseek-web.mjs +81 -0
  63. package/src/integrations/web/web-research.mjs +119 -0
  64. package/src/integrations/web/web-result.mjs +215 -0
  65. package/src/integrations/youtube-music/takeout.mjs +3 -1
  66. package/src/memory/local-memory-store.mjs +542 -57
  67. package/src/profile/history-import.mjs +42 -6
  68. package/src/profile/listener-corrections.mjs +22 -0
  69. package/src/profile/listener-profile-build.mjs +461 -0
  70. package/src/profile/listener-profile-store.mjs +130 -0
  71. package/src/profile/listener-profile-verification.mjs +68 -0
  72. package/src/profile/listener-profile-worker.mjs +179 -0
  73. package/src/profile/listening-history-store.mjs +640 -17
  74. package/src/profile/listening-profile-projection.mjs +454 -86
  75. package/src/profile/local-music-data.mjs +1 -1
  76. package/src/profile/moondog-suggestions.mjs +184 -0
  77. package/src/profile/music-providers.mjs +11 -7
  78. package/src/profile/profile-exploration.mjs +92 -0
  79. package/src/profile/recorded-measures.mjs +24 -0
  80. package/src/profile/spotify-archive-taste.mjs +0 -4
  81. package/src/runtime/pi/agent-runtime.mjs +2536 -692
  82. package/src/runtime/pi/authentication.mjs +34 -1
  83. package/src/runtime/pi/configured-runtime.mjs +22 -6
  84. package/src/runtime/pi/conversation-name.mjs +53 -0
  85. package/src/runtime/pi/memory-agent-runtime.mjs +2 -2
  86. package/src/runtime/pi/model-catalog.mjs +28 -5
  87. package/src/runtime/pi/model-settings.mjs +202 -0
  88. package/src/runtime/pi/model-transport.mjs +10 -0
  89. package/src/runtime/pi/persistent-credential-store.mjs +67 -31
  90. package/src/runtime/pi/profile-build-runtime.mjs +161 -0
  91. package/src/runtime/pi/public-model-catalog.mjs +288 -0
  92. package/src/runtime/pi/runtime-settings.mjs +10 -2
  93. package/src/runtime/pi/skills/listener-profile/SKILL.md +48 -0
  94. package/src/runtime/pi/tool-trace.mjs +37 -0
  95. package/src/surfaces/cli/auth-command.mjs +12 -4
  96. package/src/surfaces/cli/brand-components.mjs +310 -132
  97. package/src/surfaces/cli/brand-theme.mjs +51 -28
  98. package/src/surfaces/cli/command-completions.mjs +58 -42
  99. package/src/surfaces/cli/format-output.mjs +199 -388
  100. package/src/surfaces/cli/history-import-view.mjs +292 -139
  101. package/src/surfaces/cli/import-guides.mjs +2 -0
  102. package/src/surfaces/cli/listener-profile-command.mjs +92 -0
  103. package/src/surfaces/cli/listening-menu.mjs +7 -4
  104. package/src/surfaces/cli/profile-command.mjs +30 -21
  105. package/src/surfaces/cli/seasons-chart.mjs +87 -0
  106. package/src/surfaces/cli/spotify-command.mjs +54 -6
  107. package/src/surfaces/cli/taste-profile-model.mjs +78 -79
  108. package/src/surfaces/cli/taste-profile-view.mjs +33 -26
  109. package/src/surfaces/cli/terminal-art.mjs +140 -35
  110. package/src/surfaces/cli/tui.mjs +1066 -357
  111. package/src/surfaces/cli/update-command.mjs +32 -0
  112. package/src/surfaces/cli/web-command.mjs +53 -14
  113. package/src/surfaces/html/tasteprint.mjs +43 -70
  114. package/contracts/v1/capability-policy.schema.json +0 -572
  115. package/contracts/v1/profile-evidence.schema.json +0 -237
  116. package/contracts/v1/tool-invocation.schema.json +0 -318
  117. package/contracts/v1/tool-receipt.schema.json +0 -1066
  118. package/scripts/demo-studio.mjs +0 -58
  119. package/src/core/apple-data-migration.mjs +0 -50
  120. package/src/demo/moondog-interactive-tasteprint-demo.mjs +0 -293
  121. package/src/demo/moondog-tasteprint-demo.mjs +0 -631
  122. package/src/integrations/spotify/index.mjs +0 -80
  123. package/src/surfaces/web/showcase.mjs +0 -272
  124. package/src/surfaces/web/studio-page.mjs +0 -4148
  125. package/src/surfaces/web/studio.mjs +0 -2288
@@ -0,0 +1,135 @@
1
+ // 한국어. Keys match messages/en.mjs; a missing key falls back to English.
2
+ export default {
3
+ "discovery.reason.listenbrainz": ({ adjacent, seed, release }) => `ListenBrainz 청취 데이터에서 ${adjacent}, ${seed} 두 아티스트가 이어집니다. 수록: "${release}".`,
4
+ "discovery.reason.appleMusic": ({ queries, release, genre }) => `Apple Music 카탈로그 ${queries ? `검색어 ${queries}와 일치` : "후보"}. 수록: "${release}"${genre ? `, 카탈로그 장르: ${genre}` : ""}.`,
5
+ "discovery.reason.external": ({ release }) => `가져온 외부 카탈로그에서 찾았습니다. 수록: "${release}".`,
6
+ "discovery.order": ({ path }) => `이 순서로 들어 보세요: ${path}.`,
7
+ "discovery.unavailable": "음악 탐색 서비스에 연결하지 못해 이번에는 추천을 만들지 못했습니다. 같은 요청을 다시 보내 주세요.",
8
+
9
+ "citations.heading": "공개 음악 출처(개인 청취 증거와는 별개):",
10
+ "citations.item": ({ label, url, storefront, date }) => `- [${label}](<${url}>) - ${storefront} 스토어, ${date}에 확인.`,
11
+ "citations.scope": "범위: 이 공개 카탈로그 페이지는 개인 취향, 전체 청취 기록, 다른 플랫폼에서의 이용 가능 여부를 증명하지 않습니다.",
12
+ "web.sources.heading": ({ providers }) => `공개 웹 출처(${providers})`,
13
+ "web.sources.headingUnnamed": "공개 웹 출처",
14
+ "web.sources.published": ({ date }) => `출처 날짜: ${date}`,
15
+ "web.sources.retrieved": ({ date }) => `확인: ${date}`,
16
+
17
+ "external.neighbors": ({ seed }) => seed ? `${seed}에서 이어지는 ListenBrainz 청취자 이웃` : "ListenBrainz 청취자 이웃",
18
+ "external.listenbrainz": ({ neighbor, date }) => `${neighbor} (Wikidata 식별 정보, CC0, ${date}에 확인. 오디오 유사도가 아닙니다)`,
19
+ "external.appleMusic": ({ date }) => `Apple Music 미국 스토어 제목 일치 (${date}에 확인)`,
20
+ "external.limit": "가져온 라이브러리에서 제목과 아티스트가 같은 곡만 제외했기 때문에, 한 번도 들어 본 적 없는 곡이라는 뜻은 아닙니다. 들어 볼 만한 제안입니다.",
21
+ "external.from": ({ parts, limit }) => `출처: ${parts.join(", 그리고 ")}. ${limit}`,
22
+
23
+ "failure.deviceList": ({ noMatch, names, code }) => `기기 이름이 ${noMatch ? "일치하지 않았습니다" : "여러 기기와 일치했습니다"}. 현재 Spotify에 보이는 기기: ${names}. 표시된 기기 이름으로 "이 기기에서 재생해"라고 말해 주세요. 고른 곡은 그대로 남아 있습니다. 곡 재생 요청은 보내지 않았습니다. (${code})`,
24
+ "failure.networkRead": ({ notSent, code }) => `네트워크 오류로 Spotify 기기와 재생 상태를 읽지 못해 준비를 멈췄습니다.${notSent ? " 곡 재생 요청은 보내지 않았습니다." : ""} Spotify 연결을 확인한 뒤 다시 시도해 주세요. (${code})`,
25
+ "failure.readiness.spotify_device_selection_required": "고른 곡은 남아 있지만 재생할 기기가 정해지지 않았습니다. 원하는 기기 이름을 말해 주세요. 이전 기기로 대신 재생하지 않습니다.",
26
+ "failure.readiness.spotify_device_not_found": "Spotify에서 대상 기기가 보이지 않습니다. 그 기기에서 Spotify를 연 다음 다시 재생을 요청해 주세요. 다른 기기로 대신 재생하지 않았습니다.",
27
+ "failure.readiness.spotify_device_ambiguous": "사용할 수 있는 Spotify 기기가 여러 대일 수 있습니다. 원하는 기기 이름을 알려 주세요.",
28
+ "failure.readiness.spotify_device_restricted": "대상 기기가 Spotify Web API 제어를 허용하는지 확인되지 않습니다. 사용할 수 있는 다른 기기를 골라 주세요.",
29
+ "failure.readiness.spotify_device_changed": "다른 Spotify 기기가 활성화되었습니다. 원하는 기기를 직접 골라 주세요. 재생을 자동으로 옮기지 않았습니다.",
30
+ "failure.readiness.spotify_device_state_unconfirmed": "Spotify 기기와 재생 상태가 불완전하거나 서로 맞지 않습니다. Spotify에서 현재 기기를 확인한 뒤 다시 시도해 주세요.",
31
+ "failure.readiness.spotify_device_not_ready": "대상 기기가 정해진 대기 시간 안에 준비되지 않았습니다. 그 기기에서 Spotify를 연 다음 같은 곡을 다시 요청해 주세요.",
32
+ "failure.readiness.spotify_playback_restricted": "지금은 Spotify가 재생 재개를 허용하지 않습니다. 대상 기기를 확인해 주세요.",
33
+ "failure.songNotSent": " 곡 재생 요청은 보내지 않았습니다.",
34
+ "failure.preparationStopped": ({ evidence }) => `재생 기기 확인을 마치지 못했습니다(${evidence}). 곡 재생 요청은 보내지 않았습니다. Spotify 기기 상태를 확인한 뒤 다시 시도해 주세요.`,
35
+ "failure.queueRequestRequired": "곡을 대기열에 넣지 않았습니다. 이 답변을 대기열 추가 요청과 연결하지 못했습니다. 계속하려면 재생 대기열에 음악을 넣어 달라고 분명히 요청해 주세요.",
36
+ "failure.notSent": ({ code }) => `Spotify에 아무것도 보내지 않았습니다. 방금 찾은 곡과 연결하지 못했습니다(${code}). 곡 제목이나 아티스트 이름을 넣어 다시 말해 주시면 다시 찾아볼게요.`,
37
+ "failure.avoided": "Spotify에 아무것도 보내지 않았습니다. 이 아티스트나 곡은 틀지 말라고 하셨어요. 바꾸고 싶으면 언제든 말해 주세요.",
38
+ "failure.outcome.lookup": "Spotify 검색을 마치지 못했습니다",
39
+ "failure.outcome.unknown": "Spotify 작업 결과가 확실하지 않습니다",
40
+ "failure.outcome.unconfirmed": "Spotify 재생 작업이 확인되지 않았습니다",
41
+ "failure.reason": ({ reason }) => `Spotify가 알려 준 이유: ${reason}.`,
42
+ "failure.reasonUnknown": "정확한 원인은 확인되지 않았습니다.",
43
+ "failure.lookup": ({ outcome, evidence, reason }) => `${outcome} (${evidence}). ${reason} 재생이나 대기열 작업은 보내지 않았습니다.`,
44
+ "failure.general": ({ targets, outcome, evidence, reason, playbackNotSent, recovered }) =>
45
+ `${targets ? `대상: ${targets}.\n` : ""}${outcome} (${evidence}). ${reason}${playbackNotSent ? " 곡 재생 요청은 보내지 않았습니다." : ""} ${recovered ? "제한된 기기 복구를 한 번 마쳤고, 더 이상 다시 시도하지 않았습니다." : "작업을 다시 시도하지 않았습니다."} 재생 상태와 사용 가능한 기기를 읽으면 더 자세히 살펴볼 수 있습니다.`,
46
+
47
+ "queue.unverified": () => "Spotify가 받아들였지만, 대기열을 읽어 다시 확인하지는 못했습니다.",
48
+ "queue.confirmedPlaying": ({ one }) => one ? "Spotify에서 재생 중인 것을 확인했습니다." : "Spotify에서 확인했습니다. 첫 곡이 재생 중이고 나머지는 대기열에 있습니다.",
49
+ "queue.confirmed": ({ count }) => count === 1 ? "Spotify 대기열에서 확인했습니다." : `Spotify 대기열에서 ${count}곡 모두 확인했습니다.`,
50
+ "queue.partlyVisible": ({ accepted, confirmed, truncated }) => `Spotify가 ${accepted}곡을 받아들였고, 지금 대기열에는 ${confirmed}곡이 보입니다${truncated ? "(Spotify는 긴 대기열의 일부만 보여 줍니다)" : ""}.`,
51
+ "queue.noPlayback": "Spotify에 기준으로 삼을 현재 곡이 없어 아무것도 대기열에 넣지 않았습니다.",
52
+ "queue.noCandidates": "걸러 낸 뒤 남은 후보가 없어 아무것도 대기열에 넣지 않았습니다.",
53
+ "queue.requested": ({ requested, accepted, shortfall }) => `${requested}곡을 요청했고 Spotify가 ${accepted}곡을 받아들였습니다${shortfall ? `. ${shortfall}곡이 모자랍니다` : ""}.`,
54
+ "queue.similarSeed": ({ artist }) => `청취자 데이터로 본 비슷한 아티스트 (기준: ${artist}):`,
55
+ "queue.cancelled": "취소 요청에 따라 대기열 추가를 멈췄습니다. 이미 받아들여진 곡은 대기열에 남아 있습니다.",
56
+ "queue.stopped": ({ count, title, artist }) => `Spotify 대기열에 ${count}곡을 넣은 뒤 "${title}" - ${artist}에서 멈췄습니다.`,
57
+ "queue.queued": ({ count }) => `Spotify 대기열에 ${count}곡을 넣었습니다:`,
58
+ "queue.queuedSome": ({ count, unmatched }) => `Spotify 대기열에 ${count + unmatched}곡 중 ${count}곡을 넣었습니다:`,
59
+ "queue.outcomeUnknown": "멈춘 곡이 추가되었는지 Spotify가 확인해 주지 않았습니다. 다시 시도하기 전에 대기열을 확인해 주세요. 이 작업은 다시 보내지 않았습니다.",
60
+ "queue.started": ({ title, device, more }) => `재생 중인 곡이 없어서 ${device ? `${device}에서 ` : ""}"${title}" 재생을 시작했습니다${more ? ". 나머지는 그 뒤에 이어집니다" : ""}.`,
61
+ "queue.unmatchedHeading": "Spotify에서 찾지 못한 곡:",
62
+ "queue.skippedDuplicates": ({ count }) => `중복된 곡 ${count}곡을 건너뛰었습니다.`,
63
+ "queue.skippedUncertain": ({ count }) => `이전 대기열 결과가 불확실한 곡 ${count}곡은 중복을 피하려고 건너뛰었습니다. Spotify 대기열을 확인해 주세요.`,
64
+ "queue.skippedAvoided": ({ count }) => `피해 달라고 한 곡 ${count}곡을 건너뛰었습니다.`,
65
+ "plan.avoidedLeftOut": ({ titles }) => `틀지 말라고 하신 곡은 뺐습니다: ${titles}.`,
66
+ "queue.skippedKnown": ({ count }) => `요청대로 청취 기록에 있는 곡 ${count}곡을 건너뛰었습니다.`,
67
+ "queue.mix": ({ familiar = 0, returning = 0, heard = 0, known_artist: knownArtist = 0, new: fresh = 0 }) =>
68
+ `청취 기록 기준: ${[familiar && `잘 아는 곡 ${familiar}곡`, returning && `오랜만인 곡 ${returning}곡`, heard && `한두 번 들은 곡 ${heard}곡`, knownArtist && `아는 아티스트의 새 곡 ${knownArtist}곡`, fresh && `처음 듣는 곡 ${fresh}곡`].filter(Boolean).join(", ")}.`,
69
+ "queue.observationTruncated": "Spotify가 돌려준 대기열의 일부만 확인했습니다. 보이지 않는 부분에 같은 곡이 있을 수 있습니다.",
70
+ "queue.notAddedHeading": "추가하지 않은 곡:",
71
+ "queue.earlierReceipt": "이것은 앞선 대기열 작업의 결과입니다. 이번 설명으로 곡을 다시 넣지는 않았습니다.",
72
+
73
+ "playlist.partialUnknown": ({ name }) => `Spotify에 비공개 플레이리스트 "${name}" 생성은 끝났지만, 곡 추가는 확인되지 않았습니다. 다시 시도할지 정하기 전에 확인해 주세요.`,
74
+ "playlist.partialEmpty": ({ name }) => `Spotify에 비공개 플레이리스트 "${name}" 생성은 끝났지만 곡은 추가하지 않았습니다. 빈 플레이리스트가 생겼으니 다시 시도할지 정하기 전에 확인해 주세요.`,
75
+ "playlist.saved": ({ name, count }) => `Spotify 비공개 플레이리스트 "${name}"(${count}곡)으로 저장했습니다.`,
76
+
77
+ "action.playback.resume": "재생 요청",
78
+ "action.playback.pause": "일시 정지 요청",
79
+ "action.playback.next": "다음 곡 요청",
80
+ "action.playback.previous": "이전 곡 요청",
81
+ "action.playback.volume.set": "볼륨 변경",
82
+ "action.playback.seek": "재생 위치 변경",
83
+ "action.playback.shuffle.set": "셔플 설정",
84
+ "action.playback.repeat.set": "반복 설정",
85
+ "action.playback.transfer": "기기 전환",
86
+ "action.playback.queue.add": "대기열 추가",
87
+ "action.library.remove": "보관함에서 삭제",
88
+ "action.library.save": "보관함에 저장",
89
+ "action.playlist.write": "플레이리스트 만들기",
90
+ "action.playlist.edit": "플레이리스트 변경",
91
+ "action.playlist.rename": "플레이리스트 이름 변경",
92
+ "action.playlist.unfollow": "보관함에서 플레이리스트 삭제",
93
+ "action.playlist.remove_track": "플레이리스트 곡 삭제",
94
+ "action.other": "요청한 변경",
95
+ "write.unknown": ({ action, target }) => `Spotify에서 다음 작업이 확인되지 않았습니다: ${action}${target ? ` ("${target}")` : ""}. 다시 시도할지 정하기 전에 상태를 확인해 주세요.`,
96
+ "write.unfollowed": ({ name }) => `Spotify 보관함에서 "${name}" 플레이리스트를 삭제했습니다(팔로우 취소). 다른 청취자에게는 플레이리스트가 남아 있을 수 있으며, 완전히 삭제된 것은 아닙니다.`,
97
+ "write.renamed": ({ name }) => `Spotify가 플레이리스트 이름 변경을 받아들였습니다. 새 이름: "${name}".`,
98
+ "write.trackRemoved": ({ name }) => `Spotify가 "${name}"에서 곡 하나를 삭제하는 것을 받아들였습니다.`,
99
+ "write.accepted": ({ action, count, targets }) => `Spotify가 다음 작업을 받아들였습니다: ${action}${count === null ? "" : ` (${count}곡)`}${targets ? ` - ${targets}` : ""}.`,
100
+
101
+ "plan.heading.private_library": ({ count }) => `내 라이브러리에서 고른 ${count}곡 구성:`,
102
+ "plan.heading.private_history": ({ count }) => `청취 기록에서 다시 들어 볼 ${count}곡:`,
103
+ "plan.heading.external_catalog": ({ count }) => `${count}곡:`,
104
+ "plan.heading.mixed": ({ count }) => `${count}곡:`,
105
+ "plan.heading.timeCapsule": ({ count }) => `여러 해의 청취를 지나는 ${count}곡 타임머신:`,
106
+ "plan.heading.historicalReturn": ({ count }) => `오랜만에 다시 돌아온 ${count}곡:`,
107
+ "plan.heading.backToBack": ({ count }) => `연달아 재생했던 ${count}곡:`,
108
+ "plan.externalTrack": ({ line, release }) => `${line}, 수록: "${release}"`,
109
+ "plan.rationale": ({ text }) => ` 고른 이유: ${text}`,
110
+ "plan.ordering": ({ text }) => `순서를 정한 이유: ${text}`,
111
+ "plan.notSaved": "아직 저장하거나 대기열에 넣지 않았습니다.",
112
+ "plan.previewing": "30초 미리 듣기가 플레이어에서 재생 중입니다. 전체 곡은 Apple Music에서 들을 수 있습니다.",
113
+ "plan.pending": "안내: 이 구성은 지금 실행 중인 세션에서만 확인을 기다리며, Spotify에 기록되지 않았고 다른 외부 영향도 없습니다.",
114
+ "plan.backToBackBoundary": "해석 안내: 연달아 남은 재생 기록만으로는 반복 재생 설정, 의도적인 다시 듣기, 좋아함을 증명할 수 없습니다.",
115
+ "plan.validationScope": "검증 범위: 로컬 플래너가 곡 식별 정보, 후보 범위, 곡 수, 순서를 검증했습니다. 고른 이유와 상황에 맞는지는 가진 정보에 바탕을 둔 모델의 판단입니다.",
116
+ "plan.unvalidated": "이 플레이리스트 구성을 검증하지 못해, 신뢰할 수 있는 후보와 대조하지 않은 곡 목록은 보여 드리지 않습니다. 검색 범위를 좁히거나 넓혀서 다시 시도해 주세요.",
117
+
118
+ "edit.previewHeading": ({ name, before, after }) => `Spotify 비공개 플레이리스트 "${name}" 편집 미리 보기 (${before} -> ${after}곡):`,
119
+ "edit.status.added": "추가",
120
+ "edit.status.moved": "이동",
121
+ "edit.status.retained": "유지",
122
+ "edit.removeHeading": "삭제할 곡:",
123
+ "edit.summary": ({ added, removed, moved, retained }) => `변경 요약: 추가 ${added}, 삭제 ${removed}, 이동 ${moved}, 제자리 유지 ${retained}.`,
124
+ "edit.boundary": "안내: Spotify는 아직 바뀌지 않았습니다. 다음 메시지에서 분명히 확인해 주시면 Moondog가 이 보관된 버전을 그대로 기록합니다.",
125
+ "edit.updated": ({ name, before, after }) => `Spotify 비공개 플레이리스트 "${name}" 업데이트를 마쳤습니다. 확인한 미리 보기 그대로입니다${before === null ? ` (지금 ${after}곡)` : ` (${before} -> ${after}곡)`}.`,
126
+
127
+ "selection.note": ({ playlist, track }) => `\n\n다음 편집에 쓸 항목: 플레이리스트 ${playlist}, 곡 ${track}.`,
128
+ "memory.spotifyExcluded": "\n\nSpotify 결과는 일반 기억에 넣지 않습니다. 확인된 사용자 본인의 말만 기억할 수 있으며, 다른 내용은 저장하지 않았습니다.",
129
+ "memory.notSaved": "\n\n답변은 끝났지만 기억을 저장하지 못했습니다.",
130
+ "choice.like": ({ title, artist }) => { const name = title ? `${artist}의 "${title}"` : artist; return `\n\n저장했습니다: ${name}을(를) 좋아하신다고요. 바꾸고 싶으면 언제든 말씀해 주세요.`; },
131
+ "choice.avoid": ({ title, artist }) => { const name = title ? `${artist}의 "${title}"` : artist; return `\n\n저장했습니다: ${name}은(는) 재생하거나 추천하지 않겠습니다. 바꾸고 싶으면 언제든 말씀해 주세요.`; },
132
+ "choice.retract": ({ title, artist }) => { const name = title ? `${artist}의 "${title}"` : artist; return `\n\n취소했습니다: ${name}에 대한 이전 선택.`; },
133
+ "turn.cancelled": "남은 작업을 취소했습니다.",
134
+ "turn.interrupted": "답변이 중단되었습니다. 기록된 Spotify 결과는 아래와 같으며, 어떤 작업도 다시 실행하지 않았습니다.",
135
+ };
@@ -0,0 +1,135 @@
1
+ // Português do Brasil, com "você". Keys match messages/en.mjs; a missing key falls back to English.
2
+ export default {
3
+ "discovery.reason.listenbrainz": ({ adjacent, seed, release }) => `Os dados de escuta do ListenBrainz ligam ${adjacent} a ${seed}; do álbum "${release}".`,
4
+ "discovery.reason.appleMusic": ({ queries, release, genre }) => `${queries ? `Resultado do catálogo do Apple Music para ${queries}` : "Candidata do catálogo do Apple Music"}; do álbum "${release}"${genre ? `, gênero no catálogo: ${genre}` : ""}.`,
5
+ "discovery.reason.external": ({ release }) => `Do catálogo externo consultado, em "${release}".`,
6
+ "discovery.order": ({ path }) => `Ouça nesta ordem: ${path}.`,
7
+ "discovery.unavailable": "Não consegui acessar os serviços de descoberta de música, então este pedido não trouxe recomendações. Envie o pedido de novo para tentar outra vez.",
8
+
9
+ "citations.heading": "Fontes musicais públicas (separadas do seu histórico de escuta pessoal):",
10
+ "citations.item": ({ label, url, storefront, date }) => `- [${label}](<${url}>) - loja ${storefront}, consultada em ${date}.`,
11
+ "citations.scope": "Alcance: estas páginas públicas de catálogo não comprovam seu gosto pessoal, seu histórico completo nem a disponibilidade em outras plataformas.",
12
+ "web.sources.heading": ({ providers }) => `Fontes públicas da web (${providers})`,
13
+ "web.sources.headingUnnamed": "Fontes públicas da web",
14
+ "web.sources.published": ({ date }) => `data da fonte: ${date}`,
15
+ "web.sources.retrieved": ({ date }) => `consultado em ${date}`,
16
+
17
+ "external.neighbors": ({ seed }) => seed ? `ouvintes próximos de ${seed} no ListenBrainz` : "ouvintes próximos no ListenBrainz",
18
+ "external.listenbrainz": ({ neighbor, date }) => `${neighbor} (identidade do Wikidata, CC0, consultado em ${date}; isto não é semelhança de som)`,
19
+ "external.appleMusic": ({ date }) => `títulos correspondentes no Apple Music US (consultado em ${date})`,
20
+ "external.limit": "Só fica de fora o que coincide em título e artista com a sua biblioteca importada, então isso não quer dizer que você nunca ouviu essas músicas. São sugestões para ouvir.",
21
+ "external.from": ({ parts, limit }) => `A partir de ${parts.join(" e ")}. ${limit}`,
22
+
23
+ "failure.deviceList": ({ noMatch, names, code }) => `O nome do dispositivo ${noMatch ? "não corresponde a nenhum" : "corresponde a vários dispositivos"}. O Spotify mostra agora: ${names}. Diga "toca no" seguido do nome que aparece; a música escolhida fica guardada. Nenhum pedido de reprodução foi enviado. (${code})`,
24
+ "failure.networkRead": ({ notSent, code }) => `Um erro de rede impediu a leitura do estado dos dispositivos ou da reprodução do Spotify; a preparação parou.${notSent ? " O pedido de reprodução não foi enviado." : ""} Confira a conexão com o Spotify e tente de novo. (${code})`,
25
+ "failure.readiness.spotify_device_selection_required": "A música escolhida fica guardada, mas o dispositivo ainda não foi definido. Diga qual quer usar; o anterior não será usado no lugar.",
26
+ "failure.readiness.spotify_device_not_found": "O Spotify não vê o dispositivo escolhido. Abra o Spotify nele e peça a música de novo; nenhum outro dispositivo foi usado no lugar.",
27
+ "failure.readiness.spotify_device_ambiguous": "Pode haver vários dispositivos do Spotify disponíveis. Diga o nome do que você quer usar.",
28
+ "failure.readiness.spotify_device_restricted": "O dispositivo escolhido não confirma que pode ser controlado pela Web API do Spotify. Escolha outro dispositivo disponível.",
29
+ "failure.readiness.spotify_device_changed": "Outro dispositivo do Spotify ficou ativo. Escolha o dispositivo que você quer; a reprodução não foi movida automaticamente.",
30
+ "failure.readiness.spotify_device_state_unconfirmed": "O estado dos dispositivos ou da reprodução do Spotify está incompleto ou inconsistente. Confira o dispositivo atual no Spotify antes de tentar de novo.",
31
+ "failure.readiness.spotify_device_not_ready": "O dispositivo não ficou pronto dentro do tempo de espera. Abra o Spotify nele e peça a mesma música de novo.",
32
+ "failure.readiness.spotify_playback_restricted": "No momento, o Spotify não permite retomar a reprodução. Confira o dispositivo.",
33
+ "failure.songNotSent": " O pedido de reprodução não foi enviado.",
34
+ "failure.preparationStopped": ({ evidence }) => `As verificações do dispositivo não terminaram (${evidence}); nenhum pedido de reprodução foi enviado. Confira o estado do dispositivo no Spotify antes de tentar de novo.`,
35
+ "failure.queueRequestRequired": "Nenhuma música foi adicionada: não consegui ligar esta resposta a um pedido de fila. Para continuar, peça explicitamente para adicionar música à fila.",
36
+ "failure.notSent": ({ code }) => `Não enviei nada ao Spotify: não consegui relacionar isso com as músicas que encontrei (${code}). Peça de novo com o nome da música ou do artista, e eu procuro outra vez.`,
37
+ "failure.avoided": "Não enviei nada ao Spotify: você pediu para eu deixar esse artista ou essa música de fora. Me diga quando quiser mudar isso.",
38
+ "failure.outcome.lookup": "A busca no Spotify não terminou",
39
+ "failure.outcome.unknown": "Não se sabe ao certo o resultado da ação no Spotify",
40
+ "failure.outcome.unconfirmed": "O Spotify não confirmou a ação de reprodução",
41
+ "failure.reason": ({ reason }) => `O Spotify informou: ${reason}.`,
42
+ "failure.reasonUnknown": "A causa exata não foi confirmada.",
43
+ "failure.lookup": ({ outcome, evidence, reason }) => `${outcome} (${evidence}). ${reason} Nenhuma ação de reprodução ou de fila foi enviada.`,
44
+ "failure.general": ({ targets, outcome, evidence, reason, playbackNotSent, recovered }) =>
45
+ `${targets ? `Alvo: ${targets}.\n` : ""}${outcome} (${evidence}). ${reason}${playbackNotSent ? " O pedido de reprodução não foi enviado." : ""} ${recovered ? "Uma tentativa limitada de recuperar o dispositivo terminou; não houve nova tentativa." : "A ação não foi repetida."} Consulte o estado da reprodução e os dispositivos disponíveis para entender melhor.`,
46
+
47
+ "queue.unverified": ({ one }) => `O Spotify ${one ? "aceitou" : "aceitou todas"}, mas não consegui ler sua fila para conferir.`,
48
+ "queue.confirmedPlaying": ({ one }) => one ? "Confirmado: está tocando no Spotify." : "Confirmado no Spotify: a primeira está tocando e as outras estão na sua fila.",
49
+ "queue.confirmed": ({ count }) => count === 1 ? "Confirmado na sua fila do Spotify." : `Confirmadas na sua fila do Spotify: todas as ${count}.`,
50
+ "queue.partlyVisible": ({ accepted, confirmed, truncated }) => `O Spotify aceitou ${accepted}; agora aparecem ${confirmed} na sua fila${truncated ? " (o Spotify só mostra parte de uma fila longa)" : ""}.`,
51
+ "queue.noPlayback": "O Spotify não tem nenhuma música tocando para servir de ponto de partida; nada foi adicionado.",
52
+ "queue.noCandidates": "Depois do filtro, não sobrou nenhuma candidata; nada foi adicionado.",
53
+ "queue.requested": ({ requested, accepted, shortfall }) => `Você pediu ${requested}; o Spotify aceitou ${accepted}${shortfall ? `, faltaram ${shortfall}` : ""}.`,
54
+ "queue.similarSeed": ({ artist }) => `Artistas próximos de ${artist} segundo outros ouvintes:`,
55
+ "queue.cancelled": "Parei de adicionar depois do cancelamento; as que já foram aceitas continuam na fila.",
56
+ "queue.stopped": ({ count, title, artist }) => `Adicionei ${count} à fila do Spotify e parei em "${title}" - ${artist}.`,
57
+ "queue.queued": ({ count }) => `Adicionei ${count} à fila do Spotify:`,
58
+ "queue.queuedSome": ({ count, unmatched }) => `Adicionei ${count} de ${count + unmatched} à fila do Spotify:`,
59
+ "queue.outcomeUnknown": "O Spotify não confirmou se a música em que parei foi adicionada. Confira a fila antes de tentar de novo; não reenviei.",
60
+ "queue.started": ({ title, device, more }) => `Nada estava tocando, então coloquei "${title}"${device ? ` no ${device}` : ""}${more ? " e deixei as outras na fila" : ""}.`,
61
+ "queue.unmatchedHeading": "Não encontradas no Spotify:",
62
+ "queue.skippedDuplicates": ({ count }) => `Pulei ${count} músicas repetidas.`,
63
+ "queue.skippedUncertain": ({ count }) => `Pulei ${count} músicas cujo resultado anterior na fila não ficou claro, para não repetir; confira a fila do Spotify.`,
64
+ "queue.skippedAvoided": ({ count }) => `Pulei ${count} músicas que você marcou para evitar.`,
65
+ "plan.avoidedLeftOut": ({ titles }) => `Deixei de fora o que você pediu para evitar: ${titles}.`,
66
+ "queue.skippedKnown": ({ count }) => `Pulei ${count} músicas que já aparecem no seu histórico, como você pediu.`,
67
+ "queue.mix": ({ familiar = 0, returning = 0, heard = 0, known_artist: knownArtist = 0, new: fresh = 0 }) =>
68
+ `Pelo seu histórico: ${[familiar && `${familiar} que você conhece bem`, returning && `${returning} ${returning === 1 ? "que volta" : "que voltam"} depois de um tempo`, heard && `${heard} que você ouviu uma ou duas vezes`, knownArtist && `${knownArtist} ${knownArtist === 1 ? "nova de um artista" : "novas de artistas"} que você conhece`, fresh && `${fresh} ${fresh === 1 ? "nova" : "novas"} para você`].filter(Boolean).join(", ")}.`,
69
+ "queue.observationTruncated": "A checagem de repetidas só cobre a parte da fila que o Spotify mostra; o que não aparece ainda pode repetir alguma.",
70
+ "queue.notAddedHeading": "Não adicionadas:",
71
+ "queue.earlierReceipt": "Este é o resultado da fila anterior; este esclarecimento não adicionou as músicas de novo.",
72
+
73
+ "playlist.partialUnknown": ({ name }) => `O Spotify criou a playlist privada "${name}", mas não confirmou que as músicas foram adicionadas. Confira antes de decidir se tenta de novo.`,
74
+ "playlist.partialEmpty": ({ name }) => `O Spotify criou a playlist privada "${name}", mas não adicionou as músicas. A playlist vazia já existe; confira antes de decidir se tenta de novo.`,
75
+ "playlist.saved": ({ name, count }) => `Salva como a playlist privada do Spotify "${name}" (${count} músicas).`,
76
+
77
+ "action.playback.resume": "o pedido de reprodução",
78
+ "action.playback.pause": "a pausa",
79
+ "action.playback.next": "o pulo para a próxima",
80
+ "action.playback.previous": "a volta para a anterior",
81
+ "action.playback.volume.set": "a mudança de volume",
82
+ "action.playback.seek": "a mudança de posição",
83
+ "action.playback.shuffle.set": "o ajuste do modo aleatório",
84
+ "action.playback.repeat.set": "o ajuste de repetição",
85
+ "action.playback.transfer": "a troca de dispositivo",
86
+ "action.playback.queue.add": "a adição à fila",
87
+ "action.library.remove": "a remoção da sua biblioteca",
88
+ "action.library.save": "o salvamento na sua biblioteca",
89
+ "action.playlist.write": "a criação da playlist",
90
+ "action.playlist.edit": "a mudança na playlist",
91
+ "action.playlist.rename": "a mudança de nome da playlist",
92
+ "action.playlist.unfollow": "a remoção da playlist da sua biblioteca",
93
+ "action.playlist.remove_track": "a remoção de uma música da playlist",
94
+ "action.other": "a mudança pedida",
95
+ "write.unknown": ({ action, target }) => `O Spotify não confirmou ${action}${target ? ` para "${target}"` : ""}. Confira o estado antes de decidir se tenta de novo.`,
96
+ "write.unfollowed": ({ name }) => `Tirei "${name}" da sua biblioteca do Spotify (você deixou de seguir). A playlist pode continuar existindo para outros ouvintes; ela não foi apagada.`,
97
+ "write.renamed": ({ name }) => `O Spotify aceitou o novo nome da playlist: "${name}".`,
98
+ "write.trackRemoved": ({ name }) => `O Spotify aceitou tirar uma música de "${name}".`,
99
+ "write.accepted": ({ action, count, targets }) => `O Spotify aceitou ${action}${count === null ? "" : ` (${count} músicas)`}${targets ? `: ${targets}` : ""}.`,
100
+
101
+ "plan.heading.private_library": ({ count }) => `Plano de ${count} músicas da sua biblioteca:`,
102
+ "plan.heading.private_history": ({ count }) => `Plano de ${count} músicas do seu histórico para ouvir de novo:`,
103
+ "plan.heading.external_catalog": ({ count }) => `${count} músicas:`,
104
+ "plan.heading.mixed": ({ count }) => `${count} músicas:`,
105
+ "plan.heading.timeCapsule": ({ count }) => `Máquina do tempo de ${count} paradas pelos seus anos de escuta:`,
106
+ "plan.heading.historicalReturn": ({ count }) => `${count} músicas do seu histórico que voltaram depois de uma longa pausa:`,
107
+ "plan.heading.backToBack": ({ count }) => `${count} músicas do seu histórico que você ouviu em sequência:`,
108
+ "plan.externalTrack": ({ line, release }) => `${line}, do álbum "${release}"`,
109
+ "plan.rationale": ({ text }) => ` Por que está aqui: ${text}`,
110
+ "plan.ordering": ({ text }) => `Por que esta ordem: ${text}`,
111
+ "plan.notSaved": "Nada foi salvo nem adicionado à fila ainda.",
112
+ "plan.previewing": "As prévias de 30 segundos estão tocando no seu player; as músicas completas estão no Apple Music.",
113
+ "plan.pending": "Limite: este plano só está pendente nesta sessão, não foi gravado no Spotify e não tem efeitos externos.",
114
+ "plan.backToBackBoundary": "Como ler: duas reproduções guardadas em sequência não provam que você usou a repetição, que tocou de novo de propósito nem que gosta da música.",
115
+ "plan.validationScope": "O que foi conferido: o planejador local verifica a identidade de cada música, se ela está entre as candidatas, a quantidade e a ordem; os motivos e o encaixe com o momento continuam sendo julgamentos do modelo a partir dos metadados disponíveis.",
116
+ "plan.unvalidated": "Não consegui validar este plano, então não vou devolver uma lista que não foi conferida contra candidatas confiáveis. Refine ou amplie a busca e tente de novo.",
117
+
118
+ "edit.previewHeading": ({ name, before, after }) => `Prévia das mudanças na playlist privada do Spotify "${name}" (${before} -> ${after} músicas):`,
119
+ "edit.status.added": "adicionar",
120
+ "edit.status.moved": "mover",
121
+ "edit.status.retained": "manter",
122
+ "edit.removeHeading": "Remover:",
123
+ "edit.summary": ({ added, removed, moved, retained }) => `Resumo: ${added} adicionadas, ${removed} removidas, ${moved} movidas e ${retained} sem mudança.`,
124
+ "edit.boundary": "Limite: o Spotify não mudou. Confirme explicitamente na sua próxima mensagem para o Moondog gravar exatamente esta versão.",
125
+ "edit.updated": ({ name, before, after }) => `Atualizei a playlist privada do Spotify "${name}" com a prévia que você confirmou${before === null ? ` (agora tem ${after} músicas)` : ` (${before} -> ${after} músicas)`}.`,
126
+
127
+ "selection.note": ({ playlist, track }) => `\n\nSelecionado para as próximas edições: playlist ${playlist}; música ${track}.`,
128
+ "memory.spotifyExcluded": "\n\nOs resultados do Spotify não vão para a memória geral; só citações suas verificáveis podiam ser guardadas, e o resto não foi salvo.",
129
+ "memory.notSaved": "\n\nA resposta terminou, mas não foi possível salvar a memória.",
130
+ "choice.like": ({ title, artist }) => { const name = title ? `"${title}" de ${artist}` : artist; return `\n\nSalvo: você gosta de ${name}. É só me dizer quando quiser mudar.`; },
131
+ "choice.avoid": ({ title, artist }) => { const name = title ? `"${title}" de ${artist}` : artist; return `\n\nSalvo: vou deixar ${name} fora do que eu toco e sugiro. É só me dizer quando quiser mudar.`; },
132
+ "choice.retract": ({ title, artist }) => { const name = title ? `"${title}" de ${artist}` : artist; return `\n\nDesfeito: sua escolha anterior sobre ${name}.`; },
133
+ "turn.cancelled": "Cancelei o resto.",
134
+ "turn.interrupted": "A resposta foi interrompida. Abaixo estão os resultados registrados no Spotify; nenhuma ação foi repetida.",
135
+ };
@@ -0,0 +1,135 @@
1
+ // 繁體中文(台灣用語)。Keys match messages/en.mjs; a missing key falls back to English.
2
+ export default {
3
+ "discovery.reason.listenbrainz": ({ adjacent, seed, release }) => `ListenBrainz 收聽資料將 ${adjacent} 與 ${seed} 連在一起;選自《${release}》。`,
4
+ "discovery.reason.appleMusic": ({ queries, release, genre }) => `Apple Music 目錄${queries ? `符合搜尋詞 ${queries}` : "候選"};選自《${release}》${genre ? `,目錄曲風為 ${genre}` : ""}。`,
5
+ "discovery.reason.external": ({ release }) => `來自已檢索的外部目錄,選自《${release}》。`,
6
+ "discovery.order": ({ path }) => `依序試聽:${path}。`,
7
+ "discovery.unavailable": "音樂探索服務暫時無法連線,這次沒有產生推薦。請重新送出這個請求再試一次。",
8
+
9
+ "citations.heading": "公開音樂來源(與私人聆聽證據分開):",
10
+ "citations.item": ({ label, url, storefront, date }) => `- [${label}](<${url}>) - ${storefront} 商店,檢索於 ${date}。`,
11
+ "citations.scope": "範圍:這些公開目錄頁面無法證明個人偏好、完整聆聽紀錄或跨平台可用性。",
12
+ "web.sources.heading": ({ providers }) => `公開網頁來源(${providers})`,
13
+ "web.sources.headingUnnamed": "公開網頁來源",
14
+ "web.sources.published": ({ date }) => `來源日期:${date}`,
15
+ "web.sources.retrieved": ({ date }) => `檢索於 ${date}`,
16
+
17
+ "external.neighbors": ({ seed }) => seed ? `ListenBrainz 聽眾相鄰,從 ${seed} 出發` : "ListenBrainz 聽眾相鄰",
18
+ "external.listenbrainz": ({ neighbor, date }) => `${neighbor}(Wikidata 身分,CC0,檢索於 ${date};這不是音訊相似度)`,
19
+ "external.appleMusic": ({ date }) => `Apple Music 美國曲名比對(檢索於 ${date})`,
20
+ "external.limit": "唯一排除的是匯入曲庫裡同名同藝人的歌,所以這不代表你從沒聽過。這些是供試聽的建議。",
21
+ "external.from": ({ parts, limit }) => `來源:${parts.join(",以及")}。${limit}`,
22
+
23
+ "failure.deviceList": ({ noMatch, names, code }) => `裝置名稱${noMatch ? "沒有符合" : "符合多個裝置"}。Spotify 目前列出:${names}。請用顯示的名稱說「在這台裝置上播放」,選好的歌曲會保留。尚未送出歌曲播放請求。(${code})`,
24
+ "failure.networkRead": ({ notSent, code }) => `讀取 Spotify 裝置或播放狀態時發生網路錯誤,裝置準備已停止。${notSent ? "歌曲播放請求尚未送出。" : ""}請檢查與 Spotify 的連線後重試。(${code})`,
25
+ "failure.readiness.spotify_device_selection_required": "選好的歌曲仍保留,但目標裝置還沒確定。請說出要用的裝置名稱,不會改用之前的裝置。",
26
+ "failure.readiness.spotify_device_not_found": "Spotify 看不到目標裝置。請在那台裝置上開啟 Spotify,再請求播放;沒有改用其他裝置。",
27
+ "failure.readiness.spotify_device_ambiguous": "可能有多台 Spotify 裝置可用,請指定要用的裝置名稱。",
28
+ "failure.readiness.spotify_device_restricted": "目標裝置沒有確認允許 Spotify Web API 控制,請選擇其他可用的裝置。",
29
+ "failure.readiness.spotify_device_changed": "另一台 Spotify 裝置變成使用中。請明確選擇裝置,播放沒有自動轉移。",
30
+ "failure.readiness.spotify_device_state_unconfirmed": "Spotify 的裝置與播放狀態不完整或不一致。請先在 Spotify 確認目前的裝置再試。",
31
+ "failure.readiness.spotify_device_not_ready": "目標裝置在等待時間內沒有就緒。請在那台裝置上開啟 Spotify,再請求播放同一首歌。",
32
+ "failure.readiness.spotify_playback_restricted": "Spotify 目前不允許繼續播放,請檢查目標裝置。",
33
+ "failure.songNotSent": " 尚未送出歌曲播放請求。",
34
+ "failure.preparationStopped": ({ evidence }) => `播放前的裝置檢查沒有完成(${evidence}),沒有送出歌曲播放請求。請檢查 Spotify 裝置狀態後再試。`,
35
+ "failure.queueRequestRequired": "這次沒有加入歌曲:我沒辦法把這則回覆對應到加入佇列的請求。需要明確請我加入播放佇列,才能繼續。",
36
+ "failure.notSent": ({ code }) => `沒有向 Spotify 送出任何內容:我沒辦法把它對應到剛才找到的歌曲(${code})。請帶上歌名或歌手再說一次,我會重新搜尋。`,
37
+ "failure.avoided": "沒有向 Spotify 送出任何內容:你要我別再放這位歌手或這首歌。想改的話隨時告訴我。",
38
+ "failure.outcome.lookup": "Spotify 查詢沒有完成",
39
+ "failure.outcome.unknown": "Spotify 操作結果尚不確定",
40
+ "failure.outcome.unconfirmed": "Spotify 播放操作未獲確認",
41
+ "failure.reason": ({ reason }) => `Spotify 回報的原因:${reason}。`,
42
+ "failure.reasonUnknown": "具體原因尚未證實。",
43
+ "failure.lookup": ({ outcome, evidence, reason }) => `${outcome}(${evidence})。${reason}沒有送出播放或加入佇列操作。`,
44
+ "failure.general": ({ targets, outcome, evidence, reason, playbackNotSent, recovered }) =>
45
+ `${targets ? `目標:${targets}。\n` : ""}${outcome}(${evidence})。${reason}${playbackNotSent ? "歌曲播放請求尚未送出。" : ""}${recovered ? "已結束一次有限的裝置復原流程,不再重試。" : "沒有自動重試。"}可以讀取播放狀態和裝置清單繼續診斷。`,
46
+
47
+ "queue.unverified": () => "Spotify 已接受,但我沒辦法讀取你的佇列再確認一次。",
48
+ "queue.confirmedPlaying": ({ one }) => one ? "已在 Spotify 確認正在播放。" : "已在 Spotify 確認:第一首正在播放,其餘都在佇列裡。",
49
+ "queue.confirmed": ({ count }) => count === 1 ? "已在 Spotify 佇列裡確認。" : `已在 Spotify 佇列裡確認這 ${count} 首。`,
50
+ "queue.partlyVisible": ({ accepted, confirmed, truncated }) => `Spotify 接受了 ${accepted} 首,目前佇列裡看得到 ${confirmed} 首${truncated ? "(Spotify 只顯示長佇列的一部分)" : ""}。`,
51
+ "queue.noPlayback": "Spotify 目前沒有可當作起點的歌曲,沒有加入任何歌曲。",
52
+ "queue.noCandidates": "篩選後沒有可加入的候選,沒有加入任何歌曲。",
53
+ "queue.requested": ({ requested, accepted, shortfall }) => `請求 ${requested} 首;Spotify 已接受 ${accepted} 首${shortfall ? `,還差 ${shortfall} 首` : ""}。`,
54
+ "queue.similarSeed": ({ artist }) => `根據聽眾資料,與 ${artist} 相近的藝人:`,
55
+ "queue.cancelled": "已停止繼續加入佇列;已接受的歌曲仍在佇列裡。",
56
+ "queue.stopped": ({ count, title, artist }) => `已把 ${count} 首加入 Spotify 佇列,停在《${title}》- ${artist}。`,
57
+ "queue.queued": ({ count }) => `已把這 ${count} 首加入 Spotify 佇列:`,
58
+ "queue.queuedSome": ({ count, unmatched }) => `已把 ${count} 首加入 Spotify 佇列,另有 ${unmatched} 首對不上:`,
59
+ "queue.outcomeUnknown": "Spotify 沒有確認停下的那首歌是否已加入。請先檢查佇列再試;這次寫入沒有重送。",
60
+ "queue.started": ({ title, device, more }) => `剛才沒有在播放,所以我先在${device ? `「${device}」` : " Spotify "}上播放《${title}》${more ? ",其餘排在後面" : ""}。`,
61
+ "queue.unmatchedHeading": "在 Spotify 上對不上:",
62
+ "queue.skippedDuplicates": ({ count }) => `略過 ${count} 首重複曲目。`,
63
+ "queue.skippedUncertain": ({ count }) => `略過 ${count} 首先前加入結果不明的曲目,避免重複;請檢查 Spotify 佇列。`,
64
+ "queue.skippedAvoided": ({ count }) => `略過 ${count} 首你要求避開的曲目。`,
65
+ "plan.avoidedLeftOut": ({ titles }) => `依你的要求略過了:${titles}。`,
66
+ "queue.skippedKnown": ({ count }) => `依你的要求,略過 ${count} 首聆聽紀錄裡已有的曲目。`,
67
+ "queue.mix": ({ familiar = 0, returning = 0, heard = 0, known_artist: knownArtist = 0, new: fresh = 0 }) =>
68
+ `根據你的聆聽紀錄:${[familiar && `熟悉的 ${familiar} 首`, returning && `久違的 ${returning} 首`, heard && `聽過一兩次的 ${heard} 首`, knownArtist && `熟悉歌手的新歌 ${knownArtist} 首`, fresh && `沒聽過的 ${fresh} 首`].filter(Boolean).join(",")}。`,
69
+ "queue.observationTruncated": "只檢查了 Spotify 回傳的有限佇列片段,沒顯示的部分仍可能重複。",
70
+ "queue.notAddedHeading": "沒有加入:",
71
+ "queue.earlierReceipt": "這是上一次佇列操作的結果;這次說明沒有再次加入歌曲。",
72
+
73
+ "playlist.partialUnknown": ({ name }) => `Spotify 已建立私人播放清單「${name}」,但加入曲目的結果尚未確認。請先檢查再決定是否重試。`,
74
+ "playlist.partialEmpty": ({ name }) => `Spotify 已建立私人播放清單「${name}」,但沒有加入曲目;這個空的播放清單已經存在,請先檢查再決定是否重試。`,
75
+ "playlist.saved": ({ name, count }) => `已儲存為 Spotify 私人播放清單「${name}」(${count} 首)。`,
76
+
77
+ "action.playback.resume": "播放請求",
78
+ "action.playback.pause": "暫停請求",
79
+ "action.playback.next": "下一首請求",
80
+ "action.playback.previous": "上一首請求",
81
+ "action.playback.volume.set": "音量調整",
82
+ "action.playback.seek": "播放位置調整",
83
+ "action.playback.shuffle.set": "隨機播放設定",
84
+ "action.playback.repeat.set": "重複播放設定",
85
+ "action.playback.transfer": "裝置切換",
86
+ "action.playback.queue.add": "加入佇列",
87
+ "action.library.remove": "從音樂庫移除",
88
+ "action.library.save": "儲存到音樂庫",
89
+ "action.playlist.write": "建立播放清單",
90
+ "action.playlist.edit": "播放清單變更",
91
+ "action.playlist.rename": "播放清單重新命名",
92
+ "action.playlist.unfollow": "從音樂庫移除播放清單",
93
+ "action.playlist.remove_track": "移除播放清單曲目",
94
+ "action.other": "要求的變更",
95
+ "write.unknown": ({ action, target }) => `Spotify 的${action}${target ? `(「${target}」)` : ""}結果尚未確認。請先檢查狀態,再決定是否重試。`,
96
+ "write.unfollowed": ({ name }) => `已從你的 Spotify 音樂庫移除「${name}」(取消追蹤)。其他聽眾可能仍看得到這個播放清單;它沒有被永久刪除。`,
97
+ "write.renamed": ({ name }) => `Spotify 已接受把播放清單重新命名為「${name}」。`,
98
+ "write.trackRemoved": ({ name }) => `Spotify 已接受從「${name}」移除 1 首曲目。`,
99
+ "write.accepted": ({ action, count, targets }) => `Spotify 已接受${action}${count === null ? "" : `(${count} 首)`}${targets ? `:${targets}` : ""}。`,
100
+
101
+ "plan.heading.private_library": ({ count }) => `來自你曲庫的 ${count} 首方案:`,
102
+ "plan.heading.private_history": ({ count }) => `從你的聆聽紀錄挑出、值得再聽的 ${count} 首:`,
103
+ "plan.heading.external_catalog": ({ count }) => `這 ${count} 首:`,
104
+ "plan.heading.mixed": ({ count }) => `這 ${count} 首:`,
105
+ "plan.heading.timeCapsule": ({ count }) => `穿越你 ${count} 個聆聽年份的時光機:`,
106
+ "plan.heading.historicalReturn": ({ count }) => `${count} 首隔了很久又回來的歌:`,
107
+ "plan.heading.backToBack": ({ count }) => `${count} 首曾被你連著播放的歌:`,
108
+ "plan.externalTrack": ({ line, release }) => `${line},選自《${release}》`,
109
+ "plan.rationale": ({ text }) => ` 選曲理由:${text}`,
110
+ "plan.ordering": ({ text }) => `排序理由:${text}`,
111
+ "plan.notSaved": "還沒有儲存,也沒有加入佇列。",
112
+ "plan.previewing": "30 秒試聽正在播放器裡播放,完整歌曲在 Apple Music 上。",
113
+ "plan.pending": "說明:這個方案只在這次執行中等待確認,還沒寫入 Spotify,也沒有其他外部影響。",
114
+ "plan.backToBackBoundary": "說明:相鄰的播放紀錄不能證明當時開了重複播放、是刻意重播,或你喜歡這首歌。",
115
+ "plan.validationScope": "驗證範圍:本地規劃器驗證了曲目身分、候選範圍、數量和順序;選曲理由與情境適配仍是模型根據現有資訊的判斷。",
116
+ "plan.unvalidated": "我沒辦法驗證這份播放清單方案,所以不會回傳沒經過可信候選比對的曲目清單。請縮小或放寬搜尋範圍再試一次。",
117
+
118
+ "edit.previewHeading": ({ name, before, after }) => `Spotify 私人播放清單「${name}」編輯預覽(${before} -> ${after} 首):`,
119
+ "edit.status.added": "新增",
120
+ "edit.status.moved": "移動",
121
+ "edit.status.retained": "保留",
122
+ "edit.removeHeading": "將移除:",
123
+ "edit.summary": ({ added, removed, moved, retained }) => `變更摘要:新增 ${added},移除 ${removed},移動 ${moved},原位保留 ${retained}。`,
124
+ "edit.boundary": "說明:Spotify 還沒有變更。請在下一則訊息明確確認,Moondog 才會寫入這份保留下來的版本。",
125
+ "edit.updated": ({ name, before, after }) => `已依照確認過的預覽更新 Spotify 私人播放清單「${name}」${before === null ? `(現在 ${after} 首)` : `(${before} -> ${after} 首)`}。`,
126
+
127
+ "selection.note": ({ playlist, track }) => `\n\n接下來的編輯會用:播放清單 ${playlist};歌曲 ${track}。`,
128
+ "memory.spotifyExcluded": "\n\nSpotify 的結果不會進入一般記憶;只有可驗證的你的原話會被記住,其他內容沒有儲存。",
129
+ "memory.notSaved": "\n\n這則回覆已完成,但記憶沒有儲存成功。",
130
+ "choice.like": ({ title, artist }) => { const name = title ? `${artist}的《${title}》` : artist; return `\n\n已儲存:你喜歡${name}。想改的話隨時告訴我。`; },
131
+ "choice.avoid": ({ title, artist }) => { const name = title ? `${artist}的《${title}》` : artist; return `\n\n已儲存:我播放和推薦時都會避開${name}。想改的話隨時告訴我。`; },
132
+ "choice.retract": ({ title, artist }) => { const name = title ? `${artist}的《${title}》` : artist; return `\n\n已撤銷:你之前對${name}的選擇。`; },
133
+ "turn.cancelled": "已取消後續操作。",
134
+ "turn.interrupted": "回覆中斷了。已記錄的 Spotify 結果如下;沒有重新執行任何操作。",
135
+ };
@@ -0,0 +1,135 @@
1
+ // 简体中文。Keys match messages/en.mjs; a missing key falls back to English.
2
+ export default {
3
+ "discovery.reason.listenbrainz": ({ adjacent, seed, release }) => `ListenBrainz 收听数据将 ${adjacent} 与 ${seed} 关联;选自《${release}》。`,
4
+ "discovery.reason.appleMusic": ({ queries, release, genre }) => `Apple Music 目录${queries ? `匹配检索词 ${queries}` : "候选"};选自《${release}》${genre ? `,目录流派为 ${genre}` : ""}。`,
5
+ "discovery.reason.external": ({ release }) => `来自已检索的外部目录,选自《${release}》。`,
6
+ "discovery.order": ({ path }) => `依次试听:${path}。`,
7
+ "discovery.unavailable": "音乐发现服务暂时无法连接,这次没有生成推荐。请重新提交这条请求再试。",
8
+
9
+ "citations.heading": "公开音乐来源(与私人听歌证据分开):",
10
+ "citations.item": ({ label, url, storefront, date }) => `- [${label}](<${url}>) - ${storefront} storefront,检索于 ${date}。`,
11
+ "citations.scope": "范围:这些公开 catalog 页面不证明个人偏好、完整听歌历史或全平台可用性。",
12
+ "web.sources.heading": ({ providers }) => `公开网页来源(${providers})`,
13
+ "web.sources.headingUnnamed": "公开网页来源",
14
+ "web.sources.published": ({ date }) => `来源日期:${date}`,
15
+ "web.sources.retrieved": ({ date }) => `检索于 ${date}`,
16
+
17
+ "external.neighbors": ({ seed }) => seed ? `ListenBrainz 听友相邻,从 ${seed} 出发` : "ListenBrainz 听友相邻",
18
+ "external.listenbrainz": ({ neighbor, date }) => `${neighbor}(Wikidata 身份,CC0,检索于 ${date};这不是音频相似)`,
19
+ "external.appleMusic": ({ date }) => `Apple Music US 曲名匹配(检索于 ${date})`,
20
+ "external.limit": "导入曲库里的精确同名同艺人是唯一排除项,所以这不代表你从未听过。这些是供试听的建议。",
21
+ "external.from": ({ parts, limit }) => `来源:${parts.join(",以及")}。${limit}`,
22
+
23
+ "failure.deviceList": ({ noMatch, names, code }) => `设备名称${noMatch ? "没有匹配成功" : "匹配到多个设备"}。Spotify 当前可见:${names}。请用显示的名称说“在该设备上播放它”,所选歌曲会保留。尚未发送歌曲播放请求。 (${code})`,
24
+ "failure.networkRead": ({ notSent, code }) => `读取 Spotify 设备或播放状态时发生网络错误,设备准备已停止。${notSent ? "歌曲播放请求尚未发送。" : ""}请检查与 Spotify 的连接后重试。 (${code})`,
25
+ "failure.readiness.spotify_device_selection_required": "所选歌曲仍保留,但目标设备尚未确定。请明确指定设备名称,不会改用之前的设备。",
26
+ "failure.readiness.spotify_device_not_found": "Spotify 没有列出目标设备。请在该设备上打开 Spotify,再请求播放;没有改用其他设备。",
27
+ "failure.readiness.spotify_device_ambiguous": "有多个可能的 Spotify 设备,请指定要使用的设备名称。",
28
+ "failure.readiness.spotify_device_restricted": "目标设备未确认允许 Spotify Web API 控制,请选择其他可用设备。",
29
+ "failure.readiness.spotify_device_changed": "另一个 Spotify 设备已变为活跃状态。请明确选择设备,没有自动转移播放。",
30
+ "failure.readiness.spotify_device_state_unconfirmed": "Spotify 的设备与播放状态不完整或不一致。请检查 Spotify 中的当前设备后再试。",
31
+ "failure.readiness.spotify_device_not_ready": "目标设备尚未就绪,已停止有限等待。请在该设备上打开 Spotify,再请求播放同一首歌。",
32
+ "failure.readiness.spotify_playback_restricted": "Spotify 当前不允许恢复播放,请检查目标设备。",
33
+ "failure.songNotSent": " 尚未发送歌曲播放请求。",
34
+ "failure.preparationStopped": ({ evidence }) => `播放前的设备检查未完成(${evidence}),没有发送歌曲播放请求。请检查 Spotify 设备状态后再试。`,
35
+ "failure.queueRequestRequired": "这次没有加入歌曲:我没能把这条回复关联到入队请求。需要明确要加入播放队列,才能继续。",
36
+ "failure.notSent": ({ code }) => `没有向 Spotify 发送任何内容:我没能把它对应到刚才找到的歌曲(${code})。请带上歌名或歌手再说一次,我会重新搜索。`,
37
+ "failure.avoided": "没有向 Spotify 发送任何内容:你让我别再放这位歌手或这首歌。想改的话随时告诉我。",
38
+ "failure.outcome.lookup": "Spotify 查询未完成",
39
+ "failure.outcome.unknown": "Spotify 操作结果尚不确定",
40
+ "failure.outcome.unconfirmed": "Spotify 播放操作未获确认",
41
+ "failure.reason": ({ reason }) => `Spotify 返回的原因:${reason}。`,
42
+ "failure.reasonUnknown": "具体原因未获证实。",
43
+ "failure.lookup": ({ outcome, evidence, reason }) => `${outcome}(${evidence})。${reason}未发送播放或加入队列操作。`,
44
+ "failure.general": ({ targets, outcome, evidence, reason, playbackNotSent, recovered }) =>
45
+ `${targets ? `目标:${targets}。\n` : ""}${outcome}(${evidence})。${reason}${playbackNotSent ? "歌曲播放请求尚未发送。" : ""}${recovered ? "已结束一次有限的设备恢复流程,不再重试。" : "未自动重试。"}可以读取播放状态和设备列表继续诊断。`,
46
+
47
+ "queue.unverified": () => "Spotify 已接受,但我没能读取你的队列来再次确认。",
48
+ "queue.confirmedPlaying": ({ one }) => one ? "已在 Spotify 上确认正在播放。" : "已在 Spotify 上确认:第一首正在播放,其余都在队列里。",
49
+ "queue.confirmed": ({ count }) => count === 1 ? "已在 Spotify 队列里确认。" : `已在 Spotify 队列里确认这 ${count} 首。`,
50
+ "queue.partlyVisible": ({ accepted, confirmed, truncated }) => `Spotify 接受了 ${accepted} 首,目前队列里能看到 ${confirmed} 首${truncated ? "(Spotify 只显示长队列的一部分)" : ""}。`,
51
+ "queue.noPlayback": "Spotify 当前没有可用的歌曲播放信息,未加入任何歌曲。",
52
+ "queue.noCandidates": "筛选后没有可加入的候选,未加入任何歌曲。",
53
+ "queue.requested": ({ requested, accepted, shortfall }) => `请求 ${requested} 首;Spotify 已接受 ${accepted} 首${shortfall ? `,还差 ${shortfall} 首` : ""}。`,
54
+ "queue.similarSeed": ({ artist }) => `基于艺人 ${artist} 的听众相似性:`,
55
+ "queue.cancelled": "已取消继续加入队列;已接受的歌曲仍在队列中。",
56
+ "queue.stopped": ({ count, title, artist }) => `已把 ${count} 首加入 Spotify 队列,停在《${title}》- ${artist}。`,
57
+ "queue.queued": ({ count }) => `已把这 ${count} 首加入 Spotify 队列:`,
58
+ "queue.queuedSome": ({ count, unmatched }) => `已把 ${count} 首加入 Spotify 队列,另有 ${unmatched} 首对不上:`,
59
+ "queue.outcomeUnknown": "Spotify 未确认停止处这首歌是否已加入;请先检查队列,不要自动重试。",
60
+ "queue.started": ({ title, device, more }) => `刚才没有在播放,所以我先在${device ? `「${device}」` : " Spotify "}上播放《${title}》${more ? ",其余排在后面" : ""}。`,
61
+ "queue.unmatchedHeading": "Spotify 上对不上:",
62
+ "queue.skippedDuplicates": ({ count }) => `跳过 ${count} 首重复曲目。`,
63
+ "queue.skippedUncertain": ({ count }) => `跳过 ${count} 首此前入队结果不明的曲目,避免重复;请先检查 Spotify 队列。`,
64
+ "queue.skippedAvoided": ({ count }) => `跳过 ${count} 首 Avoid 曲目。`,
65
+ "plan.avoidedLeftOut": ({ titles }) => `按你的要求略过了:${titles}。`,
66
+ "queue.skippedKnown": ({ count }) => `按你的要求跳过 ${count} 首有保留听歌记录的曲目。`,
67
+ "queue.mix": ({ familiar = 0, returning = 0, heard = 0, known_artist: knownArtist = 0, new: fresh = 0 }) =>
68
+ `根据你的听歌记录:${[familiar && `熟悉的 ${familiar} 首`, returning && `久违的 ${returning} 首`, heard && `听过一两次的 ${heard} 首`, knownArtist && `熟悉歌手的新歌 ${knownArtist} 首`, fresh && `没听过的 ${fresh} 首`].filter(Boolean).join(",")}。`,
69
+ "queue.observationTruncated": "仅检查了 Spotify 返回的有限队列片段,无法保证未显示部分没有重复。",
70
+ "queue.notAddedHeading": "还没加入:",
71
+ "queue.earlierReceipt": "这是上次队列操作的结果;这次澄清没有再次添加歌曲。",
72
+
73
+ "playlist.partialUnknown": ({ name }) => `Spotify 已创建私有歌单「${name}」,但加入曲目的结果尚未确认。请检查歌单后再决定是否重试。`,
74
+ "playlist.partialEmpty": ({ name }) => `Spotify 已创建私有歌单「${name}」,但未能加入曲目;这个空歌单已经存在,请检查后再决定是否重试。`,
75
+ "playlist.saved": ({ name, count }) => `已保存为 Spotify 私有歌单「${name}」(${count} 首)。`,
76
+
77
+ "action.playback.resume": "播放请求",
78
+ "action.playback.pause": "暂停请求",
79
+ "action.playback.next": "下一首请求",
80
+ "action.playback.previous": "上一首请求",
81
+ "action.playback.volume.set": "音量调整",
82
+ "action.playback.seek": "播放位置调整",
83
+ "action.playback.shuffle.set": "随机播放设置",
84
+ "action.playback.repeat.set": "循环播放设置",
85
+ "action.playback.transfer": "设备切换请求",
86
+ "action.playback.queue.add": "加入队列请求",
87
+ "action.library.remove": "取消收藏请求",
88
+ "action.library.save": "曲目保存请求",
89
+ "action.playlist.write": "歌单创建请求",
90
+ "action.playlist.edit": "歌单更改请求",
91
+ "action.playlist.rename": "歌单重命名",
92
+ "action.playlist.unfollow": "歌单取消收藏",
93
+ "action.playlist.remove_track": "歌单曲目移除",
94
+ "action.other": "更改请求",
95
+ "write.unknown": ({ action, target }) => `Spotify 的${action}${target ? `(「${target}」)` : ""}结果尚未确认。请先检查,再决定是否重试。`,
96
+ "write.unfollowed": ({ name }) => `已从你的 Spotify 音乐库移除歌单「${name}」(取消收藏)。歌单可能仍对其他听众存在;这不是全局删除。`,
97
+ "write.renamed": ({ name }) => `Spotify 已接受歌单重命名为「${name}」。`,
98
+ "write.trackRemoved": ({ name }) => `Spotify 已接受从「${name}」移除 1 首曲目。`,
99
+ "write.accepted": ({ action, count, targets }) => `Spotify 已接受${action}${count === null ? "" : `(${count} 首)`}${targets ? `:${targets}` : ""}。`,
100
+
101
+ "plan.heading.private_library": ({ count }) => `来自个人曲库的 ${count} 首方案:`,
102
+ "plan.heading.private_history": ({ count }) => `值得再听一次的 ${count} 首历史重逢方案:`,
103
+ "plan.heading.external_catalog": ({ count }) => `这 ${count} 首:`,
104
+ "plan.heading.mixed": ({ count }) => `这 ${count} 首:`,
105
+ "plan.heading.timeCapsule": ({ count }) => `穿过 ${count} 个听歌年份的时间机器方案:`,
106
+ "plan.heading.historicalReturn": ({ count }) => `${count} 首曾在长久空档后重新出现的回归方案:`,
107
+ "plan.heading.backToBack": ({ count }) => `${count} 首曾被连续播放的历史片段方案:`,
108
+ "plan.externalTrack": ({ line, release }) => `${line},选自《${release}》`,
109
+ "plan.rationale": ({ text }) => ` 策展判断:${text}`,
110
+ "plan.ordering": ({ text }) => `排序逻辑:${text}`,
111
+ "plan.notSaved": "还没有保存,也没有加入队列。",
112
+ "plan.previewing": "30 秒试听正在播放器里播放,完整歌曲在 Apple Music 上。",
113
+ "plan.pending": "边界:此方案仅在当前进程内待确认,尚未写入 Spotify,也没有外部副作用。",
114
+ "plan.backToBackBoundary": "解释边界:相邻的保留播放事件不证明当时开启了循环、重播是有意的,或你喜欢这首歌。",
115
+ "plan.validationScope": "校验范围:本地 planner 校验了曲目身份、候选集归属、数量和输出顺序;策展理由与情境适配度仍是基于现有元数据的模型判断。",
116
+ "plan.unvalidated": "我无法验证这次 playlist plan,因此不会返回未经可信候选集校验的曲目列表。请缩小或扩大搜索范围后重试。",
117
+
118
+ "edit.previewHeading": ({ name, before, after }) => `Spotify 私有歌单「${name}」编辑预览(${before} -> ${after} 首):`,
119
+ "edit.status.added": "新增",
120
+ "edit.status.moved": "移动",
121
+ "edit.status.retained": "保留",
122
+ "edit.removeHeading": "将移除:",
123
+ "edit.summary": ({ added, removed, moved, retained }) => `变更摘要:新增 ${added},移除 ${removed},移动 ${moved},原位保留 ${retained}。`,
124
+ "edit.boundary": "边界:Spotify 尚未发生变化。请在下一条消息中明确确认,Moondog 才会写入这份宿主层保留的精确版本。",
125
+ "edit.updated": ({ name, before, after }) => `已按确认过的精确预览更新 Spotify 私有歌单「${name}」${before === null ? `(现为 ${after} 首)` : `(${before} -> ${after} 首)`}。`,
126
+
127
+ "selection.note": ({ playlist, track }) => `\n\n后续编辑选择:歌单 ${playlist};歌曲 ${track}。`,
128
+ "memory.spotifyExcluded": "\n\nSpotify 查询结果不进入通用记忆;仅保存可验证的用户原话,其他记忆未保存。",
129
+ "memory.notSaved": "\n\n这条回复已经完成,但记忆没有成功保存。",
130
+ "choice.like": ({ title, artist }) => { const name = title ? `${artist}的《${title}》` : artist; return `\n\n已保存:你喜欢${name}。想改的话随时告诉我。`; },
131
+ "choice.avoid": ({ title, artist }) => { const name = title ? `${artist}的《${title}》` : artist; return `\n\n已保存:我播放和推荐时都会避开${name}。想改的话随时告诉我。`; },
132
+ "choice.retract": ({ title, artist }) => { const name = title ? `${artist}的《${title}》` : artist; return `\n\n已撤销:你之前对${name}的选择。`; },
133
+ "turn.cancelled": "已取消后续操作。",
134
+ "turn.interrupted": "回复中断;已确认的 Spotify 操作结果如下。未重新执行任何操作。",
135
+ };