From f60d63a3737538731704ca57c68b30edc37a61fe Mon Sep 17 00:00:00 2001 From: Evans Mike Date: Sun, 28 Jun 2026 16:59:36 +0100 Subject: [PATCH 1/6] Add Chinese translations --- src/i18n/locales/yue-Hant-HK/translation.json | 91 +++++++++++++++++++ src/i18n/locales/zh-Hans/translation.json | 91 +++++++++++++++++++ src/i18n/locales/zh-Hant/translation.json | 91 +++++++++++++++++++ 3 files changed, 273 insertions(+) create mode 100644 src/i18n/locales/yue-Hant-HK/translation.json create mode 100644 src/i18n/locales/zh-Hans/translation.json create mode 100644 src/i18n/locales/zh-Hant/translation.json diff --git a/src/i18n/locales/yue-Hant-HK/translation.json b/src/i18n/locales/yue-Hant-HK/translation.json new file mode 100644 index 0000000..ec8f94a --- /dev/null +++ b/src/i18n/locales/yue-Hant-HK/translation.json @@ -0,0 +1,91 @@ +{ + "tagline": "匯出你嘅 Spotify 播放清單。", + "get_started": "開始使用", + "subtitle": "第 {{min}}-{{max}} 個,共 {{total}} 個播放清單({{userId}})", + "subtitle_search": "{{total}} 個播放清單名稱包含「{{query}}」嘅結果", + "subtitle_search_advanced": "進階查詢「{{query}}」嘅 {{total}} 個結果", + "search": "搜尋", + "export_all": "冚唪唥匯出", + "exporting_done": "搞掂!", + "exporting_playlist": "匯出緊 {{playlistName}}...", + "export_search_results": "匯出結果", + "top_menu": { + "help": "說明", + "toggle_dark_mode": "轉深色Mode", + "change_language": "轉語言", + "change_user": "轉用戶" + }, + "config": { + "include_artists_data": "包含藝人資料", + "include_audio_features_data": "包含音訊特徵資料", + "include_album_data": "包含專輯資料" + }, + "help": { + "title": "快速參考", + "search_syntax": { + "title": "進階搜尋語法", + "query": "查詢", + "behavior": "行為", + "public_true": "淨係顯示公開播放清單", + "public_false": "淨係顯示私人播放清單", + "collaborative_true": "淨係顯示一齊編輯嘅播放清單", + "collaborative_false": "唔顯示一齊編輯嘅播放清單", + "owner_me": "淨係顯示我擁有嘅播放清單", + "owner_owner": "淨係顯示 [owner] 擁有嘅播放清單", + "more_detail": "想睇更多詳情,請參閱完整項目文件。" + } + }, + "playlist": { + "name": "名", + "owner": "Owner", + "tracks": "歌", + "public": "公開?", + "collaborative": "一齊編輯?", + "not_supported": "唔支援呢個播放清單", + "export": "匯出" + }, + "track": { + "track_uri": "歌 Link", + "track_name": "歌名", + "artist_uris": "Artist Link", + "artist_names": "Artist 名", + "album_uri": "專輯 Link", + "album_name": "專輯名", + "album_artist_uris": "專輯 Artist Link", + "album_artist_names": "專輯 Artist 名", + "album_release_date": "專輯發行日", + "album_image_url": "專輯封面條 Link", + "disc_number": "碟片冧把", + "track_number": "曲目冧把", + "track_duration": "曲目時長(毫秒)", + "track_preview_url": "曲目試聽 Link", + "explicit": "家長指引", + "popularity": "熱門度", + "isrc": "ISRC", + "is_playable": "播得到", + "added_by": "加入者", + "added_at": "加入時間", + "album": { + "album_genres": "專輯曲風", + "label": "唱片公司", + "copyrights": "版權" + }, + "artist": { + "artist_genres": "藝人曲風" + }, + "audio_features": { + "danceability": "舞蹈性", + "energy": "能量", + "key": "調性", + "loudness": "響度", + "mode": "調式", + "speechiness": "語音度", + "acousticness": "原音性", + "instrumentalness": "器樂度", + "liveness": "現場感", + "valence": "情緒效價", + "tempo": "速度", + "time_signature": "拍號" + } + } +} diff --git a/src/i18n/locales/zh-Hans/translation.json b/src/i18n/locales/zh-Hans/translation.json new file mode 100644 index 0000000..b6cabc7 --- /dev/null +++ b/src/i18n/locales/zh-Hans/translation.json @@ -0,0 +1,91 @@ +{ + "tagline": "导出你的 Spotify 播放列表。", + "get_started": "开始使用", + "subtitle": "第 {{min}}-{{max}} 个,共 {{total}} 个播放列表({{userId}})", + "subtitle_search": "{{total}} 个播放列表名称包含“{{query}}”的结果", + "subtitle_search_advanced": "高级查询“{{query}}”的 {{total}} 个结果", + "search": "搜索", + "export_all": "全部导出", + "exporting_done": "完成!", + "exporting_playlist": "正在导出 {{playlistName}}...", + "export_search_results": "导出结果", + "top_menu": { + "help": "帮助", + "toggle_dark_mode": "切换深色模式", + "change_language": "更改语言", + "change_user": "更换用户" + }, + "config": { + "include_artists_data": "包含艺人数据", + "include_audio_features_data": "包含音频特征数据", + "include_album_data": "包含专辑数据" + }, + "help": { + "title": "快速参考", + "search_syntax": { + "title": "高级搜索语法", + "query": "查询", + "behavior": "行为", + "public_true": "仅显示公开播放列表", + "public_false": "仅显示私密播放列表", + "collaborative_true": "仅显示协作播放列表", + "collaborative_false": "不显示协作播放列表", + "owner_me": "仅显示我拥有的播放列表", + "owner_owner": "仅显示 [owner] 拥有的播放列表", + "more_detail": "更多详情请参阅完整项目文档。" + } + }, + "playlist": { + "name": "名称", + "owner": "所有者", + "tracks": "曲目", + "public": "公开?", + "collaborative": "协作?", + "not_supported": "不支持此播放列表", + "export": "导出" + }, + "track": { + "track_uri": "曲目 URI", + "track_name": "曲目名称", + "artist_uris": "艺人 URI", + "artist_names": "艺人名称", + "album_uri": "专辑 URI", + "album_name": "专辑名称", + "album_artist_uris": "专辑艺人 URI", + "album_artist_names": "专辑艺人名称", + "album_release_date": "专辑发行日期", + "album_image_url": "专辑封面 URL", + "disc_number": "光盘编号", + "track_number": "曲目编号", + "track_duration": "曲目时长(毫秒)", + "track_preview_url": "曲目试听 URL", + "explicit": "含露骨内容", + "popularity": "热度", + "isrc": "ISRC", + "is_playable": "可播放", + "added_by": "添加者", + "added_at": "添加时间", + "album": { + "album_genres": "专辑流派", + "label": "唱片公司", + "copyrights": "版权" + }, + "artist": { + "artist_genres": "艺人流派" + }, + "audio_features": { + "danceability": "舞蹈性", + "energy": "能量", + "key": "调性", + "loudness": "响度", + "mode": "调式", + "speechiness": "语音度", + "acousticness": "原声性", + "instrumentalness": "器乐度", + "liveness": "现场感", + "valence": "情绪效价", + "tempo": "速度", + "time_signature": "拍号" + } + } +} diff --git a/src/i18n/locales/zh-Hant/translation.json b/src/i18n/locales/zh-Hant/translation.json new file mode 100644 index 0000000..658d566 --- /dev/null +++ b/src/i18n/locales/zh-Hant/translation.json @@ -0,0 +1,91 @@ +{ + "tagline": "匯出你的 Spotify 播放清單。", + "get_started": "開始使用", + "subtitle": "第 {{min}}-{{max}} 個,共 {{total}} 個播放清單({{userId}})", + "subtitle_search": "{{total}} 筆播放清單名稱包含「{{query}}」的結果", + "subtitle_search_advanced": "進階查詢「{{query}}」的 {{total}} 筆結果", + "search": "搜尋", + "export_all": "全部匯出", + "exporting_done": "完成!", + "exporting_playlist": "正在匯出 {{playlistName}}...", + "export_search_results": "匯出結果", + "top_menu": { + "help": "說明", + "toggle_dark_mode": "切換深色模式", + "change_language": "變更語言", + "change_user": "切換使用者" + }, + "config": { + "include_artists_data": "包含藝人資料", + "include_audio_features_data": "包含音訊特徵資料", + "include_album_data": "包含專輯資料" + }, + "help": { + "title": "快速參考", + "search_syntax": { + "title": "進階搜尋語法", + "query": "查詢", + "behavior": "行為", + "public_true": "只顯示公開播放清單", + "public_false": "只顯示私人播放清單", + "collaborative_true": "只顯示共同編輯播放清單", + "collaborative_false": "不顯示共同編輯播放清單", + "owner_me": "只顯示我擁有的播放清單", + "owner_owner": "只顯示 [owner] 擁有的播放清單", + "more_detail": "更多詳情請參閱完整專案文件。" + } + }, + "playlist": { + "name": "名稱", + "owner": "擁有者", + "tracks": "曲目", + "public": "公開?", + "collaborative": "共同編輯?", + "not_supported": "不支援此播放清單", + "export": "匯出" + }, + "track": { + "track_uri": "曲目 URI", + "track_name": "曲目名稱", + "artist_uris": "藝人 URI", + "artist_names": "藝人名稱", + "album_uri": "專輯 URI", + "album_name": "專輯名稱", + "album_artist_uris": "專輯藝人 URI", + "album_artist_names": "專輯藝人名稱", + "album_release_date": "專輯發行日期", + "album_image_url": "專輯封面 URL", + "disc_number": "光碟編號", + "track_number": "曲目編號", + "track_duration": "曲目長度(毫秒)", + "track_preview_url": "曲目試聽 URL", + "explicit": "含露骨內容", + "popularity": "熱門度", + "isrc": "ISRC", + "is_playable": "可播放", + "added_by": "新增者", + "added_at": "新增時間", + "album": { + "album_genres": "專輯曲風", + "label": "唱片公司", + "copyrights": "著作權" + }, + "artist": { + "artist_genres": "藝人曲風" + }, + "audio_features": { + "danceability": "舞蹈性", + "energy": "能量", + "key": "調性", + "loudness": "響度", + "mode": "調式", + "speechiness": "語音度", + "acousticness": "原音性", + "instrumentalness": "器樂度", + "liveness": "現場感", + "valence": "情緒效價", + "tempo": "速度", + "time_signature": "拍號" + } + } +} From 3c844216b721981daaab2d2b7a75a024df5f3a90 Mon Sep 17 00:00:00 2001 From: Evans Mike Date: Sun, 28 Jun 2026 17:00:17 +0100 Subject: [PATCH 2/6] Add Chinese locale configuration --- src/i18n/config.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 1f2d094..332340e 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -2,11 +2,14 @@ import i18n from "i18next" import { initReactI18next } from "react-i18next" import LanguageDetector from "i18next-browser-languagedetector" +const supportedLanguages = ['de', 'en', 'el', 'es', 'fr', 'it', 'nl', 'pt', 'sv', 'ar', 'ja', 'tr', 'zh-Hans', 'zh-Hant', 'yue-Hant-HK'] as const + i18n .use(initReactI18next) .use(LanguageDetector) .init({ fallbackLng: "en", + supportedLngs: supportedLanguages, interpolation: { escapeValue: false, }, @@ -46,6 +49,15 @@ i18n }, tr: { translations: require('./locales/tr/translation.json') + }, + 'zh-Hans': { + translations: require('./locales/zh-Hans/translation.json') + }, + 'zh-Hant': { + translations: require('./locales/zh-Hant/translation.json') + }, + 'yue-Hant-HK': { + translations: require('./locales/yue-Hant-HK/translation.json') } }, ns: ['translations'], From 4ec2525c275a1c53ed1cda8035c1464dda8062a7 Mon Sep 17 00:00:00 2001 From: Evans Mike Date: Sun, 28 Jun 2026 17:00:33 +0100 Subject: [PATCH 3/6] Add Chinese language menu options --- src/components/TopMenu.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/TopMenu.tsx b/src/components/TopMenu.tsx index 0b67850..1dbe228 100644 --- a/src/components/TopMenu.tsx +++ b/src/components/TopMenu.tsx @@ -139,6 +139,9 @@ class TopMenu extends React.Component { {this.renderLanguageDropdownItem("ar", "العربية")} {this.renderLanguageDropdownItem("ja", "日本語")} {this.renderLanguageDropdownItem("tr", "Türkçe")} + {this.renderLanguageDropdownItem("zh-Hans", "简体中文")} + {this.renderLanguageDropdownItem("zh-Hant", "正體中文")} + {this.renderLanguageDropdownItem("yue-Hant-HK", "粵語(香港)")} {logoutButton} From 15369af5527ea8df9c987238b6b6bca6c6cb5cd1 Mon Sep 17 00:00:00 2001 From: Evans Mike Date: Sun, 28 Jun 2026 17:00:58 +0100 Subject: [PATCH 4/6] Add language detection options --- src/i18n/config.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 332340e..8f6b22f 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -10,6 +10,12 @@ i18n .init({ fallbackLng: "en", supportedLngs: supportedLanguages, + detection: { + order: ['localStorage', 'sessionStorage', 'cookie', 'navigator', 'htmlTag'], + lookupLocalStorage: 'i18nextLng', + lookupSessionStorage: 'i18nextLng', + lookupCookie: 'i18nextLng', + }, interpolation: { escapeValue: false, }, From 27624eeafea279ec6d674c9de631f17799f649c7 Mon Sep 17 00:00:00 2001 From: Evans Mike Date: Sun, 28 Jun 2026 17:01:24 +0100 Subject: [PATCH 5/6] Add Chinese fallback languages --- src/i18n/config.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 8f6b22f..8ddddea 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -8,7 +8,17 @@ i18n .use(initReactI18next) .use(LanguageDetector) .init({ - fallbackLng: "en", + fallbackLng: { + 'zh-CN': ['zh-Hans'], + 'zh-SG': ['zh-Hans'], + 'zh-Hans': ['zh-Hans'], + 'zh-TW': ['zh-Hant'], + 'zh-MO': ['zh-Hant'], + 'zh-Hant': ['zh-Hant'], + 'zh-HK': ['yue-Hant-HK'], + 'yue-Hant-HK': ['yue-Hant-HK'], + default: ['en'] + }, supportedLngs: supportedLanguages, detection: { order: ['localStorage', 'sessionStorage', 'cookie', 'navigator', 'htmlTag'], From cecf4a5b5bbc17e6d56958f81f6e27fa40eeeee8 Mon Sep 17 00:00:00 2001 From: Evans Mike Date: Sun, 28 Jun 2026 17:01:47 +0100 Subject: [PATCH 6/6] Normalize detected Chinese languages --- src/i18n/config.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/i18n/config.ts b/src/i18n/config.ts index 8ddddea..0539c16 100644 --- a/src/i18n/config.ts +++ b/src/i18n/config.ts @@ -4,6 +4,28 @@ import LanguageDetector from "i18next-browser-languagedetector" const supportedLanguages = ['de', 'en', 'el', 'es', 'fr', 'it', 'nl', 'pt', 'sv', 'ar', 'ja', 'tr', 'zh-Hans', 'zh-Hant', 'yue-Hant-HK'] as const +const normalizeDetectedLanguage = (language: string): string => { + switch (language.toLowerCase()) { + case 'zh-cn': + case 'zh-sg': + case 'zh-hans': + return 'zh-Hans' + case 'zh-tw': + case 'zh-mo': + case 'zh-hant': + return 'zh-Hant' + case 'zh-hk': + case 'yue': + case 'yue-hk': + case 'yue-hant-hk': + return 'yue-Hant-HK' + case 'zh': + return 'en' + default: + return language + } +} + i18n .use(initReactI18next) .use(LanguageDetector) @@ -25,6 +47,7 @@ i18n lookupLocalStorage: 'i18nextLng', lookupSessionStorage: 'i18nextLng', lookupCookie: 'i18nextLng', + convertDetectedLanguage: normalizeDetectedLanguage, }, interpolation: { escapeValue: false,