Skip to content

Commit

Permalink
Implement PlatformLocale and StringDelegate for k/wasm (#561) (#625)
Browse files Browse the repository at this point in the history
* Implement PlatformLocale and StringDelegate for k/wasm (#561)

* Workaround `isOnUiThread` (ui-test) for k/wasm browser target

* Implement PlatformLocale  and StringDelegate for k/wasm

* Get rid of expect/actuals for jsWasmMain and use js(...) for a common implementation

* use the list of preferred languages - `window.navigator.languages`

* delete unused fun `currentLanguageTag`

* Add a comment
  • Loading branch information
eymar authored Jul 10, 2023
1 parent aba6dd3 commit e92f3b2
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package androidx.compose.ui.text.intl

internal actual fun userPreferredLanguages(): List<String> {
return getUserPreferredLanguagesAsArray().toList()
}

@Suppress("UnsafeCastFromDynamic")
private fun getUserPreferredLanguagesAsArray(): Array<String> =
js("window.navigator.languages")
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,54 @@

package androidx.compose.ui.text.intl

internal class JsLocale(val locale: dynamic) : PlatformLocale {
internal class JsLocale(val locale: IntlLocale) : PlatformLocale {

constructor(languageTag: String): this(languageTag.toIntlLocale())

override val language: String
get() = locale.languageCode!!
get() = locale.language

override val script: String
get() = locale.scriptCode!!
get() = locale.script ?: ""

override val region: String
get() = locale.countryCode!!
get() = locale.region ?: ""

override fun toLanguageTag(): String = TODO("implement native toLanguageTag") // locale.toLanguageTag()
override fun toLanguageTag(): String = locale.baseName
}

internal actual fun createPlatformLocaleDelegate(): PlatformLocaleDelegate =
object : PlatformLocaleDelegate {
override val current: LocaleList
get() = LocaleList(listOf(Locale(JsLocale(Any()))))
get() = LocaleList(
userPreferredLanguages().map {
Locale(JsLocale(it))
}
)


override fun parseLanguageTag(languageTag: String): PlatformLocale {
return JsLocale(Any())
return JsLocale(languageTag)
}
}

internal actual fun PlatformLocale.isRtl(): Boolean = false // TODO
// The list of RTL languages is taken from https://github.com/openjdk/jdk/blob/master/src/java.desktop/share/classes/java/awt/ComponentOrientation.java#L156
private val rtlLanguagesSet = setOf("ar", "fa", "he", "iw", "ji", "ur", "yi")

// Implemented according to ComponentOrientation.getOrientation (AWT),
// since there is no js API for this.
internal actual fun PlatformLocale.isRtl(): Boolean = this.language in rtlLanguagesSet

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale
internal external interface IntlLocale {
val language: String
val script: String?
val region: String?
val baseName: String
}

internal fun parseLanguageTagToIntlLocale(languageTag: String): IntlLocale = js("new Intl.Locale(languageTag)")

internal expect fun userPreferredLanguages(): List<String>

private fun String.toIntlLocale(): IntlLocale = parseLanguageTagToIntlLocale(this)
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,46 @@ import androidx.compose.ui.text.intl.PlatformLocale
/**
* A JS implementation of StringDelegate
*/

internal class JsStringDelegate : PlatformStringDelegate {
override fun toUpperCase(string: String, locale: PlatformLocale): String =
string.toUpperCase()
toLocaleUpperCase(string, locale.language)

override fun toLowerCase(string: String, locale: PlatformLocale): String =
string.toLowerCase()
toLocaleLowerCase(string, locale.language)

override fun capitalize(string: String, locale: PlatformLocale): String {
return string.replaceFirstChar {
if (it.isLowerCase()) {
it.titlecaseImpl(locale)
} else {
it.toString()
}
}
}

override fun capitalize(string: String, locale: PlatformLocale): String =
string.capitalize()
// Copy-pasted from kotlin _OneToManyTitlecaseMappings.kt (internal in stdlib, but doesn't take locale into account)
private fun Char.titlecaseImpl(locale: PlatformLocale): String {
val uppercase = toLocaleUpperCase(this.toString(), locale.language)
if (uppercase.length > 1) {
return if (this == '\u0149') uppercase else uppercase[0] + uppercase.substring(1).lowercase()
}
return titlecaseChar().toString()
}

override fun decapitalize(string: String, locale: PlatformLocale): String =
string.decapitalize()
override fun decapitalize(string: String, locale: PlatformLocale): String {
return string.replaceFirstChar {
toLocaleLowerCase(it.toString(), locale.language)
}
}
}

internal actual fun ActualStringDelegate(): PlatformStringDelegate =
// TODO("implement JsStringDelegate")
JsStringDelegate()

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLocaleUpperCase
internal fun toLocaleUpperCase(text: String, locale: String): String =
js("text.toLocaleUpperCase(locale)")

//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLocaleLowerCase
internal fun toLocaleLowerCase(text: String, locale: String): String =
js("text.toLocaleLowerCase(locale)")
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package androidx.compose.ui.text.intl

internal actual fun userPreferredLanguages(): List<String> {
val jsStringArray = getUserPreferredLanguagesAsArray()
return buildList<String> {
repeat(jsStringArray.length) {
add(jsStringArray[it].toString())
}
}
}

private fun getUserPreferredLanguagesAsArray(): JsArray<JsString> =
js("window.navigator.languages")

0 comments on commit e92f3b2

Please sign in to comment.