Skip to content

Commit 3b2afa9

Browse files
authored
feat: add Chinese lyrics romanization (supports Simplified and Traditional Chinese)
1 parent b8e2777 commit 3b2afa9

File tree

9 files changed

+51
-2
lines changed

9 files changed

+51
-2
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ dependencies {
206206

207207
implementation(libs.room.runtime)
208208
implementation(libs.kuromoji.ipadic)
209+
implementation(libs.tinypinyin)
209210
ksp(libs.room.compiler)
210211
implementation(libs.room.ktx)
211212

app/src/main/kotlin/com/metrolist/music/constants/PreferenceKeys.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ val LyricsClickKey = booleanPreferencesKey("lyricsClick")
299299
val LyricsScrollKey = booleanPreferencesKey("lyricsScrollKey")
300300
val LyricsRomanizeJapaneseKey = booleanPreferencesKey("lyricsRomanizeJapanese")
301301
val LyricsRomanizeKoreanKey = booleanPreferencesKey("lyricsRomanizeKorean")
302+
val LyricsRomanizeChineseKey = booleanPreferencesKey("lyricsRomanizeChinese")
302303
val LyricsRomanizeRussianKey = booleanPreferencesKey("lyricsRomanizeRussian")
303304
val LyricsRomanizeUkrainianKey = booleanPreferencesKey("lyricsRomanizeUkrainian")
304305
val LyricsRomanizeSerbianKey = booleanPreferencesKey("lyricsRomanizeSerbian")

app/src/main/kotlin/com/metrolist/music/lyrics/LyricsUtils.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package com.metrolist.music.lyrics
22

33
import android.text.format.DateUtils
44
import com.atilika.kuromoji.ipadic.Tokenizer
5+
import com.github.promeg.pinyinhelper.Pinyin
6+
import java.util.Locale
57
import kotlinx.coroutines.Dispatchers
68
import kotlinx.coroutines.withContext
79

@@ -458,7 +460,23 @@ object LyricsUtils {
458460
romajaBuilder.toString()
459461
}
460462

461-
463+
suspend fun romanizeChinese(text: String): String = withContext(Dispatchers.Default) {
464+
if (text.isEmpty()) return@withContext ""
465+
val builder = StringBuilder(text.length * 2)
466+
for (ch in text) {
467+
if (ch in '\u4E00'..'\u9FFF') {
468+
val py = Pinyin.toPinyin(ch).lowercase(Locale.getDefault())
469+
builder.append(py).append(' ')
470+
} else {
471+
builder.append(ch)
472+
}
473+
}
474+
// Remove whitespaces before ASCII and CJK punctuations
475+
builder.toString()
476+
.replace(Regex("\\s+([,.!?;:])"), "$1")
477+
.replace(Regex("\\s+([,。!?;:、()《》〈〉【】『』「」])"), "$1")
478+
.trim()
479+
}
462480

463481
suspend fun romanizeCyrillic(text: String): String? = withContext(Dispatchers.Default) {
464482
if (text.isEmpty()) return@withContext null

app/src/main/kotlin/com/metrolist/music/ui/component/Lyrics.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ import com.metrolist.music.constants.LyricsClickKey
100100
import com.metrolist.music.constants.LyricsRomanizeBelarusianKey
101101
import com.metrolist.music.constants.LyricsRomanizeBulgarianKey
102102
import com.metrolist.music.constants.LyricsRomanizeCyrillicByLineKey
103+
import com.metrolist.music.constants.LyricsRomanizeChineseKey
103104
import com.metrolist.music.constants.LyricsRomanizeJapaneseKey
104105
import com.metrolist.music.constants.LyricsRomanizeKoreanKey
105106
import com.metrolist.music.constants.LyricsRomanizeKyrgyzKey
@@ -128,6 +129,7 @@ import com.metrolist.music.lyrics.LyricsUtils.parseLyrics
128129
import com.metrolist.music.lyrics.LyricsUtils.romanizeCyrillic
129130
import com.metrolist.music.lyrics.LyricsUtils.romanizeJapanese
130131
import com.metrolist.music.lyrics.LyricsUtils.romanizeKorean
132+
import com.metrolist.music.lyrics.LyricsUtils.romanizeChinese
131133
import com.metrolist.music.ui.component.shimmer.ShimmerHost
132134
import com.metrolist.music.ui.component.shimmer.TextPlaceholder
133135
import com.metrolist.music.ui.screens.settings.DarkMode
@@ -173,6 +175,7 @@ fun Lyrics(
173175
val romanizeKyrgyzLyrics by rememberPreference(LyricsRomanizeKyrgyzKey, true)
174176
val romanizeMacedonianLyrics by rememberPreference(LyricsRomanizeMacedonianKey, true)
175177
val romanizeCyrillicByLine by rememberPreference(LyricsRomanizeCyrillicByLineKey, false)
178+
val romanizeChineseLyrics by rememberPreference(LyricsRomanizeChineseKey, true)
176179
val scope = rememberCoroutineScope()
177180

178181
val mediaMetadata by playerConnection.mediaMetadata.collectAsState()
@@ -262,6 +265,12 @@ fun Lyrics(
262265
}
263266
}
264267

268+
else if (romanizeChineseLyrics && isChinese(entry.text)) {
269+
scope.launch {
270+
newEntry.romanizedTextFlow.value = romanizeChinese(entry.text)
271+
}
272+
}
273+
265274
newEntry
266275
}.let {
267276
listOf(LyricsEntry.HEAD_LYRICS_ENTRY) + it
@@ -332,6 +341,12 @@ fun Lyrics(
332341
}
333342
}
334343

344+
else if (romanizeChineseLyrics && isChinese(line)) {
345+
scope.launch {
346+
newEntry.romanizedTextFlow.value = romanizeChinese(line)
347+
}
348+
}
349+
335350
newEntry
336351
}
337352
}
@@ -758,7 +773,8 @@ fun Lyrics(
758773
romanizeBulgarianLyrics ||
759774
romanizeBelarusianLyrics ||
760775
romanizeKyrgyzLyrics ||
761-
romanizeMacedonianLyrics)) {
776+
romanizeMacedonianLyrics ||
777+
romanizeChineseLyrics)) {
762778
// Show romanized text if available
763779
val romanizedText by item.romanizedTextFlow.collectAsState()
764780
romanizedText?.let { romanized ->

app/src/main/kotlin/com/metrolist/music/ui/screens/settings/RomanizationSettings.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ fun RomanizationSettings(
5656

5757
val (lyricsRomanizeJapanese, onLyricsRomanizeJapaneseChange) = rememberPreference(LyricsRomanizeJapaneseKey, defaultValue = true)
5858
val (lyricsRomanizeKorean, onLyricsRomanizeKoreanChange) = rememberPreference(LyricsRomanizeKoreanKey, defaultValue = true)
59+
val (lyricsRomanizeChinese, onLyricsRomanizeChineseChange) = rememberPreference(LyricsRomanizeChineseKey, defaultValue = true)
5960
val (lyricsRomanizeRussian, onLyricsRomanizeRussianChange) = rememberPreference(LyricsRomanizeRussianKey, defaultValue = true)
6061
val (lyricsRomanizeUkrainian, onLyricsRomanizeUkrainianChange) = rememberPreference(LyricsRomanizeUkrainianKey, defaultValue = true)
6162
val (lyricsRomanizeSerbian, onLyricsRomanizeSerbianChange) = rememberPreference(LyricsRomanizeSerbianKey, defaultValue = true)
@@ -87,6 +88,13 @@ fun RomanizationSettings(
8788
onCheckedChange = onLyricsRomanizeKoreanChange,
8889
)
8990

91+
SwitchPreference(
92+
title = { Text(stringResource(R.string.lyrics_romanize_chinese)) },
93+
icon = { Icon(painterResource(R.drawable.language), null) },
94+
checked = lyricsRomanizeChinese,
95+
onCheckedChange = onLyricsRomanizeChineseChange,
96+
)
97+
9098
PreferenceGroupTitle(title = stringResource(R.string.lyrics_romanization_cyrillic))
9199
SwitchPreference(
92100
title = { Text(stringResource(R.string.lyrics_romanize_russian)) },

app/src/main/res/values/metrolist_strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@
217217
<string name="romanization">Romanization</string>
218218
<string name="lyrics_romanize_japanese">Romanize Japanese lyrics</string>
219219
<string name="lyrics_romanize_korean">Romanize Korean lyrics</string>
220+
<string name="lyrics_romanize_chinese">Romanize Chinese lyrics</string>
220221
<string name="lyrics_romanize_russian">Romanize Russian lyrics</string>
221222
<string name="lyrics_romanize_ukrainian">Romanize Ukrainian lyrics</string>
222223
<string name="lyrics_romanize_belarusian">Romanize Belarusian lyrics</string>

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ buildscript {
88
google()
99
mavenCentral()
1010
maven { setUrl("https://jitpack.io") }
11+
maven { setUrl("https://maven.aliyun.com/repository/public") }
1112
}
1213
dependencies {
1314
classpath(libs.gradle)

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ timber = "5.0.1"
3535
materialKolor = "4.0.5"
3636
kuromojiIpadic = "0.9.0"
3737
extractor = "a194151"
38+
tinypinyin = "2.0.3"
3839

3940
[libraries]
4041
annotation = { module = "androidx.annotation:annotation", version.ref = "annotation" }
@@ -113,6 +114,7 @@ multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
113114
extractor = { module = "com.github.mostafaalagamy:MetrolistExtractor", version.ref = "extractor" }
114115

115116
kuromoji-ipadic = { module = "com.atilika.kuromoji:kuromoji-ipadic", version.ref = "kuromojiIpadic" }
117+
tinypinyin = { module = "com.github.promeG:tinypinyin", version.ref = "tinypinyin" }
116118

117119
[plugins]
118120
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ dependencyResolutionManagement {
77
google()
88
mavenCentral()
99
maven { setUrl("https://jitpack.io") }
10+
maven { setUrl("https://maven.aliyun.com/repository/public") }
1011
}
1112
}
1213

0 commit comments

Comments
 (0)