Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions core/src/androidMain/kotlin/io/tolgee/TolgeeAndroid.kt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ data class TolgeeAndroid internal constructor(
} ?: context.getString(id)
}

fun t(resources: Resources, @StringRes id: Int): String {
return getKeyFromResources(resources, id)?.let { key ->
t(key)
} ?: resources.getString(id)
}

/**
* Provides an immediate translation for a given string resource ID with optional format arguments.
* If the translation key is found in the string resource, it retrieves the translation; otherwise,
Expand All @@ -121,6 +127,12 @@ data class TolgeeAndroid internal constructor(
} ?: context.getString(id, *formatArgs)
}

fun t(resources: Resources, @StringRes id: Int, vararg formatArgs: Any): String {
return getKeyFromResources(resources, id)?.let { key ->
t(key, TolgeeMessageParams.Indexed(*formatArgs))
} ?: resources.getString(id, *formatArgs)
}

fun tPlural(resources: Resources, @PluralsRes id: Int, quantity: Int): String {
return getKeyFromResources(resources, id)?.let { key ->
t(key, TolgeeMessageParams.Indexed(quantity))
Expand Down Expand Up @@ -159,6 +171,32 @@ data class TolgeeAndroid internal constructor(
} ?: context.getText(id)
}

fun tStyled(resources: Resources, @StringRes id: Int): CharSequence {
return getKeyFromResources(resources, id)?.let { key ->
t(key)
} ?: resources.getText(id)
}

fun tStyled(resources: Resources, @StringRes id: Int, def: CharSequence?): CharSequence? {
return id.takeUnless { it == 0 }?.let {
getKeyFromResources(resources, it)?.let { key ->
t(key)
}
} ?: resources.getText(id, def)
}

fun tPluralStyled(resources: Resources, @PluralsRes id: Int, quantity: Int): CharSequence {
return getKeyFromResources(resources, id)?.let { key ->
t(key, TolgeeMessageParams.Indexed(quantity))
} ?: resources.getQuantityText(id, quantity)
}

fun tArrayStyled(resources: Resources, @ArrayRes id: Int): Array<out CharSequence> {
return getKeyFromResources(resources, id)?.let { key ->
tArray(key).toTypedArray()
} ?: resources.getTextArray(id)
}

/**
* Preloads the required languages and their translations for the current Tolgee instance.
*
Expand Down
20 changes: 8 additions & 12 deletions core/src/androidMain/kotlin/io/tolgee/TolgeeContextWrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,18 @@ class TolgeeContextWrapper(
val tolgee: Tolgee
) : ContextWrapper(base) {

private val res = atomic<Resources?>(null)
private var baseRes by atomic<Resources?>(null)
private var res by atomic<Resources?>(baseRes)

override fun getResources(): Resources? {
if (res.value == null) {
val superResources: Resources? = super.getResources()

if (superResources != null) {
res.compareAndSet(null, TolgeeResources(
baseContext = base,
base = superResources,
tolgee = tolgee
))
}
val base = super.getResources() ?: return res

if (res == null || baseRes != base) {
res = TolgeeResources(base, tolgee)
baseRes = base
}
return res.value ?: super.getResources()

return res ?: base
}

companion object {
Expand Down
271 changes: 257 additions & 14 deletions core/src/androidMain/kotlin/io/tolgee/TolgeeResources.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
package io.tolgee

import android.content.Context
import android.annotation.SuppressLint
import android.content.res.AssetFileDescriptor
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.content.res.Resources
import android.content.res.TypedArray
import android.content.res.XmlResourceParser
import android.content.res.loader.ResourcesLoader
import android.graphics.Movie
import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.util.TypedValue
import androidx.annotation.AnimRes
import androidx.annotation.AnimatorRes
import androidx.annotation.AnyRes
import androidx.annotation.ArrayRes
import androidx.annotation.BoolRes
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DimenRes
import androidx.annotation.Discouraged
import androidx.annotation.DrawableRes
import androidx.annotation.FontRes
import androidx.annotation.FractionRes
import androidx.annotation.IntegerRes
import androidx.annotation.LayoutRes
import androidx.annotation.PluralsRes
import androidx.annotation.RawRes
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import androidx.annotation.StyleableRes
import androidx.annotation.XmlRes
import io.tolgee.common.getQuantityStringT
import io.tolgee.common.getQuantityTextT
import io.tolgee.common.getStringArrayT
import io.tolgee.common.getStringT
import io.tolgee.common.getTextArrayT
import io.tolgee.common.getTextT
import java.io.InputStream

/**
* Ignore Deprecation: Resources constructor is not really deprecated, apps should just not create
Expand All @@ -14,32 +51,238 @@ import io.tolgee.common.getTextT
*/
@Suppress("DEPRECATION")
internal class TolgeeResources(
val baseContext: Context,
val base: Resources,
val tolgee: Tolgee
) : Resources(base.assets, base.displayMetrics, base.configuration) {

override fun getString(id: Int): String {
return baseContext.getStringT(tolgee, id)
/**
* Following methods are intercepted by Tolgee.
*/

override fun getString(@StringRes id: Int): String {
return base.getStringT(tolgee, id)
}

override fun getString(@StringRes id: Int, vararg formatArgs: Any?): String {
return base.getStringT(tolgee, id, *formatArgs.filterNotNull().toTypedArray())
}

override fun getQuantityString(@PluralsRes id: Int, quantity: Int): String {
return base.getQuantityStringT(tolgee, id, quantity)
}

override fun getQuantityString(@PluralsRes id: Int, quantity: Int, vararg formatArgs: Any?): String {
return base.getQuantityStringT(tolgee, id, quantity, *formatArgs.filterNotNull().toTypedArray())
}

override fun getStringArray(@ArrayRes id: Int): Array<out String?> {
return base.getStringArrayT(tolgee, id)
}

override fun getText(@StringRes id: Int): CharSequence {
return base.getTextT(tolgee, id)
}

override fun getQuantityText(@PluralsRes id: Int, quantity: Int): CharSequence {
return base.getQuantityTextT(tolgee, id, quantity)
}

override fun getTextArray(id: Int): Array<out CharSequence?> {
return base.getTextArrayT(tolgee, id)
}

override fun getText(@StringRes id: Int, def: CharSequence?): CharSequence? {
return base.getTextT(tolgee, id, def)
}

/**
* Following methods are proxied to preserve original behavior.
*/

@RequiresApi(Build.VERSION_CODES.O)
override fun getFont(@FontRes id: Int): Typeface {
return base.getFont(id)
}

override fun getIntArray(@ArrayRes id: Int): IntArray {
return base.getIntArray(id)
}

override fun obtainTypedArray(@ArrayRes id: Int): TypedArray {
return base.obtainTypedArray(id)
}

override fun getDimension(@DimenRes id: Int): Float {
return base.getDimension(id)
}

override fun getDimensionPixelOffset(@DimenRes id: Int): Int {
return base.getDimensionPixelOffset(id)
}

override fun getDimensionPixelSize(@DimenRes id: Int): Int {
return base.getDimensionPixelSize(id)
}

override fun getFraction(@FractionRes id: Int, base: Int, pbase: Int): Float {
return this.base.getFraction(id, base, pbase)
}

@Deprecated("Proxied Deprecation")
override fun getDrawable(@DrawableRes id: Int): Drawable? {
return base.getDrawable(id)
}

override fun getDrawable(@DrawableRes id: Int, theme: Theme?): Drawable? {
return base.getDrawable(id, theme)
}

@Deprecated("Proxied Deprecation")
override fun getDrawableForDensity(@DrawableRes id: Int, density: Int): Drawable? {
return base.getDrawableForDensity(id, density)
}

override fun getDrawableForDensity(@DrawableRes id: Int, density: Int, theme: Theme?): Drawable? {
return base.getDrawableForDensity(id, density, theme)
}

@Deprecated("Proxied Deprecation")
override fun getMovie(@RawRes id: Int): Movie? {
return base.getMovie(id)
}

@ColorInt
@Deprecated("Proxied Deprecation")
override fun getColor(@ColorRes id: Int): Int {
return base.getColor(id)
}

@ColorInt
@RequiresApi(Build.VERSION_CODES.M)
override fun getColor(@ColorRes id: Int, theme: Theme?): Int {
return base.getColor(id, theme)
}

@Deprecated("Proxied Deprecation")
override fun getColorStateList(@ColorRes id: Int): ColorStateList {
return base.getColorStateList(id)
}

@RequiresApi(Build.VERSION_CODES.M)
override fun getColorStateList(@ColorRes id: Int, theme: Theme?): ColorStateList {
return base.getColorStateList(id, theme)
}

override fun getBoolean(@BoolRes id: Int): Boolean {
return base.getBoolean(id)
}

override fun getInteger(@IntegerRes id: Int): Int {
return base.getInteger(id)
}

@RequiresApi(Build.VERSION_CODES.Q)
override fun getFloat(@DimenRes id: Int): Float {
return base.getFloat(id)
}

override fun getLayout(@LayoutRes id: Int): XmlResourceParser {
return base.getLayout(id)
}

override fun getAnimation(@AnimatorRes @AnimRes id: Int): XmlResourceParser {
return base.getAnimation(id)
}

override fun getXml(@XmlRes id: Int): XmlResourceParser {
return base.getXml(id)
}

override fun openRawResource(@RawRes id: Int): InputStream {
return base.openRawResource(id)
}

override fun openRawResource(@RawRes id: Int, value: TypedValue?): InputStream {
return base.openRawResource(id, value)
}

override fun openRawResourceFd(@RawRes id: Int): AssetFileDescriptor? {
return base.openRawResourceFd(id)
}

override fun getValue(@AnyRes id: Int, outValue: TypedValue?, resolveRefs: Boolean) {
return base.getValue(id, outValue, resolveRefs)
}

override fun getValueForDensity(
@AnyRes id: Int,
density: Int,
outValue: TypedValue?,
resolveRefs: Boolean
) {
return base.getValueForDensity(id, density, outValue, resolveRefs)
}

@SuppressLint("DiscouragedApi")
@Discouraged("Proxied Discourage")
override fun getValue(name: String?, outValue: TypedValue?, resolveRefs: Boolean) {
return base.getValue(name, outValue, resolveRefs)
}

override fun obtainAttributes(set: AttributeSet?, @StyleableRes attrs: IntArray?): TypedArray? {
return base.obtainAttributes(set, attrs)
}

@Deprecated("Proxied Deprecation")
override fun updateConfiguration(config: Configuration?, metrics: DisplayMetrics?) {
return base.updateConfiguration(config, metrics)
}

override fun getDisplayMetrics(): DisplayMetrics? {
return base.displayMetrics
}

override fun getConfiguration(): Configuration? {
return base.configuration
}

@SuppressLint("DiscouragedApi")
@Discouraged("Proxied Discourage")
override fun getIdentifier(name: String?, defType: String?, defPackage: String?): Int {
return base.getIdentifier(name, defType, defPackage)
}

override fun getResourceName(@AnyRes resid: Int): String? {
return base.getResourceName(resid)
}

override fun getResourcePackageName(@AnyRes resid: Int): String? {
return base.getResourcePackageName(resid)
}

override fun getResourceEntryName(@AnyRes resid: Int): String? {
return base.getResourceEntryName(resid)
}

override fun getString(id: Int, vararg formatArgs: Any?): String {
return baseContext.getStringT(tolgee, id, *formatArgs.filterNotNull().toTypedArray())
override fun parseBundleExtras(parser: XmlResourceParser?, outBundle: Bundle?) {
return base.parseBundleExtras(parser, outBundle)
}

override fun getQuantityString(id: Int, quantity: Int): String {
return baseContext.resources.getQuantityStringT(tolgee, id, quantity)
override fun parseBundleExtra(tagName: String?, attrs: AttributeSet?, outBundle: Bundle?) {
return base.parseBundleExtra(tagName, attrs, outBundle)
}

override fun getQuantityString(id: Int, quantity: Int, vararg formatArgs: Any?): String {
return baseContext.resources.getQuantityStringT(tolgee, id, quantity, *formatArgs.filterNotNull().toTypedArray())
@RequiresApi(Build.VERSION_CODES.R)
override fun addLoaders(vararg loaders: ResourcesLoader?) {
return base.addLoaders(*loaders)
}

override fun getStringArray(id: Int): Array<out String?> {
return baseContext.resources.getStringArrayT(tolgee, id)
@RequiresApi(Build.VERSION_CODES.R)
override fun removeLoaders(vararg loaders: ResourcesLoader?) {
return base.removeLoaders(*loaders)
}

override fun getText(id: Int): CharSequence {
return baseContext.getTextT(tolgee, id)
override fun getResourceTypeName(@AnyRes resid: Int): String? {
return base.getResourceTypeName(resid)
}
}
Loading
Loading